chore: remove ui-playground app (#25266)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
sean-brydon
2025-11-19 14:48:47 +00:00
committed by GitHub
co-authored by Devin AI
parent 5f8db0557c
commit a7352cf587
132 changed files with 0 additions and 7366 deletions
-1
View File
@@ -1,6 +1,5 @@
/**/package.json @calcom/Foundation
/apps/api/**/* @calcom/API
/apps/ui-playground/**/* @calcom/Consumer @calcom/Foundation
/apps/web/**/layout.tsx @calcom/Consumer @calcom/Foundation
/apps/web/lib/csp.ts @calcom/Foundation
/apps/web/lib/buildNonce.ts @calcom/Foundation
-44
View File
@@ -1,44 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# Source maps
*.source/*
-36
View File
@@ -1,36 +0,0 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
-9
View File
@@ -1,9 +0,0 @@
import { HomeLayout } from "fumadocs-ui/layouts/home";
import type { ReactNode } from "react";
import { baseOptions } from "../layout.config";
export default function Layout({ children }: { children: ReactNode }) {
// @ts-expect-error weird type check
return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
}
-12
View File
@@ -1,12 +0,0 @@
export default function Page() {
return (
<div className="font-cal -mt-16 flex min-h-screen w-screen items-center justify-center border pt-16 text-center ">
<div>
<h1 className="text-7xl leading-none md:text-8xl">
Open UI for absolutely
<br /> everyone
</h1>
</div>
</div>
);
}
@@ -1,16 +0,0 @@
import { componentSource } from "@/app/source";
import { createSearchAPI } from "fumadocs-core/search/server";
const indexes = [componentSource].flatMap((src) =>
src.getPages().map((page) => ({
title: page.data.title,
description: page.data.description,
structuredData: page.data.structuredData,
id: page.url,
url: page.url,
}))
);
export const { GET } = createSearchAPI("advanced", {
indexes,
});
@@ -1,21 +0,0 @@
import type { PropsWithChildren } from "react";
type Props = {
name: string;
};
export const Icon: React.FC<PropsWithChildren<Props>> = (props) => {
return (
<div className="text-gray-12 flex flex-col items-center justify-center gap-4">
<div className="size-12 border-gray-5 bg-gray-3 flex aspect-square items-center justify-center rounded-lg border ">
{props.children}
</div>
<span className="font-mono text-sm">
{"<"}
{props.name}
{"/>"}
</span>
</div>
);
};
Icon.displayName = "Icon";
@@ -1,57 +0,0 @@
"use client";
import { type PropsWithChildren, useState } from "react";
import className from "@calcom/ui/classNames";
import { Button } from "@calcom/ui/components/button";
type Props = {
customCodeSnippet?: string;
className?: string;
};
export const RenderComponentWithSnippet: React.FC<PropsWithChildren<Props>> = (props) => {
const [open, setOpen] = useState(false);
// const snippet =
// props.customCodeSnippet ??
// reactElementToJSXString(props.children, {
// showFunctions: true,
// useBooleanShorthandSyntax: true,
// displayName: (node) => {
// // @ts-ignore
// return node?.type.displayName ?? node?.type ?? "Unknown";
// },
// });
return (
<div className="border-gray-6 bg-default rounded-lg border">
<div className={className("not-prose p-8 xl:p-12", props.className)}>{props.children}</div>
<div className="bg-gray-3 border-gray-6 flex items-center justify-start border-b border-t p-2">
<Button
color="minimal"
onClick={() => {
setOpen(!open);
}}>
Code (WIP)
</Button>
</div>
<div
className={className(
"bg-gray-2 max-h-96 w-full overflow-y-scroll transition-all",
open ? "block" : "hidden"
)}>
<div className="flex items-start">
{/* <pre className="py-0 text-gray-8 text-right">
{snippet
.split("\n")
.map((_line, i) => i + 1)
.join("\n")}
</pre>
<pre className="py-0 text-gray-12 w-full">{snippet}</pre> */}
</div>
</div>
</div>
);
};
@@ -1,7 +0,0 @@
import type { PropsWithChildren } from "react";
export const Row: React.FC<PropsWithChildren> = (props) => {
return <div className="flex w-full items-center justify-between gap-8">{props.children}</div>;
};
Row.displayName = "Row";
@@ -1,56 +0,0 @@
import { componentSource } from "@/app/source";
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { createTypeTable } from "fumadocs-typescript/ui";
import defaultMdxComponents from "fumadocs-ui/mdx";
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { Toaster } from "react-hot-toast";
export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
const page = componentSource.getPage(params.slug);
if (!page) {
notFound();
}
const { AutoTypeTable } = createTypeTable();
const MDX = page.data.body;
return (
<>
{/* @ts-expect-error idk why fumadocs doersnt like this */}
<DocsPage toc={page.data.toc} full={page.data.full}>
<Toaster />
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<TooltipProvider>
{/* @ts-expect-error idk why fumadocs doersnt like this */}
<MDX components={{ ...defaultMdxComponents, AutoTypeTable }} />
</TooltipProvider>
</DocsBody>
</DocsPage>
</>
);
}
export async function generateStaticParams() {
return componentSource.generateParams();
}
export async function generateMetadata({ params }: { params: Promise<{ slug?: string[] }> }) {
const page = componentSource.getPage((await params).slug);
if (!page) {
notFound();
}
return {
title: page.data.title,
description: page.data.description,
} satisfies Metadata;
}
-17
View File
@@ -1,17 +0,0 @@
import { componentSource } from "@/app/source";
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { DocsLayout } from "fumadocs-ui/layouts/notebook";
import type { ReactNode } from "react";
import { IconSprites } from "@calcom/ui/components/icon";
import { baseOptions } from "../layout.config";
export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout tree={componentSource.pageTree} {...baseOptions}>
<TooltipProvider>{children}</TooltipProvider>
<IconSprites />
</DocsLayout>
);
}
File diff suppressed because it is too large Load Diff
-26
View File
@@ -1,26 +0,0 @@
import type { HomeLayoutProps } from "fumadocs-ui/layouts/home";
/**
* Shared layout configurations
*
* you can configure layouts individually from:
* Home Layout: app/(home)/layout.tsx
* Docs Layout: app/docs/layout.tsx
*/
export const baseOptions: HomeLayoutProps = {
nav: {
title: "Cal.com",
},
githubUrl: "https://github.com/calcom/cal.com",
links: [
{
text: "Design",
url: "/design",
active: "nested-url",
},
{
text: "GitHub",
url: "https://github.com/calcom/cal.com",
},
],
};
-36
View File
@@ -1,36 +0,0 @@
import { RootProvider } from "fumadocs-ui/provider";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./global.css";
const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<head>
<style>{`
:root {
--font-inter: ${inter.style.fontFamily.replace(/\'/g, "")};
}
`}</style>
</head>
<body className={`${inter.variable} antialiased`}>
<RootProvider>{children}</RootProvider>
</body>
</html>
);
}
-13
View File
@@ -1,13 +0,0 @@
import { components, docs, meta } from "@/.source";
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";
export const source = loader({
baseUrl: "/docs",
source: createMDXSource(docs, meta),
});
export const componentSource = loader({
baseUrl: "/design",
source: createMDXSource(components, []),
});
@@ -1,19 +0,0 @@
import { Toaster } from "react-hot-toast";
import { Status } from "./Status";
export function MDXLayout({ children, frontmatter }: { children: React.ReactNode; frontmatter: any }) {
return (
<div className="prose max-w-none">
<Toaster />
{frontmatter.airtableId && (
<Status
airtableId={frontmatter.airtableId}
designStatus={frontmatter.designStatus}
devStatus={frontmatter.devStatus}
/>
)}
{children}
</div>
);
}
@@ -1,65 +0,0 @@
"use client";
import React from "react";
import { Toaster } from "react-hot-toast";
import { showToast } from "@calcom/ui/components/toast";
interface BorderRadiusToken {
name: string;
value: string;
className: string;
}
interface BorderRadiusTableProps {
tokens: BorderRadiusToken[];
}
const tokens: BorderRadiusToken[] = [
{ name: "None", value: "0px", className: "rounded-none" },
{ name: "Small", value: "0.125rem", className: "rounded-sm" },
{ name: "Default", value: "0.25rem", className: "rounded" },
{ name: "Medium", value: "0.375rem", className: "rounded-md" },
{ name: "Large", value: "0.5rem", className: "rounded-lg" },
{ name: "XLarge", value: "0.75rem", className: "rounded-xl" },
{ name: "2XLarge", value: "1rem", className: "rounded-2xl" },
{ name: "3XLarge", value: "1.5rem", className: "rounded-3xl" },
{ name: "Full", value: "9999px", className: "rounded-full" },
];
export const BorderRadiusTable: React.FC<BorderRadiusTableProps> = () => {
const handleCopy = async (value: string) => {
try {
await navigator.clipboard.writeText(value);
showToast(`Copied ${value}`, "success");
} catch (error) {
console.error("Failed to copy:", error);
showToast("Failed to copy", "error");
}
};
return (
<>
<Toaster position="bottom-right" />
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{tokens.map((token) => (
<div
key={token.name}
onClick={() => handleCopy(token.className)}
className="border-subtle bg-default hover:bg-subtle group relative cursor-pointer overflow-hidden rounded-lg border p-4">
<div className="space-y-3">
<div className="flex items-center justify-between">
<div>
<p className="text-emphasis text-sm font-medium">{token.name}</p>
<p className="text-subtle mt-1 text-xs">{token.className}</p>
</div>
<p className="text-subtle text-sm">{token.value}</p>
</div>
<div className={`bg-emphasis h-16 w-full ${token.className}`} />
</div>
</div>
))}
</div>
</>
);
};
@@ -1,372 +0,0 @@
"use client";
import React, { useState, useMemo } from "react";
import { Toaster } from "react-hot-toast";
import { Icon } from "@calcom/ui/components/icon";
import { showToast } from "@calcom/ui/components/toast";
type IconName =
| "activity"
| "arrow-down"
| "arrow-left"
| "arrow-right"
| "arrow-up-right"
| "arrow-up"
| "asterisk"
| "at-sign"
| "atom"
| "badge-check"
| "ban"
| "bell"
| "binary"
| "blocks"
| "bold"
| "book"
| "book-open-check"
| "book-open"
| "book-user"
| "building"
| "calendar-check-2"
| "calendar-days"
| "calendar-heart"
| "calendar-range"
| "calendar-search"
| "calendar-x-2"
| "calendar"
| "chart-bar"
| "chart-line"
| "check-check"
| "check"
| "chevron-down"
| "chevron-left"
| "chevron-right"
| "chevron-up"
| "chevrons-down-up"
| "chevrons-left"
| "chevrons-right"
| "chevrons-up-down"
| "circle-alert"
| "circle-arrow-up"
| "circle-check-big"
| "circle-check"
| "circle-help"
| "circle-x"
| "circle"
| "clipboard-check"
| "clipboard"
| "clock"
| "code"
| "columns-3"
| "command"
| "contact"
| "copy"
| "corner-down-left"
| "corner-down-right"
| "credit-card"
| "disc"
| "dot"
| "download"
| "ellipsis-vertical"
| "ellipsis"
| "external-link"
| "eye-off"
| "eye"
| "file-down"
| "file-text"
| "file"
| "filter"
| "fingerprint"
| "flag"
| "folder"
| "gift"
| "git-merge"
| "github"
| "globe"
| "grid-3x3"
| "handshake"
| "info"
| "italic"
| "key"
| "layers"
| "layout-dashboard"
| "link-2"
| "link"
| "loader"
| "lock-open"
| "lock"
| "log-out"
| "mail-open"
| "mail"
| "map-pin"
| "map"
| "menu"
| "message-circle"
| "messages-square"
| "moon"
| "paintbrush"
| "paperclip"
| "pencil"
| "phone-call"
| "phone"
| "plus"
| "refresh-ccw"
| "refresh-cw"
| "repeat"
| "rocket"
| "rotate-ccw"
| "rotate-cw"
| "search"
| "send"
| "settings"
| "share-2"
| "shield-check"
| "shield"
| "shuffle"
| "sliders-horizontal"
| "sliders-vertical"
| "smartphone"
| "sparkles"
| "square-check"
| "star"
| "sun"
| "sunrise"
| "sunset"
| "tags"
| "terminal"
| "trash-2"
| "trash"
| "trello"
| "triangle-alert"
| "upload"
| "user-check"
| "user-plus"
| "user-x"
| "user"
| "users"
| "venetian-mask"
| "video"
| "waypoints"
| "webhook"
| "x"
| "zap";
interface IconGridProps {
className?: string;
}
interface CopyMenuProps {
name: IconName;
onCopy: (format: string) => void;
}
const CopyMenu: React.FC<CopyMenuProps> = ({ name, onCopy }) => {
return (
<div className="absolute inset-2 flex items-center justify-center bg-black/10 opacity-0 transition-opacity group-hover:opacity-100">
<div className="bg-default space-y-1 rounded-md p-2 shadow-lg">
<button
onClick={() => onCopy(`<Icon name="${name}" className="h-4 w-4" />`)}
className="hover:bg-subtle w-full rounded px-3 py-1 text-left text-sm">
Copy Component
</button>
</div>
</div>
);
};
export const IconGrid: React.FC<IconGridProps> = ({ className }) => {
const [searchQuery, setSearchQuery] = useState("");
const handleCopy = async (value: string) => {
try {
await navigator.clipboard.writeText(value);
showToast(`Copied ${value}`, "success");
} catch (error) {
console.error("Failed to copy:", error);
showToast("Failed to copy", "error");
}
};
const icons: IconName[] = [
"activity",
"arrow-down",
"arrow-left",
"arrow-right",
"arrow-up-right",
"arrow-up",
"asterisk",
"at-sign",
"atom",
"badge-check",
"ban",
"bell",
"binary",
"blocks",
"bold",
"book-open-check",
"book-open",
"book-user",
"building",
"calendar-check-2",
"calendar-days",
"calendar-heart",
"calendar-range",
"calendar-search",
"calendar-x-2",
"calendar",
"chart-bar",
"chart-line",
"check-check",
"check",
"chevron-down",
"chevron-left",
"chevron-right",
"chevron-up",
"chevrons-down-up",
"chevrons-left",
"chevrons-right",
"chevrons-up-down",
"circle-alert",
"circle-arrow-up",
"circle-check-big",
"circle-check",
"circle-help",
"circle-x",
"circle",
"clipboard-check",
"clipboard",
"clock",
"code",
"columns-3",
"command",
"contact",
"copy",
"corner-down-left",
"corner-down-right",
"credit-card",
"disc",
"dot",
"download",
"ellipsis-vertical",
"ellipsis",
"external-link",
"eye-off",
"eye",
"file-down",
"file-text",
"file",
"filter",
"fingerprint",
"flag",
"folder",
"gift",
"git-merge",
"github",
"globe",
"grid-3x3",
"handshake",
"info",
"italic",
"key",
"layers",
"layout-dashboard",
"link-2",
"link",
"loader",
"lock-open",
"lock",
"log-out",
"mail-open",
"mail",
"map-pin",
"map",
"menu",
"message-circle",
"messages-square",
"moon",
"paintbrush",
"paperclip",
"pencil",
"phone-call",
"phone",
"plus",
"refresh-ccw",
"refresh-cw",
"repeat",
"rocket",
"rotate-ccw",
"rotate-cw",
"search",
"send",
"settings",
"share-2",
"shield-check",
"shield",
"shuffle",
"sliders-horizontal",
"sliders-vertical",
"smartphone",
"sparkles",
"square-check",
"star",
"sun",
"sunrise",
"sunset",
"tags",
"terminal",
"trash-2",
"trash",
"trello",
"triangle-alert",
"upload",
"user-check",
"user-plus",
"user-x",
"user",
"users",
"venetian-mask",
"video",
"waypoints",
"webhook",
"x",
"zap",
];
const filteredIcons = useMemo(() => {
if (!searchQuery) return icons;
const query = searchQuery.toLowerCase();
return icons.filter((icon) => icon.toLowerCase().includes(query));
}, [searchQuery]);
return (
<>
<Toaster position="bottom-right" />
<div className={className}>
<div className="mb-8">
<input
type="text"
placeholder="Search icons..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="border-subtle bg-default text-emphasis focus:ring-emphasis placeholder:text-subtle w-full rounded-md border px-4 py-2 focus:outline-none focus:ring-2"
/>
</div>
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6">
{filteredIcons.map((iconName) => (
<div
key={iconName}
className="border-subtle bg-default group relative overflow-hidden rounded-lg border p-4">
<div className="flex flex-col items-center space-y-2">
<div className="bg-subtle flex h-12 w-12 items-center justify-center rounded-md">
<Icon name={iconName} className="text-emphasis h-6 w-6" />
</div>
<p className="text-emphasis text-center text-sm">{iconName}</p>
</div>
<CopyMenu name={iconName} onCopy={handleCopy} />
</div>
))}
</div>
</div>
</>
);
};
@@ -1,126 +0,0 @@
"use client";
import React from "react";
import { Toaster } from "react-hot-toast";
import { showToast } from "@calcom/ui/components/toast";
interface ShadowToken {
name: string;
className: string;
description: string;
}
interface ShadowTableProps {
tokens: ShadowToken[];
}
const tokens: ShadowToken[] = [
{
name: "Dropdown",
className: "shadow-dropdown",
description: "Used for dropdown menus and popovers",
},
{
name: "Solid Gray Rested",
className: "shadow-solid-gray-rested",
description: "Default state for solid gray buttons",
},
{
name: "Solid Gray Hover",
className: "shadow-solid-gray-hover",
description: "Hover state for solid gray buttons",
},
{
name: "Solid Gray Active",
className: "shadow-solid-gray-active",
description: "Active/pressed state for solid gray buttons",
},
{
name: "Outline Gray Rested",
className: "shadow-outline-gray-rested",
description: "Default state for outline gray buttons",
},
{
name: "Outline Gray Hover",
className: "shadow-outline-gray-hover",
description: "Hover state for outline gray buttons",
},
{
name: "Outline Gray Active",
className: "shadow-outline-gray-active",
description: "Active/pressed state for outline gray buttons",
},
{
name: "Outline Red Rested",
className: "shadow-outline-red-rested",
description: "Default state for outline red buttons",
},
{
name: "Outline Red Hover",
className: "shadow-outline-red-hover",
description: "Hover state for outline red buttons",
},
{
name: "Outline Red Active",
className: "shadow-outline-red-active",
description: "Active/pressed state for outline red buttons",
},
{
name: "Elevation Low",
className: "shadow-elevation-low",
description: "Subtle elevation for UI elements",
},
{
name: "Button Solid Brand Default",
className: "shadow-button-solid-brand-default",
description: "Default state for solid brand buttons",
},
{
name: "Button Solid Brand Hover",
className: "shadow-button-solid-brand-hover",
description: "Hover state for solid brand buttons",
},
{
name: "Button Solid Brand Active",
className: "shadow-button-solid-brand-active",
description: "Active/pressed state for solid brand buttons",
},
];
export const ShadowTable: React.FC<ShadowTableProps> = () => {
const handleCopy = async (value: string) => {
try {
await navigator.clipboard.writeText(value);
showToast(`Copied ${value}`, "success");
} catch (error) {
console.error("Failed to copy:", error);
showToast("Failed to copy", "error");
}
};
return (
<>
<Toaster position="bottom-right" />
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
{tokens.map((token) => (
<div
key={token.name}
onClick={() => handleCopy(token.className)}
className="group relative cursor-pointer">
<div
className={`border-subtle bg-default hover:bg-subtle relative overflow-hidden rounded-lg border p-4 transition-colors ${token.className}`}>
<div className="space-y-2">
<div>
<p className="text-emphasis text-sm font-medium">{token.name}</p>
<p className="text-subtle mt-1 text-xs">{token.className}</p>
</div>
<p className="text-subtle text-sm">{token.description}</p>
</div>
</div>
</div>
))}
</div>
</>
);
};
@@ -1,45 +0,0 @@
"use client";
import React from "react";
import { Toaster } from "react-hot-toast";
interface SpacingToken {
name: string;
value: string;
pixels: string;
}
interface SpacingTableProps {
tokens: SpacingToken[];
}
export const SpacingTable: React.FC<SpacingTableProps> = ({ tokens }) => {
return (
<>
<Toaster position="bottom-right" />
<div className="space-y-6">
<div className="grid grid-cols-3 gap-4">
{tokens.map((token, index) => (
<div
key={index}
className="border-subtle bg-default group relative overflow-hidden rounded-lg border">
<div className="flex items-center gap-4 p-4">
<div className="w-[100px] space-y-1">
<p className="text-emphasis text-sm font-medium">
{token.name === "px" ? "px" : `${token.name} (${token.pixels})`}
</p>
</div>
<div className="bg-subtle relative h-8 rounded">
<div
className="absolute left-0 top-1/2 h-2 -translate-y-1/2 bg-red-500"
style={{ width: token.pixels }}
/>
</div>
</div>
</div>
))}
</div>
</div>
</>
);
};
@@ -1,103 +0,0 @@
"use client";
import { getColorFormats, copyToClipboard } from "@/lib/colorUtils";
import React from "react";
import { Toaster } from "react-hot-toast";
import { showToast } from "@calcom/ui/components/toast";
interface ColorSwatch {
name: string;
value: string;
className: string;
}
interface ColorSection {
title: string;
description?: string;
colors: ColorSwatch[];
}
interface TypeTableProps {
sections: ColorSection[];
}
interface CopyMenuProps {
color: string;
className: string;
onCopy: (format: string) => void;
}
const CopyMenu: React.FC<CopyMenuProps> = ({ color, className, onCopy }) => {
const formats = getColorFormats(color, className);
if (!formats) return null;
return (
<div className="absolute inset-2 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100">
<div className="bg-default space-y-1 rounded-md p-2 shadow-lg">
<button
onClick={() => onCopy(formats.hex)}
className="hover:bg-subtle w-full rounded px-3 py-1 text-left text-sm">
Copy as HEX
</button>
<button
onClick={() => onCopy(formats.rgb)}
className="hover:bg-subtle w-full rounded px-3 py-1 text-left text-sm">
Copy as RGB
</button>
<button
onClick={() => onCopy(formats.hsl)}
className="hover:bg-subtle w-full rounded px-3 py-1 text-left text-sm">
Copy as HSL
</button>
<button
onClick={() => onCopy(formats.tailwind)}
className="hover:bg-subtle w-full rounded px-3 py-1 text-left text-sm">
Copy Tailwind Class
</button>
</div>
</div>
);
};
export const TypeTable: React.FC<TypeTableProps> = ({ sections }) => {
const handleCopy = async (colorValue: string) => {
try {
await copyToClipboard(colorValue);
showToast(`Copied ${colorValue}`, "success");
} catch (error) {
console.error("Failed to copy color:", error);
showToast("Failed to copy color", "error");
}
};
return (
<>
<Toaster position="bottom-right" />
<div className="space-y-8">
{sections.map((section, sectionIndex) => (
<div key={sectionIndex} className="space-y-4">
<div>
<h3 className="text-emphasis text-lg font-semibold">{section.title}</h3>
{section.description && <p className="text-subtle text-sm">{section.description}</p>}
</div>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{section.colors.map((color, colorIndex) => (
<div key={colorIndex} className="border-subtle bg-default overflow-hidden rounded-lg border">
<div className="group relative">
<div className={`h-36 w-full ${color.className}`} title={color.value} />
<CopyMenu color={color.value} className={color.className} onCopy={handleCopy} />
</div>
<div className="space-y-1 p-2">
<p className="text-emphasis text-sm font-medium">{color.name}</p>
<p className="text-subtle text-xs">{color.value}</p>
</div>
</div>
))}
</div>
</div>
))}
</div>
</>
);
};
@@ -1,109 +0,0 @@
"use client";
import React from "react";
import { Toaster } from "react-hot-toast";
import { showToast } from "@calcom/ui/components/toast";
interface TypographyStyle {
name: string;
className: string;
specs: {
fontFamily: string;
fontSize: string;
lineHeight: string;
weight: string;
};
category?: string;
}
interface TypographySection {
title: string;
description?: string;
styles: TypographyStyle[];
}
interface TypographyTableProps {
sections: TypographySection[];
}
interface CopyMenuProps {
style: TypographyStyle;
onCopy: (format: string) => void;
}
const CopyMenu: React.FC<CopyMenuProps> = ({ style, onCopy }) => {
return (
<div className="absolute inset-2 flex items-center justify-center bg-black/10 opacity-0 transition-opacity group-hover:opacity-100">
<div className="bg-default space-y-1 rounded-md p-2 shadow-lg">
<button
onClick={() => onCopy(style.className)}
className="hover:bg-subtle w-full rounded px-3 py-1 text-left text-sm">
Copy Tailwind Class
</button>
<button
onClick={() =>
onCopy(`font-family: ${style.specs.fontFamily};
font-size: ${style.specs.fontSize}px;
line-height: ${style.specs.lineHeight};
font-weight: ${style.specs.weight};`)
}
className="hover:bg-subtle w-full rounded px-3 py-1 text-left text-sm">
Copy CSS
</button>
</div>
</div>
);
};
export const TypographyTable: React.FC<TypographyTableProps> = ({ sections }) => {
const handleCopy = async (value: string) => {
try {
await navigator.clipboard.writeText(value);
showToast(`Copied to clipboard`, "success");
} catch (error) {
console.error("Failed to copy:", error);
showToast("Failed to copy", "error");
}
};
return (
<>
<Toaster position="bottom-right" />
<div className="space-y-12">
{sections.map((section, sectionIndex) => (
<div key={sectionIndex} className="space-y-6">
<div>
<h3 className="text-emphasis text-2xl font-semibold">{section.title}</h3>
{section.description && <p className="text-subtle mt-2 text-base">{section.description}</p>}
</div>
<div className="grid gap-6">
{section.styles.map((style, styleIndex) => (
<div
key={styleIndex}
className="border-subtle bg-default group relative overflow-hidden rounded-lg border p-6">
<div className="flex items-start justify-between">
<div className="space-y-4">
<div>
<p className="text-emphasis text-sm font-medium">{style.name}</p>
<p className="text-subtle mt-1 text-xs">{style.className}</p>
</div>
<div className={style.className}>The quick brown fox jumps over the lazy dog</div>
</div>
<div className="text-subtle space-y-2 text-sm">
<p>Font: {style.specs.fontFamily}</p>
<p>Size: {style.specs.fontSize}px</p>
<p>Line Height: {style.specs.lineHeight}</p>
<p>Weight: {style.specs.weight}</p>
</div>
</div>
<CopyMenu style={style} onCopy={handleCopy} />
</div>
))}
</div>
</div>
))}
</div>
</>
);
};
@@ -1,32 +0,0 @@
---
title: Border Radius
description: Cal.com's border radius system
---
import { BorderRadiusTable } from '@/components/ui/BorderRadiusTable';
# Border Radius
Our design system uses a consistent set of border radius values to create a cohesive and modern interface. The border radius scale ranges from none (sharp corners) to full (completely rounded).
## Usage
```tsx
// No border radius
<div className="rounded-none">Sharp corners</div>
// Default radius
<div className="rounded">Default rounded corners</div>
// Larger radius
<div className="rounded-lg">Larger rounded corners</div>
// Fully rounded (useful for pills, badges)
<div className="rounded-full">Completely rounded</div>
```
## Border Radius Scale
Click on any radius to copy its Tailwind class name. Each preview shows how the radius appears when applied.
<BorderRadiusTable />
@@ -1,144 +0,0 @@
---
title: Colors
description: Cal.com Color System Documentation
---
import { TypeTable } from '@/components/ui/TypeTable';
export const backgroundTokens = {
title: 'Background Tokens',
colors: [
{ name: 'emphasis', value: 'var(--cal-bg-emphasis)', className: 'bg-emphasis' },
{ name: 'default', value: 'var(--cal-bg, white)', className: 'bg-default' },
{ name: 'subtle', value: 'var(--cal-bg-subtle)', className: 'bg-subtle' },
{ name: 'muted', value: 'var(--cal-bg-muted)', className: 'bg-muted' },
{ name: 'inverted', value: 'var(--cal-bg-inverted)', className: 'bg-inverted' },
],
};
export const primaryBackgroundTokens = {
title: 'Primary Background Tokens',
colors: [
{ name: 'primary.default', value: 'var(--cal-bg-primary)', className: 'bg-primary-default' },
{ name: 'primary.emphasis', value: 'var(--cal-bg-primary-emphasis)', className: 'bg-primary-emphasis' },
{ name: 'primary.muted', value: 'var(--cal-bg-primary-muted)', className: 'bg-primary-muted' },
],
};
export const brandTokens = {
title: 'Brand Colors',
colors: [
{ name: 'brand.default', value: 'var(--cal-brand,#111827)', className: 'bg-brand-default' },
{ name: 'brand.emphasis', value: 'var(--cal-brand-emphasis,#101010)', className: 'bg-brand-emphasis' },
{ name: 'brand.subtle', value: 'var(--cal-brand-subtle,#9CA3AF)', className: 'bg-brand-subtle' },
{ name: 'brand.accent', value: 'var(--cal-brand-accent,white)', className: 'bg-brand-accent' },
],
};
export const semanticBackgroundTokens = {
title: 'Semantic Background Tokens',
colors: [
{ name: 'semantic.info.subtle', value: 'var(--cal-bg-semantic-info-subtle)', className: 'bg-semantic-info-subtle' },
{ name: 'semantic.info.emphasis', value: 'var(--cal-bg-semantic-info-emphasis)', className: 'bg-semantic-info-emphasis' },
{ name: 'semantic.success.subtle', value: 'var(--cal-bg-semantic-success-subtle)', className: 'bg-semantic-success-subtle' },
{ name: 'semantic.success.emphasis', value: 'var(--cal-bg-semantic-success-emphasis)', className: 'bg-semantic-success-emphasis' },
{ name: 'semantic.attention.subtle', value: 'var(--cal-bg-semantic-attention-subtle)', className: 'bg-semantic-attention-subtle' },
{ name: 'semantic.attention.emphasis', value: 'var(--cal-bg-semantic-attention-emphasis)', className: 'bg-semantic-attention-emphasis' },
{ name: 'semantic.error.subtle', value: 'var(--cal-bg-semantic-error-subtle)', className: 'bg-semantic-error-subtle' },
{ name: 'semantic.error.emphasis', value: 'var(--cal-bg-semantic-error-emphasis)', className: 'bg-semantic-error-emphasis' },
],
};
export const borderTokens = {
title: 'Border Tokens',
colors: [
{ name: 'border.emphasis', value: 'var(--cal-border-emphasis)', className: 'border-emphasis' },
{ name: 'border.default', value: 'var(--cal-border)', className: 'border-default' },
{ name: 'border.subtle', value: 'var(--cal-border-subtle)', className: 'border-subtle' },
{ name: 'border.muted', value: 'var(--cal-border-muted)', className: 'border-muted' },
{ name: 'border.semantic.error', value: 'var(--cal-border-semantic-error)', className: 'border-semantic-error' },
],
};
export const textTokens = {
title: 'Text Tokens',
description: 'Semantic text colors for various purposes',
colors: [
{ name: 'text.emphasis', value: 'var(--cal-text-emphasis)', className: 'text-emphasis' },
{ name: 'text.default', value: 'var(--cal-text)', className: 'text-default' },
{ name: 'text.subtle', value: 'var(--cal-text-subtle)', className: 'text-subtle' },
{ name: 'text.muted', value: 'var(--cal-text-muted)', className: 'text-muted' },
{ name: 'text.inverted', value: 'var(--cal-text-inverted)', className: 'text-inverted' },
],
};
# Color System
Our color system is built on semantic tokens that provide consistent theming across light and dark modes. The system is organized into five main categories:
1. Background Colors
2. Primary Colors
3. Brand Colors
4. Semantic Colors
5. Text & Border Colors
## Usage Guidelines
1. **Use Semantic Tokens**: Always prefer semantic tokens over direct color values to maintain consistency and theme support.
2. **Dark Mode Support**: Our semantic tokens automatically handle dark mode transitions.
3. **Component Design**: When creating new components, follow the semantic token system:
- Use `bg-*` tokens for backgrounds
- Use `text-*` tokens for text content
- Use `border-*` tokens for borders
4. **Semantic States**: Use semantic color variations to convey meaning:
- Info: For informational UI elements
- Success: For successful actions and positive states
- Attention: For warnings and important notices
- Error: For error states and destructive actions
5. **Brand Colors**: Use brand colors sparingly and primarily for:
- Primary CTAs
- Brand identity elements
- Key interactive components
## Using Semantic Tokens
Our color tokens are CSS variables that automatically handle light and dark mode transitions. Here's how to use them:
### Background Colors
```tsx
// Basic backgrounds
<div className="bg-default">Default background</div>
<div className="bg-subtle">Subtle background</div>
<div className="bg-emphasis">Emphasis background</div>
// Primary backgrounds
<div className="bg-primary-default">Primary background</div>
<div className="bg-primary-emphasis">Primary emphasis</div>
```
### Semantic Colors
```tsx
// Info states
<div className="bg-semantic-info-subtle">Info subtle</div>
<div className="bg-semantic-info-emphasis">Info emphasis</div>
// Success states
<div className="bg-semantic-success-subtle">Success subtle</div>
<div className="bg-semantic-success-emphasis">Success emphasis</div>
// Error states
<div className="bg-semantic-error-subtle">Error subtle</div>
<div className="bg-semantic-error-emphasis">Error emphasis</div>
```
### Text Colors
```tsx
<p className="text-emphasis">High emphasis text</p>
<p className="text-default">Default text</p>
<p className="text-subtle">Subtle text</p>
<p className="text-muted">Muted text</p>
```
<TypeTable sections={[backgroundTokens, primaryBackgroundTokens, brandTokens, semanticBackgroundTokens, borderTokens, textTokens]} />
@@ -1,106 +0,0 @@
---
title: MultiOptionInput
---
import { MultiOptionInput } from "@calcom/ui/components/form"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { StatesExample } from "./multiInputField.states"
import { PasteExample } from "./multiInputField.paste"
import { CustomizationExample } from "./multiInputField.customization"
## Overview
The MultiOptionInput component is a flexible form input that allows users to manage a list of options with features like:
- Adding and removing options
- Reordering options with move up/down buttons
- Pasting multiple options with configurable delimiters
- Minimum options requirement
- Customizable placeholders and labels
- Key-value pair mode for environment variables, configuration settings, etc.
## States
MultiOptionInput in different states: default, disabled, and with minimum options.
<StatesExample/>
## Paste Support
Demonstrates the paste functionality with different delimiters. Users can paste comma-separated values, newline-separated values, or use custom delimiters.
In key-value mode, the component can also parse key-value pairs from pasted text. For example:
- `KEY=value`
- `NODE_ENV:production`
<PasteExample/>
## Customization
Examples of different customization options like placeholders, move buttons, remove buttons, and key-value pairs.
<CustomizationExample/>
## Usage
### Standard Options
```tsx
import { MultiOptionInput } from "@calcom/ui/components/form/inputs/MultiOptionInput";
import { useForm, FormProvider } from "react-hook-form";
type FormValues = {
options: Array<{ label: string; id: string }>;
};
const MyForm = () => {
const methods = useForm<FormValues>();
return (
<FormProvider {...methods}>
<form>
<MultiOptionInput<FormValues>
fieldArrayName="options"
optionPlaceholders={["Option 1", "Option 2"]}
defaultNumberOfOptions={2}
pasteDelimiters={["\n", ","]} // Optional
showMoveButtons={true} // Optional
minOptions={1} // Optional
addOptionLabel="Add another option" // Optional
/>
</form>
</FormProvider>
);
};
```
### Key-Value Pairs
```tsx
import { MultiOptionInput } from "@calcom/ui/components/form/inputs/MultiOptionInput";
import { useForm, FormProvider } from "react-hook-form";
type FormValues = {
envVars: Array<{ label: string; value: string; id: string }>;
};
const EnvVarsForm = () => {
const methods = useForm<FormValues>();
return (
<FormProvider {...methods}>
<form>
<MultiOptionInput<FormValues>
fieldArrayName="envVars"
keyValueMode={true}
keyLabel="Environment Variable"
valueLabel="Value"
optionPlaceholders={["NODE_ENV", "PORT"]}
valuePlaceholders={["production", "3000"]}
keyValueDelimiters={[":", "="]} // Optional, defaults to [":", "="]
defaultNumberOfOptions={2}
/>
</form>
</FormProvider>
);
};
```
@@ -1,28 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Alert } from "@calcom/ui/components/alert";
const severities = ["neutral", "info", "warning", "error"] as const;
export const ActionsExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="no-prose space-y-4">
{severities.map((severity) => (
<Alert
key={severity}
severity={severity}
title={`${severity.charAt(0).toUpperCase() + severity.slice(1)} Alert with Actions`}
message={`This is a ${severity} alert with action buttons.`}
actions={
<div className="flex space-x-2">
<button className="rounded-md px-3 py-2 text-sm font-medium hover:opacity-90">Dismiss</button>
<button className="rounded-md px-3 py-2 text-sm font-medium hover:opacity-90">View</button>
</div>
}
/>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,22 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Alert } from "@calcom/ui/components/alert";
const severities = ["neutral", "info", "warning", "error"] as const;
export const BasicExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="not-prose space-y-4">
{severities.map((severity) => (
<Alert
key={severity}
severity={severity}
title={`${severity.charAt(0).toUpperCase() + severity.slice(1)} Alert`}
message={`This is a ${severity} alert message to show important information to users.`}
/>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,24 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Alert } from "@calcom/ui/components/alert";
const severities = ["neutral", "info", "warning", "error"] as const;
export const CustomIconColorExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="not-prose space-y-4">
{severities.map((severity) => (
<Alert
key={severity}
severity={severity}
CustomIcon="bell"
customIconColor="text-emphasis"
title={`${severity.charAt(0).toUpperCase() + severity.slice(1)} Alert with Custom Icon Color`}
message="This alert uses a custom icon color."
/>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,25 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Alert } from "@calcom/ui/components/alert";
const severities = ["neutral", "info", "warning", "error"] as const;
const customIcons = ["info", "alert-triangle", "circle-x", "circle-check", "bell"] as const;
export const CustomIconsExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="not-prose space-y-4">
{severities.map((severity, index) => (
<Alert
key={severity}
severity={severity}
// @ts-expect-error Didnt type this as IconName for CustomIcon
CustomIcon={customIcons[index % customIcons.length]}
title={`${severity.charAt(0).toUpperCase() + severity.slice(1)} Alert with Custom Icon`}
message="This alert uses a custom icon instead of the default one."
/>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,42 +0,0 @@
---
title: Alert
airtableId: recnvwaXqOucqLo9Y
---
import { Alert } from "@calcom/ui/components/alert"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { BasicExample } from "./alert.basic"
import { ActionsExample } from "./alert.actions"
import { CustomIconsExample } from "./alert.customIcons"
import { CustomIconColorExample } from "./alert.customIconColor"
import { NoTitleExample } from "./alert.noTitle"
## Basic Alerts
Alerts come in different severities to convey different levels of importance.
<BasicExample/>
## Alerts with Actions
Alerts can include action buttons to provide users with immediate options.
<ActionsExample/>
## Custom Icons
You can customize the alert icon to better match your message context.
<CustomIconsExample/>
## Custom Icon Colors
The icon color can be customized independently of the alert severity.
<CustomIconColorExample/>
## Without Title
For simpler messages, alerts can be displayed without a title.
<NoTitleExample/>
@@ -1,21 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Alert } from "@calcom/ui/components/alert";
const severities = ["neutral", "info", "warning", "error"] as const;
export const NoTitleExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="not-prose space-y-4">
{severities.map((severity) => (
<Alert
key={severity}
severity={severity}
message={`This is a ${severity} alert without a title to show a more compact version.`}
/>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,22 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Avatar } from "@calcom/ui/components/avatar";
const sampleImage = "https://cal.com/stakeholder/peer.jpg";
export const ImageExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex items-center gap-8">
<div className="flex flex-col items-center gap-2">
<Avatar size="md" alt="With image" imageSrc={sampleImage} />
<span className="text-subtle text-xs">With Image</span>
</div>
<div className="flex flex-col items-center gap-2">
<Avatar size="md" alt="Without image" imageSrc="https://cal.com/avatar.svg" />
<span className="text-subtle text-xs">Without Image</span>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,25 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Avatar } from "@calcom/ui/components/avatar";
const sampleImage = "https://cal.com/stakeholder/peer.jpg";
export const IndicatorExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex items-center gap-4">
<div className="flex flex-col items-center gap-2">
<Avatar
size="md"
alt="With indicator"
imageSrc={sampleImage}
indicator={
<div className="absolute bottom-0 right-0 h-2.5 w-2.5 rounded-full bg-green-500 ring-2 ring-white" />
}
/>
<span className="text-subtle text-xs">With Status Indicator</span>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,18 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Avatar } from "@calcom/ui/components/avatar";
const sampleImage = "https://cal.com/stakeholder/peer.jpg";
export const LinkExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex items-center gap-4">
<div className="flex flex-col items-center gap-2">
<Avatar size="md" alt="With link" imageSrc={sampleImage} href="https://cal.com" />
<span className="text-subtle text-xs">Clickable</span>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,103 +0,0 @@
---
title: Avatar
airtableId: reciflFrJo5cVgGrS
---
import { Avatar } from "@calcom/ui/components/avatar"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { SizesExample } from "./avatar.sizes"
import { ImageExample } from "./avatar.image"
import { TooltipExample } from "./avatar.tooltip"
import { LinkExample } from "./avatar.link"
import { IndicatorExample } from "./avatar.indicator"
import { ShapeExample } from "./avatar.shape"
## Size Variations
Avatars come in different sizes to suit various use cases.
<SizesExample/>
## Shape Variations
Avatars can be displayed in either circular or square shapes.
<ShapeExample/>
## With and Without Image
Avatars can display either an image or a fallback placeholder.
<ImageExample/>
## With Tooltip
Avatars can show additional information on hover using tooltips.
<TooltipExample/>
## With Link
Avatars can be made clickable by providing an href.
<LinkExample/>
## With Indicator
Avatars can include status indicators or badges.
<IndicatorExample/>
## Usage Example
Here's a complete example showing how to use the Avatar component with all its props:
```tsx
import { Avatar } from "@calcom/ui/components/avatar";
function Example() {
return (
<div className="flex flex-col gap-4">
{/* Basic Avatar */}
<Avatar
alt="User Name"
title="User Name"
size="md"
shape="circle"
imageSrc="https://example.com/avatar.jpg"
/>
{/* Avatar with Link */}
<Avatar
alt="User Name"
title="User Name"
size="lg"
shape="square"
imageSrc="https://example.com/avatar.jpg"
href="/profile"
/>
{/* Avatar with Tooltip and Indicator */}
<Avatar
alt="User Name"
title="User Name"
size="md"
shape="circle"
imageSrc="https://example.com/avatar.jpg"
indicator={<div className="absolute bottom-0 right-0 h-3 w-3 rounded-full bg-green-500" />}
/>
{/* Avatar with Fallback */}
<Avatar
alt="User Name"
title="User Name"
size="md"
shape="circle"
fallback={<span>UN</span>}
/>
</div>
);
}
```
@@ -1,24 +0,0 @@
import { Avatar } from "@calcom/ui/components/avatar";
export function ShapeExample() {
return (
<div className="flex flex-col gap-4">
<div className="flex items-center gap-4">
<Avatar
alt="Circle Avatar"
title="Circle Avatar"
size="lg"
shape="circle"
imageSrc="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
/>
<Avatar
alt="Square Avatar"
title="Square Avatar"
size="lg"
shape="square"
imageSrc="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
/>
</div>
</div>
);
}
@@ -1,29 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Avatar } from "@calcom/ui/components/avatar";
const sizes = ["xs", "xsm", "sm", "md", "mdLg", "lg", "xl"] as const;
const sampleImage = "https://cal.com/stakeholder/peer.jpg";
export const SizesExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex items-center gap-4">
{sizes.map((size) => (
<div key={size} className="flex flex-col items-center gap-2">
<Avatar size={size} alt={`${size} avatar`} imageSrc={sampleImage} />
<span className="text-subtle text-xs">{size}</span>
</div>
))}
</div>
<div className="mt-2 flex items-center gap-4">
{sizes.map((size) => (
<div key={size} className="flex flex-col items-center gap-2">
<Avatar size={size} alt={`${size} avatar`} imageSrc={sampleImage} shape="square" />
<span className="text-subtle text-xs">{size}</span>
</div>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,18 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Avatar } from "@calcom/ui/components/avatar";
const sampleImage = "https://cal.com/stakeholder/peer.jpg";
export const TooltipExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex items-center gap-4">
<div className="flex flex-col items-center gap-2">
<Avatar size="md" alt="With tooltip" imageSrc={sampleImage} title="Hover me!" />
<span className="text-subtle text-xs">Hover to see tooltip</span>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,33 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Badge } from "@calcom/ui/components/badge";
const variants = [
"default",
"warning",
"orange",
"success",
"green",
"gray",
"blue",
"red",
"error",
"grayWithoutHover",
] as const;
export const DotsExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex flex-wrap items-center gap-4">
{variants.map((variant) => (
<div key={variant} className="flex flex-col items-center gap-2">
<Badge variant={variant} withDot>
{variant}
</Badge>
<span className="text-subtle text-xs">Dot</span>
</div>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,33 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Badge } from "@calcom/ui/components/badge";
const variants = [
"default",
"warning",
"orange",
"success",
"green",
"gray",
"blue",
"red",
"error",
"grayWithoutHover",
] as const;
export const IconsExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex flex-wrap items-center gap-4">
{variants.map((variant) => (
<div key={variant} className="flex flex-col items-center gap-2">
<Badge variant={variant} startIcon="bell">
{variant}
</Badge>
<span className="text-subtle text-xs">Icon</span>
</div>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,33 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Badge } from "@calcom/ui/components/badge";
const variants = [
"default",
"warning",
"orange",
"success",
"green",
"gray",
"blue",
"red",
"error",
"grayWithoutHover",
] as const;
export const InteractiveExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex flex-wrap items-center gap-4">
{variants.map((variant) => (
<div key={variant} className="flex flex-col items-center gap-2">
<Badge variant={variant} onClick={() => alert(`${variant} badge clicked!`)}>
Click me
</Badge>
<span className="text-subtle text-xs">Clickable</span>
</div>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,42 +0,0 @@
---
title: Badge
airtableId: recso7QMfD2xwI04K
---
import { Badge } from "@calcom/ui/components/badge"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { VariantsExample } from "./badge.variants"
import { IconsExample } from "./badge.icons"
import { DotsExample } from "./badge.dots"
import { InteractiveExample } from "./badge.interactive"
import { RoundedExample } from "./badge.rounded"
## Variants and Sizes
Badges come in different variants and sizes to suit various use cases.
<VariantsExample/>
## With Icons
Badges can include icons to provide additional visual context.
<IconsExample/>
## With Dots
Badges can include dots, useful for showing status or notifications.
<DotsExample/>
## Interactive
Badges can be made interactive with click handlers.
<InteractiveExample/>
## Rounded
Badges can be fully rounded, useful for numerical indicators.
<RoundedExample/>
@@ -1,33 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Badge } from "@calcom/ui/components/badge";
const variants = [
"default",
"warning",
"orange",
"success",
"green",
"gray",
"blue",
"red",
"error",
"grayWithoutHover",
] as const;
export const RoundedExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex flex-wrap items-center gap-4">
{variants.map((variant) => (
<div key={variant} className="flex flex-col items-center gap-2">
<Badge variant={variant} rounded>
1
</Badge>
<span className="text-subtle text-xs">{variant}</span>
</div>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,42 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Badge } from "@calcom/ui/components/badge";
const variants = [
"default",
"warning",
"orange",
"success",
"green",
"gray",
"blue",
"red",
"error",
"grayWithoutHover",
] as const;
const sizes = ["sm", "md", "lg"] as const;
export const VariantsExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-8">
{variants.map((variant) => (
<div key={variant} className="space-y-2">
<h4 className="text-default text-sm capitalize">{variant}</h4>
<div className="flex items-center gap-4">
{sizes.map((size) => (
<div key={size} className="flex flex-col items-center gap-2">
<Badge variant={variant} size={size}>
{variant}
</Badge>
<span className="text-subtle text-xs">{size}</span>
</div>
))}
</div>
</div>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,63 +0,0 @@
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Button } from "@calcom/ui/components/button";
import { ButtonGroup } from "@calcom/ui/components/buttonGroup";
export const ButtonGroupExample = () => {
return (
<RenderComponentWithSnippet className="flex flex-col space-y-6">
{/* Basic Button Group */}
<ButtonGroup>
<Button variant="icon" StartIcon="ellipsis" color="secondary" />
<Button variant="icon" StartIcon="ellipsis" color="secondary" />
<Button variant="icon" StartIcon="ellipsis" color="secondary" />
</ButtonGroup>
{/* Combined Button Group */}
<ButtonGroup combined>
<Button variant="icon" StartIcon="ellipsis" color="secondary" />
<Button variant="icon" StartIcon="ellipsis" color="secondary" />
<Button variant="icon" StartIcon="ellipsis" color="secondary" />
</ButtonGroup>
{/* Button Group with different variants */}
<ButtonGroup combined>
<Button variant="icon" color="secondary" StartIcon="ellipsis" />
<Button variant="icon" color="secondary" StartIcon="ellipsis" />
<Button variant="icon" color="destructive" StartIcon="trash" />
</ButtonGroup>
</RenderComponentWithSnippet>
);
};
export const ButtonGroupSizes = () => {
const sizes = ["xs", "sm", "base", "lg"] as const;
return (
<RenderComponentWithSnippet className="flex flex-col space-y-6">
{sizes.map((size) => (
<ButtonGroup key={size}>
<Button variant="icon" color="secondary" StartIcon="ellipsis" size={size} />
<Button variant="icon" color="secondary" StartIcon="ellipsis" size={size} />
<Button variant="icon" color="secondary" StartIcon="ellipsis" size={size} />
</ButtonGroup>
))}
</RenderComponentWithSnippet>
);
};
export const ButtonGroupSizesCombined = () => {
const sizes = ["xs", "sm", "base", "lg"] as const;
return (
<RenderComponentWithSnippet className="flex flex-col space-y-6">
{sizes.map((size) => (
<ButtonGroup key={size} combined>
<Button variant="icon" color="secondary" StartIcon="ellipsis" size={size} />
<Button variant="icon" color="secondary" StartIcon="ellipsis" size={size} />
<Button variant="icon" color="secondary" StartIcon="ellipsis" size={size} />
</ButtonGroup>
))}
</RenderComponentWithSnippet>
);
};
@@ -1,42 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Row } from "@/app/components/row";
import { Button } from "@calcom/ui/components/button";
const colors = ["primary", "secondary", "minimal", "destructive"] as const;
export const DisabledExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-8">
<div>
<h3 className="text-default mb-4 text-sm">Disabled State</h3>
<Row>
{colors.map((color) => (
<div key={color} className="flex flex-col items-center gap-2">
<Button color={color} disabled>
{color}
</Button>
<span className="text-subtle text-xs">Disabled</span>
</div>
))}
</Row>
</div>
<div>
<h3 className="text-default mb-4 text-sm">Disabled with Icons</h3>
<Row>
{colors.map((color) => (
<div key={color} className="flex flex-col items-center gap-2">
<Button color={color} disabled StartIcon="calendar">
{color}
</Button>
<span className="text-subtle text-xs">With Icon</span>
</div>
))}
</Row>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,42 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Row } from "@/app/components/row";
import { Button } from "@calcom/ui/components/button";
const colors = ["primary", "secondary", "minimal", "destructive"] as const;
export const IconsExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-8">
<div>
<h3 className="text-default mb-4 text-sm">Start Icon</h3>
<Row>
{colors.map((color) => (
<div key={color} className="flex flex-col items-center gap-2">
<Button color={color} StartIcon="calendar">
{color}
</Button>
<span className="text-subtle text-xs">Start Icon</span>
</div>
))}
</Row>
</div>
<div>
<h3 className="text-default mb-4 text-sm">End Icon</h3>
<Row>
{colors.map((color) => (
<div key={color} className="flex flex-col items-center gap-2">
<Button color={color} EndIcon="arrow-right">
{color}
</Button>
<span className="text-subtle text-xs">End Icon</span>
</div>
))}
</Row>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,44 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Row } from "@/app/components/row";
import { Button } from "@calcom/ui/components/button";
const colors = ["primary", "secondary", "minimal", "destructive"] as const;
export const LoadingExample: React.FC = () => {
return (
<RenderComponentWithSnippet>
<div className="space-y-8">
<div>
<h3 className="text-default mb-4 text-sm">Loading State</h3>
<Row>
{colors.map((color) => (
<div key={color} className="flex flex-col items-center gap-2">
<Button color={color} loading>
{color}
</Button>
<span className="text-subtle text-xs">Loading</span>
</div>
))}
</Row>
</div>
<div>
<h3 className="text-default mb-4 text-sm">Loading with Icons</h3>
<Row>
{colors.map((color) => (
<div key={color} className="flex flex-col items-center gap-2">
<Button color={color} loading StartIcon="calendar">
{color}
</Button>
<span className="text-subtle text-xs">With Icon</span>
</div>
))}
</Row>
</div>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,98 +0,0 @@
---
title: Button
airtableId: recEJyzUeMMXCjyTB
---
import { Button } from "@calcom/ui/components/button"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { OnClickExample } from "./button.onClick"
import { VariantExample } from "./button.variant"
import { IconsExample } from "./button.icons"
import { LoadingExample } from "./button.loading"
import { DisabledExample } from "./button.disabled"
import { ButtonGroupExample,ButtonGroupSizes,ButtonGroupSizesCombined} from "./button.buttongroup"
## Variants and Colors
Buttons come in different variants and colors to suit different purposes. Each variant can be combined with any color and size.
<VariantExample/>
## Icons
Buttons can include icons at the start or end. The icon color and size are automatically handled by the button component.
<IconsExample/>
## Loading State
Buttons can show a loading state to indicate an ongoing action. The loading spinner replaces the button text while maintaining the button's width.
<LoadingExample/>
## Disabled State
Buttons can be disabled to indicate that they are not interactive. The disabled state is visually distinct and prevents any click events.
<DisabledExample/>
## onClick Events
Buttons handle click events like any other button component. You can provide an onClick handler to respond to user interactions.
<OnClickExample/>
Providing a loading state will add a spinner on the left side of the button.
<LoadingExample/>
## Disabled
<DisabledExample/>
## Button Groups
Buttons can be grouped together using the ButtonGroup component. The ButtonGroup component can be used to create a group of related buttons with consistent spacing and optional combined borders.
<ButtonGroupExample/>
### Button Group Sizes
#### Separated
<ButtonGroupSizes/>
#### Combined
<ButtonGroupSizesCombined/>
## Usage
```tsx
// Primary button (default)
<Button>Primary Button</Button>
// Secondary button
<Button color="secondary">Secondary Button</Button>
// Minimal button
<Button color="minimal">Minimal Button</Button>
// Destructive button
<Button color="destructive">Destructive Button</Button>
// With start icon
<Button StartIcon="plus">Add Item</Button>
// With end icon
<Button EndIcon="chevronRight">View More</Button>
// With link
<Button href="/some-path">Navigate</Button>
// Different sizes
<Button size="xs">Extra Small</Button>
<Button size="sm">Small</Button>
<Button size="base">Base (Default)</Button>
<Button size="lg">Large</Button>
```
@@ -1,14 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Row } from "@/app/components/row";
import { Button } from "@calcom/ui/components/button";
export const OnClickExample: React.FC = () => (
<RenderComponentWithSnippet>
<Row>
<Button onClick={() => alert("hello")}>Trigger Alert</Button>
</Row>
</RenderComponentWithSnippet>
);
@@ -1,43 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Row } from "@/app/components/row";
import { Button } from "@calcom/ui/components/button";
const variants = ["button", "icon"] as const;
const colors = ["primary", "secondary", "minimal", "destructive"] as const;
const sizes = ["xs", "sm", "base", "lg"] as const;
export const VariantExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-8">
{variants.map((variant) => (
<div key={variant} className="space-y-6">
<h3 className="text-default text-sm capitalize">{variant === "button" ? "Default" : variant}</h3>
<div className="space-y-6">
{colors.map((color) => (
<div key={color} className="space-y-2">
<h4 className="text-default text-sm capitalize">{color}</h4>
<Row>
{sizes.map((size) => (
<div key={size} className="flex flex-col items-center gap-2">
<Button
variant={variant}
color={color}
size={size}
StartIcon={variant === "icon" ? "plus" : undefined}>
{variant !== "icon" && `Label`}
</Button>
<span className="text-subtle text-xs">{size}</span>
</div>
))}
</Row>
</div>
))}
</div>
</div>
))}
</div>
</RenderComponentWithSnippet>
);
@@ -1,31 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Checkbox } from "@calcom/ui/components/form";
export const BasicExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex flex-wrap gap-4">
<div className="flex flex-col items-center justify-center gap-2">
<Checkbox id="basic-unchecked" />
<span className="text-subtle text-xs">Unchecked</span>
</div>
<div className="flex flex-col items-center justify-center gap-2">
<Checkbox id="basic-checked" defaultChecked />
<span className="text-subtle text-xs">Checked</span>
</div>
<div className="flex flex-col items-center justify-center gap-2">
<Checkbox id="basic-disabled" disabled />
<span className="text-subtle text-xs">Disabled</span>
</div>
<div className="flex flex-col items-center justify-center gap-2">
<Checkbox id="basic-disabled-checked" disabled defaultChecked />
<span className="text-subtle text-xs">Disabled Checked</span>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,15 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { CheckboxField } from "@calcom/ui/components/form";
export const DescriptionExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-4">
<CheckboxField description="This is a regular description" id="desc-normal" />
<CheckboxField description="This description acts as the label" descriptionAsLabel id="desc-as-label" />
<CheckboxField id="desc-long" label="Label and description" description="Label and description" />
</div>
</RenderComponentWithSnippet>
);
@@ -1,16 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { CheckboxField } from "@calcom/ui/components/form";
export const InfoIconExample: React.FC = () => (
<RenderComponentWithSnippet>
<CheckboxField
description="Checkbox with additional information"
id="info-icon"
label="Information Icon"
informationIconText="This is additional information that appears in a tooltip"
/>
</RenderComponentWithSnippet>
);
@@ -1,24 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { CheckboxField } from "@calcom/ui/components/form";
export const LabelPositionExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-4">
<CheckboxField
description="Description with label above (default on mobile)"
id="label-above"
label="Label Above"
/>
<div className="sm:min-w-[400px]">
<CheckboxField
description="Description with label to the side (on larger screens)"
id="label-side"
label="Label to the Side"
/>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,35 +0,0 @@
---
title: Checkbox
airtableId: recXGPNAfDJVm8NHo
---
import { Checkbox } from "@calcom/ui/components/form"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { BasicExample } from "./checkbox.basic"
import { DescriptionExample } from "./checkbox.description"
import { LabelPositionExample } from "./checkbox.labelPosition"
import { InfoIconExample } from "./checkbox.infoIcon"
## Basic Usage
Checkboxes in their various states - unchecked, checked, disabled, and disabled checked.
<BasicExample/>
## With Description
Checkboxes can include descriptions to provide additional context.
<DescriptionExample/>
## Label Positions
Labels can be positioned above or to the side of the checkbox.
<LabelPositionExample/>
## With Information Icon
Checkboxes can include an information icon with a tooltip for additional details.
<InfoIconExample/>
@@ -1,33 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { Button } from "@calcom/ui/components/button";
import { Dialog, DialogClose, DialogContent, DialogFooter } from "@calcom/ui/components/dialog";
export const BasicExample: React.FC = () => {
const [open, setOpen] = useState(false);
return (
<RenderComponentWithSnippet>
<div className="space-y-2">
<Button onClick={() => setOpen(true)}>Open Basic Dialog</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent
title="Basic Dialog"
description="This is a basic dialog with a title and description.">
<p className="text-default text-sm">
Dialogs are used to display content that requires user attention or interaction. They appear
above the page content and must be closed before interacting with the page again.
</p>
<DialogFooter>
<DialogClose />
<Button>Action</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,37 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { Button } from "@calcom/ui/components/button";
import { Dialog, DialogClose, DialogContent, DialogFooter } from "@calcom/ui/components/dialog";
export const ConfirmationExample: React.FC = () => {
const [open, setOpen] = useState(false);
return (
<RenderComponentWithSnippet>
<div className="space-y-2">
<Button color="destructive" onClick={() => setOpen(true)}>
Delete Item
</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent
title="Confirm Deletion"
description="Are you sure you want to delete this item? This action cannot be undone.">
<DialogFooter>
<DialogClose />
<Button
color="destructive"
onClick={() => {
setOpen(false);
}}>
Delete
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,56 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { Button } from "@calcom/ui/components/button";
import { Dialog, DialogClose, DialogContent, DialogFooter } from "@calcom/ui/components/dialog";
import { TextAreaField } from "@calcom/ui/components/form";
export const FormExample: React.FC = () => {
const [open, setOpen] = useState(false);
const [formInput, setFormInput] = useState("");
return (
<RenderComponentWithSnippet>
<div className="space-y-2">
<Button color="secondary" onClick={() => setOpen(true)}>
Open Form Dialog
</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent title="Feedback Form" description="Please provide your feedback below">
<div>
<TextAreaField
name="feedback"
label={
<>
Your Feedback
<span className="text-subtle font-normal"> (Optional)</span>
</>
}
value={formInput}
onChange={(e) => setFormInput(e.target.value)}
/>
</div>
<DialogFooter>
<DialogClose />
<Button
onClick={() => {
setOpen(false);
setFormInput("");
}}>
Submit Feedback
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<div className="mt-6">
<h4 className="text-emphasis text-sm font-medium">Current Form Input:</h4>
<pre className="text-default bg-subtle mt-2 rounded-md p-4 text-sm">
{formInput || "No input yet"}
</pre>
</div>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,58 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { Button } from "@calcom/ui/components/button";
import { Dialog, DialogClose, DialogContent, DialogFooter } from "@calcom/ui/components/dialog";
export const LargeContentExample: React.FC = () => {
const [open, setOpen] = useState(false);
return (
<RenderComponentWithSnippet>
<div className="space-y-2">
<Button color="secondary" onClick={() => setOpen(true)}>
Open Large Content Dialog
</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent
title="Terms and Conditions"
description="Please review our terms and conditions carefully"
enableOverflow>
<div className="space-y-6">
{Array.from({ length: 5 }).map((_, i) => (
<div key={i} className="space-y-4">
<h3 className="text-emphasis font-semibold">Section {i + 1}</h3>
<p className="text-default text-sm">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam in dui mauris. Vivamus
hendrerit arcu sed erat molestie vehicula. Sed auctor neque eu tellus rhoncus ut eleifend
nibh porttitor. Ut in nulla enim. Phasellus molestie magna non est bibendum non venenatis
nisl tempor.
</p>
<p className="text-default text-sm">
Suspendisse in orci enim. Integer vel sapien at risus ultrices ornare nec sit amet nibh.
Duis blandit lectus ac odio rhoncus non congue diam bibendum. Aliquam erat volutpat.
</p>
{i === 2 && (
<div className="border-subtle rounded-lg border p-4 shadow-sm">
<h4 className="text-emphasis mb-2 font-medium">Important Notice</h4>
<p className="text-default text-sm">
This is a highlighted section within the content to demonstrate how different UI
elements appear within a scrollable dialog.
</p>
</div>
)}
</div>
))}
</div>
<DialogFooter>
<DialogClose>Decline</DialogClose>
<Button>Accept Terms</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,35 +0,0 @@
---
title: Dialog
airtableId: rechCoaoEMZLnLChR
---
import { Dialog } from "@calcom/ui/components/dialog"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { BasicExample } from "./dialog.basic"
import { LargeContentExample } from "./dialog.largeContent"
import { FormExample } from "./dialog.form"
import { ConfirmationExample } from "./dialog.confirmation"
## Basic Dialog
A simple dialog with a title, description, and action buttons.
<BasicExample/>
## Large Content Dialog
A dialog that can handle scrollable content, such as terms and conditions.
<LargeContentExample/>
## Form Dialog
A dialog containing a form for user input.
<FormExample/>
## Confirmation Dialog
A dialog for confirming destructive actions.
<ConfirmationExample/>
@@ -1,38 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Avatar } from "@calcom/ui/components/avatar";
import {
Dropdown,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownItem,
DropdownMenuLabel,
DropdownMenuSeparator,
} from "@calcom/ui/components/dropdown";
export const AvatarTriggerExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex flex-wrap items-center gap-8">
<div className="flex flex-col items-center gap-2">
<Dropdown>
<DropdownMenuTrigger asChild>
<button className="cursor-pointer">
<Avatar size="sm" imageSrc="https://cal.com/stakeholder/peer.jpg" alt="Avatar" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Profile</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownItem>View Profile</DropdownItem>
<DropdownItem>Settings</DropdownItem>
<DropdownMenuSeparator />
<DropdownItem className="text-error">Sign out</DropdownItem>
</DropdownMenuContent>
</Dropdown>
<span className="text-subtle text-xs">Avatar Menu</span>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,84 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import type { IconName } from "@calcom/ui";
import { Button } from "@calcom/ui/components/button";
import {
Dropdown,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownItem,
DropdownMenuLabel,
} from "@calcom/ui/components/dropdown";
const menuItems = [
{ label: "View", icon: "eye", kbd: "V" },
{ label: "Edit", icon: "edit", kbd: "E" },
{ label: "Share", icon: "share", kbd: "⌘S" },
{ label: "Delete", icon: "trash", destructive: true, kbd: "⌘⌫" },
] as {
label: string;
icon: IconName;
destructive?: boolean;
kbd?: string;
}[];
export const ButtonTriggerExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex flex-wrap items-center gap-8">
{/* Default Button */}
<div className="flex flex-col items-center gap-2">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>Menu</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Actions</DropdownMenuLabel>
{menuItems.map((item) => (
<DropdownItem color={item.destructive ? "destructive" : "secondary"} key={item.label}>
{item.label}
</DropdownItem>
))}
</DropdownMenuContent>
</Dropdown>
<span className="text-subtle text-xs">Default</span>
</div>
{/* Button with Icon */}
<div className="flex flex-col items-center gap-2">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button>More Actions</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Quick Actions</DropdownMenuLabel>
{menuItems.map((item) => (
<DropdownItem key={item.label} color={item.destructive ? "destructive" : undefined}>
{item.label}
</DropdownItem>
))}
</DropdownMenuContent>
</Dropdown>
<span className="text-subtle text-xs">With Icon</span>
</div>
{/* Icon Button */}
<div className="flex flex-col items-center gap-2">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button variant="icon" StartIcon="calendar" />
</DropdownMenuTrigger>
<DropdownMenuContent>
{menuItems.map((item) => (
<DropdownItem key={item.label} color={item.destructive ? "destructive" : undefined}>
{item.label}
</DropdownItem>
))}
</DropdownMenuContent>
</Dropdown>
<span className="text-subtle text-xs">Icon Only</span>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,86 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Avatar } from "@calcom/ui/components/avatar";
import { Button } from "@calcom/ui/components/button";
import {
Dropdown,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownItem,
DropdownMenuLabel,
DropdownMenuSeparator,
} from "@calcom/ui/components/dropdown";
export const ComplexExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="flex flex-wrap items-center gap-8">
{/* Example 1: Complex Menu Structure */}
<div className="flex flex-col items-center gap-2">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button color="secondary">Open</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>My account</DropdownMenuLabel>
<DropdownItem>Profile</DropdownItem>
<DropdownItem>Billing</DropdownItem>
<DropdownItem>Settings</DropdownItem>
<DropdownItem>Keyboard Shortcuts</DropdownItem>
<DropdownMenuSeparator />
<DropdownMenuLabel>Team</DropdownMenuLabel>
<DropdownItem>Invite users</DropdownItem>
<DropdownItem>New team</DropdownItem>
<DropdownMenuSeparator />
<DropdownItem>Github</DropdownItem>
<DropdownItem>Support</DropdownItem>
<DropdownItem>API</DropdownItem>
</DropdownMenuContent>
</Dropdown>
<span className="text-subtle text-xs">Complex Menu</span>
</div>
{/* Example 2: Avatar with Icons */}
<div className="flex flex-col items-center gap-2">
<Dropdown>
<DropdownMenuTrigger asChild>
<button className="cursor-pointer">
<Avatar size="md" imageSrc="https://cal.com/stakeholder/peer.jpg" alt="Avatar" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>peer@cal.com</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownItem StartIcon="user">My account</DropdownItem>
<DropdownItem StartIcon="map">Plan</DropdownItem>
<DropdownItem StartIcon="credit-card">Billing</DropdownItem>
<DropdownItem StartIcon="link">Integrations</DropdownItem>
<DropdownItem StartIcon="upload">Module</DropdownItem>
<DropdownMenuSeparator />
<DropdownItem className="text-error" StartIcon="log-out">
Log out
</DropdownItem>
</DropdownMenuContent>
</Dropdown>
<span className="text-subtle text-xs">With Icons</span>
</div>
{/* Example 3: Simple Actions */}
<div className="flex flex-col items-center gap-2">
<Dropdown>
<DropdownMenuTrigger asChild>
<Button variant="icon" color="secondary" StartIcon="calendar" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownItem>Rename</DropdownItem>
<DropdownItem>Duplicate</DropdownItem>
<DropdownItem>Move</DropdownItem>
<DropdownItem color="destructive">Delete</DropdownItem>
</DropdownMenuContent>
</Dropdown>
<span className="text-subtle text-xs">Simple Actions</span>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,28 +0,0 @@
---
title: Dropdown
airtableId: recMePWqulsKOCa59
---
import { Dropdown } from "@calcom/ui/components/dropdown"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { ButtonTriggerExample } from "./dropdown.buttonTrigger"
import { AvatarTriggerExample } from "./dropdown.avatarTrigger"
import { ComplexExample } from "./dropdown.complex"
## Button Trigger
Dropdowns can be triggered by various types of buttons.
<ButtonTriggerExample/>
## Avatar Trigger
Dropdowns can be triggered by avatar components, commonly used for profile menus.
<AvatarTriggerExample/>
## Complex Examples
More complex dropdown examples with various combinations of labels, icons, and separators.
<ComplexExample/>
@@ -1,17 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
export const BasicExample: React.FC = () => (
<RenderComponentWithSnippet>
<EmptyScreen
Icon="calendar"
headline="No upcoming meetings"
description="Create a meeting to get started"
buttonText="Create Meeting"
buttonOnClick={() => alert("Create Meeting clicked")}
/>
</RenderComponentWithSnippet>
);
@@ -1,37 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
export const BorderExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-8">
{/* Without Border */}
<div>
<h4 className="text-emphasis mb-4 text-sm font-medium">Without Border</h4>
<EmptyScreen
Icon="grid-3x3"
headline="No apps installed"
description="Browse available apps in the marketplace"
buttonText="Browse Apps"
buttonOnClick={() => alert("Browse Apps clicked")}
border={false}
/>
</div>
{/* With Solid Border */}
<div>
<h4 className="text-emphasis mb-4 text-sm font-medium">With Solid Border</h4>
<EmptyScreen
Icon="mail"
headline="No messages"
description="Your inbox is empty"
buttonText="Compose"
buttonOnClick={() => alert("Compose clicked")}
dashedBorder={false}
/>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,18 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
export const CustomIconExample: React.FC = () => (
<RenderComponentWithSnippet>
<EmptyScreen
Icon="user"
iconClassName="text-emphasis h-12 w-12"
headline="No team members"
description="Add team members to collaborate"
buttonText="Add Members"
buttonOnClick={() => alert("Add Members clicked")}
/>
</RenderComponentWithSnippet>
);
@@ -1,28 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
export const HtmlContentExample: React.FC = () => (
<RenderComponentWithSnippet>
<EmptyScreen
Icon="info"
headline={
<span className="text-emphasis">
Custom Headline with <strong>HTML</strong>
</span>
}
description={
<div className="text-subtle space-y-2">
<p>This is a custom description with multiple paragraphs.</p>
<p>
You can include <em>any HTML content</em> here.
</p>
</div>
}
buttonText="Learn More"
buttonOnClick={() => alert("Learn More clicked")}
/>
</RenderComponentWithSnippet>
);
@@ -1,41 +0,0 @@
---
title: Empty Screen
---
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { BasicExample } from "./emptyScreen.basic"
import { CustomIconExample } from "./emptyScreen.customIcon"
import { RawButtonExample } from "./emptyScreen.rawButton"
import { BorderExample } from "./emptyScreen.border"
import { HtmlContentExample } from "./emptyScreen.htmlContent"
## Basic Usage
A simple empty screen with an icon, headline, description, and action button.
<BasicExample/>
## Custom Icon
Empty screen with a custom icon and styling.
<CustomIconExample/>
## Raw Button
Empty screen with custom button layout.
<RawButtonExample/>
## Border Variations
Empty screen with different border styles.
<BorderExample/>
## HTML Content
Empty screen with custom HTML content in headline and description.
<HtmlContentExample/>
@@ -1,26 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Button } from "@calcom/ui/components/button";
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
export const RawButtonExample: React.FC = () => (
<RenderComponentWithSnippet>
<EmptyScreen
Icon="link"
headline="No links found"
description="Create a new link to share"
buttonRaw={
<div className="flex space-x-2">
<Button color="primary" onClick={() => alert("Create Link clicked")}>
Create Link
</Button>
<Button color="secondary" onClick={() => alert("Import Links clicked")}>
Import Links
</Button>
</div>
}
/>
</RenderComponentWithSnippet>
);
@@ -1,67 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useForm } from "react-hook-form";
import { Button } from "@calcom/ui/components/button";
import { InputField } from "@calcom/ui/components/form";
import { Icon } from "@calcom/ui/components/icon";
type FormValues = {
website: string;
githubUsername: string;
customDomain: string;
};
export const AddOnsExample: React.FC = () => {
const form = useForm<FormValues>({
defaultValues: {
website: "",
githubUsername: "",
customDomain: "",
},
});
const onSubmit = (data: FormValues) => {
console.log("Form submitted:", data);
};
return (
<RenderComponentWithSnippet>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<InputField
label="Website"
addOnLeading="https://"
{...form.register("website")}
error={form.formState.errors.website?.message}
/>
<InputField
label="Website"
addOnLeading="apple.cal.com/team/seeded-team/"
{...form.register("website")}
error={form.formState.errors.website?.message}
/>
<InputField
label="GitHub Username"
addOnLeading={<Icon name="github" className="text-subtle h-4 w-4" />}
{...form.register("githubUsername")}
error={form.formState.errors.githubUsername?.message}
/>
<InputField
label="Custom Domain"
addOnSuffix=".cal.com"
{...form.register("customDomain")}
error={form.formState.errors.customDomain?.message}
/>
<div className="flex justify-end space-x-2">
<Button color="minimal" type="button" onClick={() => form.reset()}>
Reset
</Button>
<Button type="submit" loading={form.formState.isSubmitting}>
Submit
</Button>
</div>
</form>
</RenderComponentWithSnippet>
);
};
@@ -1,59 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useForm } from "react-hook-form";
import { Button } from "@calcom/ui/components/button";
import { InputField } from "@calcom/ui/components/form";
type FormValues = {
username: string;
email: string;
password: string;
};
export const BasicExample: React.FC = () => {
const form = useForm<FormValues>({
defaultValues: {
username: "",
email: "",
password: "",
},
});
const onSubmit = (data: FormValues) => {
console.log("Form submitted:", data);
};
return (
<RenderComponentWithSnippet>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<InputField
label="Username"
{...form.register("username")}
error={form.formState.errors.username?.message}
/>
<InputField
label="Email"
type="email"
{...form.register("email")}
error={form.formState.errors.email?.message}
/>
<InputField
label="Password"
type="password"
{...form.register("password")}
error={form.formState.errors.password?.message}
/>
<div className="flex justify-end space-x-2">
<Button color="minimal" type="button" onClick={() => form.reset()}>
Reset
</Button>
<Button type="submit" loading={form.formState.isSubmitting}>
Submit
</Button>
</div>
</form>
</RenderComponentWithSnippet>
);
};
@@ -1,26 +0,0 @@
---
title: Form
---
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { BasicExample } from "./form.basic"
import { AddOnsExample } from "./form.addOns"
import { SpecialFieldsExample } from "./form.specialFields"
## Basic Fields
Basic form fields with validation using React Hook Form and Zod.
<BasicExample/>
## Fields with Add-ons
Form fields with leading and trailing add-ons.
<AddOnsExample/>
## Number and Special Fields
Form fields for numbers and special input types.
<SpecialFieldsExample/>
@@ -1,53 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useForm } from "react-hook-form";
import { Button } from "@calcom/ui/components/button";
import { InputField } from "@calcom/ui/components/form";
type FormValues = {
age?: number;
phone?: string;
};
export const SpecialFieldsExample: React.FC = () => {
const form = useForm<FormValues>({
defaultValues: {
age: undefined,
phone: "",
},
});
const onSubmit = (data: FormValues) => {
console.log("Form submitted:", data);
};
return (
<RenderComponentWithSnippet>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<InputField
label="Age"
type="number"
{...form.register("age", { valueAsNumber: true })}
error={form.formState.errors.age?.message}
/>
<InputField
label="Phone"
type="tel"
addOnLeading="+"
{...form.register("phone")}
error={form.formState.errors.phone?.message}
/>
<div className="flex justify-end space-x-2">
<Button color="minimal" type="button" onClick={() => form.reset()}>
Reset
</Button>
<Button type="submit" loading={form.formState.isSubmitting}>
Submit
</Button>
</div>
</form>
</RenderComponentWithSnippet>
);
};
@@ -1,35 +0,0 @@
---
title: Input
airtableId: rec2yv97ak5hv3kIL
---
import { Input } from "@calcom/ui/components/form"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { StatesExample } from "./input.states"
import { TypesExample } from "./input.types"
import { WidthsExample } from "./input.widths"
import { ValuesExample } from "./input.values"
## States
Input fields in different states: default, disabled, and readonly.
<StatesExample/>
## Types
Different types of input fields for various data types.
<TypesExample/>
## Widths
Input fields with different width configurations.
<WidthsExample/>
## With Values
Input fields with default values and placeholders.
<ValuesExample/>
@@ -1,37 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Input } from "@calcom/ui/components/form";
const states = ["default", "disabled", "readonly"] as const;
const sizes = ["sm", "md"] as const;
export const StatesExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-6">
<div className="space-y-4">
{states.map((state) => (
<div key={state} className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm capitalize">{state}</h3>
<div className="flex flex-wrap items-center gap-4">
{sizes.map((size) => (
<div key={size} className="flex flex-col items-center gap-2">
<Input
type="text"
placeholder={`${state} input`}
disabled={state === "disabled"}
readOnly={state === "readonly"}
size={size}
isFullWidth={false}
/>
<span className="text-subtle text-xs">{size}</span>
</div>
))}
</div>
</div>
))}
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,46 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Input } from "@calcom/ui/components/form";
const types = ["text", "email", "password", "number", "tel", "url", "search"] as const;
const sizes = ["sm", "md"] as const;
export const TypesExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-6">
<div className="space-y-4">
{types.map((type) => (
<div key={type} className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm capitalize">{type}</h3>
<div className="flex flex-wrap items-center gap-4">
{sizes.map((size) => (
<div key={size} className="flex flex-col items-center gap-2">
<Input
type={type}
placeholder={`Enter ${type}...`}
defaultValue={
type === "email"
? "example@cal.com"
: type === "url"
? "https://cal.com"
: type === "number"
? "42"
: type === "tel"
? "+1234567890"
: ""
}
size={size}
isFullWidth={false}
/>
<span className="text-subtle text-xs">{size}</span>
</div>
))}
</div>
</div>
))}
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,31 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Input } from "@calcom/ui/components/form";
const sizes = ["sm", "md"] as const;
export const ValuesExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-6">
<div className="space-y-4">
{sizes.map((size) => (
<div key={size} className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Size: {size}</h3>
<div className="space-y-4">
<div className="flex flex-col space-y-2">
<h4 className="text-subtle text-xs">Default Value</h4>
<Input type="text" defaultValue="Default text value" size={size} />
</div>
<div className="flex flex-col space-y-2">
<h4 className="text-subtle text-xs">Placeholder</h4>
<Input type="text" placeholder="Placeholder text" size={size} />
</div>
</div>
</div>
))}
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,31 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Input } from "@calcom/ui/components/form";
const sizes = ["sm", "md"] as const;
export const WidthsExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-6">
<div className="space-y-4">
{sizes.map((size) => (
<div key={size} className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Size: {size}</h3>
<div className="space-y-4">
<div className="flex flex-col space-y-2">
<h4 className="text-subtle text-xs">Full Width</h4>
<Input type="text" placeholder="Full width input" size={size} isFullWidth />
</div>
<div className="flex flex-col space-y-2">
<h4 className="text-subtle text-xs">Auto Width</h4>
<Input type="text" placeholder="Auto width input" size={size} isFullWidth={false} />
</div>
</div>
</div>
))}
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,71 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useForm, FormProvider } from "react-hook-form";
import { MultiOptionInput } from "@calcom/ui/components/form";
type FormValues = {
customPlaceholders: Array<{ label: string; id: string }>;
noMoveButtons: Array<{ label: string; id: string }>;
customLabel: Array<{ label: string; id: string }>;
keyValuePairs: Array<{ label: string; value: string; id: string }>;
};
export const CustomizationExample: React.FC = () => {
const methods = useForm<FormValues>();
return (
<RenderComponentWithSnippet>
<FormProvider {...methods}>
<div className="space-y-6">
<div className="space-y-4">
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Key-Value Pairs</h3>
<p className="text-subtle text-xs">Allows inputting keys and values</p>
<MultiOptionInput<FormValues>
fieldArrayName="keyValuePairs"
keyValueMode
keyLabel="Environment Variable"
valueLabel="Value"
optionPlaceholders={["NODE_ENV", "PORT", "DATABASE_URL"]}
valuePlaceholders={["production", "3000", "postgres://..."]}
defaultNumberOfOptions={3}
keyValueDelimiters={[":", "="]}
/>
</div>
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Custom Placeholders</h3>
<MultiOptionInput<FormValues>
fieldArrayName="customPlaceholders"
optionPlaceholders={["Enter your name", "Enter your email", "Enter your phone"]}
defaultNumberOfOptions={3}
/>
</div>
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Without Move Buttons</h3>
<MultiOptionInput<FormValues>
fieldArrayName="noMoveButtons"
optionPlaceholders={["Static option 1", "Static option 2"]}
defaultNumberOfOptions={2}
showMoveButtons={false}
/>
</div>
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Custom Add Button Label</h3>
<MultiOptionInput<FormValues>
fieldArrayName="customLabel"
optionPlaceholders={["Social media link"]}
defaultNumberOfOptions={1}
addOptionLabel="Add another social media link"
/>
</div>
</div>
</div>
</FormProvider>
</RenderComponentWithSnippet>
);
};
@@ -1,76 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useForm, FormProvider } from "react-hook-form";
import { MultiOptionInput } from "@calcom/ui/components/form";
type FormValues = {
newlineOptions: Array<{ label: string; id: string }>;
commaOptions: Array<{ label: string; id: string }>;
customOptions: Array<{ label: string; id: string }>;
keyValueOptions: Array<{ label: string; value: string; id: string }>;
};
export const PasteExample: React.FC = () => {
const methods = useForm<FormValues>();
return (
<RenderComponentWithSnippet>
<FormProvider {...methods}>
<div className="space-y-6">
<div className="space-y-4">
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Default Delimiters (Newline and Comma)</h3>
<p className="text-subtle text-xs">
Try pasting: &ldquo;Option 1, Option 2&rdquo; or multiple lines
</p>
<MultiOptionInput<FormValues>
fieldArrayName="newlineOptions"
optionPlaceholders={["Paste here..."]}
defaultNumberOfOptions={1}
/>
</div>
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Comma Only Delimiter</h3>
<p className="text-subtle text-xs">Try pasting: &ldquo;First, Second, Third&rdquo;</p>
<MultiOptionInput<FormValues>
fieldArrayName="commaOptions"
optionPlaceholders={["Paste here..."]}
defaultNumberOfOptions={1}
pasteDelimiters={[","]}
/>
</div>
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Key-Value Pair Paste Support</h3>
<p className="text-subtle text-xs">
Try pasting: &ldquo;NODE_ENV=production&rdquo; or &ldquo;KEY1:value1, KEY2:value2&rdquo;
</p>
<MultiOptionInput<FormValues>
fieldArrayName="keyValueOptions"
keyValueMode
optionPlaceholders={["Key..."]}
valuePlaceholders={["Value..."]}
defaultNumberOfOptions={1}
keyValueDelimiters={[":", "="]}
/>
</div>
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Custom Delimiters (Semicolon and Pipe)</h3>
<p className="text-subtle text-xs">Try pasting: &ldquo;One;Two|Three&rdquo;</p>
<MultiOptionInput<FormValues>
fieldArrayName="customOptions"
optionPlaceholders={["Paste here..."]}
defaultNumberOfOptions={1}
pasteDelimiters={[";", "|"]}
/>
</div>
</div>
</div>
</FormProvider>
</RenderComponentWithSnippet>
);
};
@@ -1,55 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useForm, FormProvider } from "react-hook-form";
import { MultiOptionInput } from "@calcom/ui/components/form";
type FormValues = {
defaultOptions: Array<{ label: string; id: string }>;
disabledOptions: Array<{ label: string; id: string }>;
minOptions: Array<{ label: string; id: string }>;
};
export const StatesExample: React.FC = () => {
const methods = useForm<FormValues>();
return (
<RenderComponentWithSnippet>
<FormProvider {...methods}>
<div className="space-y-6">
<div className="space-y-4">
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Default State</h3>
<MultiOptionInput<FormValues>
fieldArrayName="defaultOptions"
optionPlaceholders={["First option", "Second option", "Third option"]}
defaultNumberOfOptions={3}
/>
</div>
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Disabled State</h3>
<MultiOptionInput<FormValues>
fieldArrayName="disabledOptions"
optionPlaceholders={["Disabled option 1", "Disabled option 2"]}
defaultNumberOfOptions={2}
disabled
/>
</div>
<div className="flex flex-col space-y-2">
<h3 className="text-emphasis text-sm">Minimum Options (2)</h3>
<MultiOptionInput<FormValues>
fieldArrayName="minOptions"
optionPlaceholders={["Required option 1", "Required option 2", "Optional option"]}
defaultNumberOfOptions={3}
minOptions={2}
/>
</div>
</div>
</div>
</FormProvider>
</RenderComponentWithSnippet>
);
};
@@ -1,92 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Badge } from "@calcom/ui/components/badge";
import { NavigationItem } from "@calcom/ui/components/navigation";
export const ItemsExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-4 md:w-64">
<div className="space-y-1">
<h3 className="text-emphasis mb-2 text-sm font-medium">Basic Items</h3>
<NavigationItem
item={{
name: "Event Types",
href: "#event-types",
icon: "link",
}}
/>
<NavigationItem
item={{
name: "Availability",
href: "#availability",
icon: "clock",
}}
/>
</div>
<div className="space-y-1">
<h3 className="text-emphasis mb-2 text-sm font-medium">With Badge</h3>
<NavigationItem
item={{
name: "Bookings",
href: "#bookings",
icon: "calendar",
badge: <Badge variant="blue">3</Badge>,
}}
/>
<NavigationItem
item={{
name: "Teams",
href: "#teams",
icon: "users",
badge: <Badge variant="gray">New</Badge>,
}}
/>
</div>
<div className="space-y-1">
<h3 className="text-emphasis mb-2 text-sm font-medium">Active State</h3>
<NavigationItem
item={{
name: "Event Types",
href: "#event-types",
icon: "link",
isCurrent: true,
}}
/>
<NavigationItem
item={{
name: "Availability",
href: "#availability",
icon: "clock",
badge: <Badge variant="blue">3</Badge>,
isCurrent: true,
}}
/>
<NavigationItem
item={{
name: "Apps",
href: "#apps",
icon: "grid-3x3",
isCurrent: true,
isExpanded: true,
child: [
{
name: "App Store",
href: "#apps/store",
isCurrent: true,
},
{
name: "Installed Apps",
href: "#apps/installed",
},
],
}}
/>
</div>
</div>
</RenderComponentWithSnippet>
);
@@ -1,20 +0,0 @@
---
title: Navigation
airtableId: recwHio1ktKQOdXGL
---
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
// import { SidebarExample } from "./navigation.sidebar.tsx"
import { ItemsExample } from "./navigation.items.tsx"
{/* ## Sidebar Navigation
Full sidebar navigation with logo, sections, and footer.
<SidebarExample/> */}
## Navigation Items
Individual navigation items with different states and variants.
<ItemsExample/>
@@ -1,204 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { NavigationItem } from "@calcom/ui";
export const SidebarExample: React.FC = () => {
const [isDeelExpanded, setIsDeelExpanded] = useState(true);
return (
<RenderComponentWithSnippet>
<div className="bg-muted border-muted flex w-60 flex-col border p-3">
<NavigationItem
item={{
name: "Event types",
href: "#event-types",
icon: "link",
}}
/>
<NavigationItem
item={{
name: "Bookings",
href: "#bookings",
icon: "users",
isCurrent: true,
isExpanded: true,
child: [
{ name: "Label", href: "#label-1" },
{ name: "Label", href: "#label-2" },
{ name: "Label", href: "#label-3" },
{ name: "Label", href: "#label-4" },
],
}}
/>
<NavigationItem
item={{
name: "Insights",
href: "#insights",
icon: "chart-line",
}}
/>
<NavigationItem
item={{
name: "Routing forms",
href: "#routing-forms",
icon: "shuffle",
}}
/>
<NavigationItem
item={{
name: "Workflows",
href: "#workflows",
icon: "zap",
}}
/>
<div className="text-subtle mt-4 px-2 text-sm">Manage</div>
<NavigationItem
item={{
name: "Members",
href: "#members",
icon: "users",
}}
/>
<NavigationItem
item={{
name: "Apps",
href: "#apps",
icon: "grid-3x3",
}}
/>
<NavigationItem
item={{
name: "Availability",
href: "#availability",
icon: "clock",
}}
/>
<div className="mt-auto" />
<NavigationItem
item={{
name: "Help",
href: "#help",
icon: "circle-help",
}}
/>
<NavigationItem
item={{
name: "Settings",
href: "#settings",
icon: "settings",
}}
/>
</div>
<div className="bg-muted border-muted flex w-60 flex-col border p-3">
<NavigationItem
item={{
name: "Back",
href: "#back",
icon: "chevron-left",
}}
/>
<NavigationItem
item={{
name: "Appearance",
href: "#appearance",
}}
/>
<NavigationItem
item={{
name: "Out of office",
href: "#out-of-office",
}}
/>
<NavigationItem
item={{
name: "Security",
icon: "shield",
isExpanded: true,
child: [
{
name: "Password",
href: "#password",
},
{
name: "Impersonation",
href: "#impersonation",
},
{
name: "Two factor auth",
href: "#2fa",
},
],
}}
/>
<NavigationItem
item={{
name: "Billing",
icon: "credit-card",
isExpanded: true,
child: [
{
name: "Manage billing",
href: "#manage-billing",
},
],
}}
/>
<NavigationItem
item={{
name: "Developer",
icon: "code",
isExpanded: true,
child: [
{
name: "Webhooks",
href: "#webhooks",
},
{
name: "API Keys",
href: "#api-keys",
},
],
}}
/>
<NavigationItem
item={{
name: "Teams",
icon: "users",
}}
/>
<NavigationItem
item={{
name: "Deel",
icon: isDeelExpanded ? "chevron-down" : "chevron-right",
isExpanded: isDeelExpanded,
onToggle: () => setIsDeelExpanded(!isDeelExpanded),
child: [
{ name: "Profile", href: "#profile" },
{ name: "Members", href: "#members" },
{ name: "Event Types", href: "#event-types" },
{ name: "Appearance", href: "#appearance" },
{ name: "Billing", href: "#billing" },
{ name: "Booking Limits", href: "#booking-limits" },
{ name: "Attributes", href: "#attributes" },
{ name: "Segments", href: "#segments", isCurrent: true },
],
}}
/>
<NavigationItem
item={{
name: "Add team",
href: "#add-team",
icon: "plus",
}}
/>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,42 +0,0 @@
---
title: Pagination
---
import { BasicExample, CustomPageSizesExample, LargeDatasetExample, WithCallbacksExample } from "./pagination";
# Pagination
The Pagination component provides a consistent way to handle data pagination across the application. It includes rows per page selection and navigation controls.
## Installation
```ts
import { Pagination } from "@calcom/ui";
```
## Examples
### Basic Usage
A basic pagination component with default settings.
<BasicExample />
### Custom Page Sizes
Customize the available page size options.
<CustomPageSizesExample />
### With Navigation Callbacks
Use `onNext` and `onPrevious` callbacks to perform additional actions when navigating pages.
<WithCallbacksExample />
### Large Dataset
The pagination component handles large datasets efficiently.
<LargeDatasetExample />
@@ -1,97 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { Pagination } from "@calcom/ui/components/pagination";
export const BasicExample = () => {
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(25);
const totalItems = 61;
return (
<RenderComponentWithSnippet>
<div className="w-full">
<Pagination
currentPage={currentPage}
pageSize={pageSize}
totalItems={totalItems}
onPageChange={setCurrentPage}
onPageSizeChange={setPageSize}
/>
</div>
</RenderComponentWithSnippet>
);
};
export const CustomPageSizesExample = () => {
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(5);
const totalItems = 25;
return (
<RenderComponentWithSnippet>
<div className="w-full">
<Pagination
currentPage={currentPage}
pageSize={pageSize}
totalItems={totalItems}
onPageChange={setCurrentPage}
onPageSizeChange={setPageSize}
pageSizeOptions={[5, 10, 15, 25]}
/>
</div>
</RenderComponentWithSnippet>
);
};
export const WithCallbacksExample = () => {
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(25);
const totalItems = 100;
const handleNext = () => {
console.log("Next page clicked");
};
const handlePrevious = () => {
console.log("Previous page clicked");
};
return (
<RenderComponentWithSnippet>
<div className="w-full">
<Pagination
currentPage={currentPage}
pageSize={pageSize}
totalItems={totalItems}
onPageChange={setCurrentPage}
onPageSizeChange={setPageSize}
onNext={handleNext}
onPrevious={handlePrevious}
/>
</div>
</RenderComponentWithSnippet>
);
};
export const LargeDatasetExample = () => {
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(25);
const totalItems = 1000;
return (
<RenderComponentWithSnippet>
<div className="w-full">
<Pagination
currentPage={currentPage}
pageSize={pageSize}
totalItems={totalItems}
onPageChange={setCurrentPage}
onPageSizeChange={setPageSize}
/>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,72 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { RangeSlider } from "@calcom/ui/components/form";
export const BasicExample: React.FC = () => {
const [singleValue, setSingleValue] = useState([50]);
const [rangeValue, setRangeValue] = useState([20, 80]);
const [steppedValue, setSteppedValue] = useState([25]);
return (
<RenderComponentWithSnippet>
<div className="space-y-6 md:w-80">
<div className="w-full max-w-[300px] space-y-6">
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-default text-sm">Single Value: {singleValue[0]}%</span>
</div>
<RangeSlider
value={singleValue}
onValueChange={setSingleValue}
max={100}
step={1}
aria-label="Single value"
/>
</div>
</div>
<div className="w-full max-w-[300px] space-y-6">
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-default text-sm">
Range: {rangeValue[0]}% - {rangeValue[1]}%
</span>
</div>
<RangeSlider
value={rangeValue}
onValueChange={setRangeValue}
max={100}
step={1}
aria-label="Range"
/>
</div>
</div>
<div className="w-full max-w-[300px] space-y-6">
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-default text-sm">Step Size 25: {steppedValue[0]}%</span>
</div>
<RangeSlider
value={steppedValue}
onValueChange={setSteppedValue}
max={100}
step={25}
aria-label="Stepped"
/>
</div>
</div>
<div className="w-full max-w-[300px] space-y-6">
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-default text-sm">Disabled: 50%</span>
</div>
<RangeSlider value={[50]} max={100} step={1} disabled aria-label="Disabled" />
</div>
</div>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,22 +0,0 @@
---
title: Range Slider
airtableId: recPe8JPnGCVmjROA
---
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Row } from "@/app/components/row";
import { BasicExample } from "./rangeslider.basic.tsx";
import { PopoverExample } from "./rangeslider.popover";
## Basic
Basic range slider for selecting numeric values.
<BasicExample />
## With Popover
Range slider with a popover showing the current value.
<PopoverExample />
@@ -1,71 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { RangeSliderPopover } from "@calcom/ui/components/form";
export const PopoverExample: React.FC = () => {
const [defaultRange, setDefaultRange] = useState([15, 30]);
const [customRange, setCustomRange] = useState([5, 20]);
const [largeRange, setLargeRange] = useState([0, 100]);
return (
<RenderComponentWithSnippet>
<div className="space-y-6 md:w-80">
<div className="space-y-2">
<h3 className="text-emphasis text-sm font-medium">Default Range (15-30 mins)</h3>
<RangeSliderPopover
triggerText="Total time"
value={defaultRange}
onChange={setDefaultRange}
min={0}
max={48}
badgeVariant="default"
inputSuffix="mins"
/>
</div>
<div className="space-y-2">
<h3 className="text-emphasis text-sm font-medium">Custom Range with Success Badge (5-20 mins)</h3>
<RangeSliderPopover
triggerText="Meeting duration"
value={customRange}
onChange={setCustomRange}
min={5}
max={20}
badgeVariant="success"
/>
</div>
<div className="space-y-2">
<h3 className="text-emphasis text-sm font-medium">Large Range with Warning Badge (0-100 mins)</h3>
<RangeSliderPopover
triggerText="Extended duration"
value={largeRange}
onChange={setLargeRange}
min={0}
max={100}
step={5}
badgeVariant="warning"
/>
</div>
<div className="mt-4">
<h4 className="text-default text-sm font-medium">Selected Values:</h4>
<pre className="text-emphasis bg-subtle mt-2 rounded-md p-4 text-sm">
{JSON.stringify(
{
defaultRange,
customRange,
largeRange,
},
null,
2
)}
</pre>
</div>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,58 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { Switch } from "@calcom/ui/components/form";
import { Section } from "@calcom/ui/components/section";
export const BasicExample = () => {
const [isOpen, setIsOpen] = useState(true);
return (
<RenderComponentWithSnippet>
<Section>
<Section.Header
title="Basic Section"
description="This is a basic section with a title and description">
<Switch size="sm" checked={isOpen} onCheckedChange={setIsOpen} />
</Section.Header>
{isOpen && (
<Section.Content>
<p>This is the main content of the section.</p>
</Section.Content>
)}
</Section>
</RenderComponentWithSnippet>
);
};
export const BasicExampleSnippet = `
"use client";
import { useState } from "react";
import { Section } from "@calcom/ui/components/section";
import { Switch } from "@calcom/ui/components/form";
const BasicSection = () => {
const [isOpen, setIsOpen] = useState(true);
return (
<Section>
<Section.Header
title="Basic Section"
description="This is a basic section with a title and description"
>
<Section.HeaderRight>
<Switch size="sm" checked={isOpen} onCheckedChange={setIsOpen} />
</Section.HeaderRight>
</Section.Header>
{isOpen && (
<Section.Content>
<p>This is the main content of the section.</p>
</Section.Content>
)}
</Section>
);
};
`;
@@ -1,40 +0,0 @@
---
title: Section
---
import { Section } from "@calcom/ui/components/section"
import { RenderComponentWithSnippet } from "@/app/components/render"
import { Row } from "@/app/components/row"
import { BasicExample } from "./section.basic"
import { WithIconExample } from "./section.withIcon"
import { SubSectionExample } from "./section.subsection"
## Basic Usage
The Section component provides a structured way to organize content with a consistent visual hierarchy. It includes several subcomponents for different parts of the section.
<BasicExample/>
## With Icon
Sections can include an icon in the header to provide visual context.
<WithIconExample/>
## SubSections
Sections can contain subsections for organizing related content in a nested structure.
<SubSectionExample/>
## Components
The Section component is composed of several subcomponents:
- `Section` - The root container
- `Section.Header` - Container for the section header
- `Section.Title` - The section title
- `Section.Description` - Optional description text
- `Section.Content` - The main content area
- `Section.SubSection` - A nested section
- `Section.SubSectionHeader` - Header for a subsection
- `Section.SubSectionContent` - Content area for a subsection
@@ -1,151 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { Input, Switch } from "@calcom/ui/components/form";
import { Section } from "@calcom/ui/components/section";
export const SubSectionExample = () => {
const [isMainOpen, setIsMainOpen] = useState(true);
const [isFirstSubsectionOpen, setIsFirstSubsectionOpen] = useState(true);
const [isSecondSubsectionOpen, setIsSecondSubsectionOpen] = useState(true);
return (
<RenderComponentWithSnippet>
<Section>
<Section.Header
icon="calendar"
title="Section with Subsections"
description="This section contains nested subsections">
<Switch size="sm" checked={isMainOpen} onCheckedChange={setIsMainOpen} />
</Section.Header>
{isMainOpen && (
<Section.Content>
<Section.SubSection>
<Section.SubSectionHeader icon="zap" title="Automatic Sync (Justify Start)" justify="start">
<Switch size="sm" />
</Section.SubSectionHeader>
</Section.SubSection>
<Section.SubSection>
<Section.SubSectionHeader icon="user" title="Create new records for guests added">
<Switch
size="sm"
checked={isFirstSubsectionOpen}
onCheckedChange={setIsFirstSubsectionOpen}
/>
</Section.SubSectionHeader>
{isFirstSubsectionOpen && (
<Section.SubSectionContent>
<p>This is the content of the first subsection.</p>
</Section.SubSectionContent>
)}
</Section.SubSection>
<Section.SubSection>
<Section.SubSectionHeader icon="settings" title="Open Settings">
<Switch
size="sm"
checked={isSecondSubsectionOpen}
onCheckedChange={setIsSecondSubsectionOpen}
/>
</Section.SubSectionHeader>
{isSecondSubsectionOpen && (
<Section.SubSectionContent>
<div className="flex gap-3 px-3 py-1.5">
<div className="text-subtle w-full text-sm font-medium">Field Name</div>
<div className="text-subtle w-full text-sm font-medium">Field Type</div>
<div className="text-subtle w-full text-sm font-medium">Value</div>
<div className="text-subtle w-full text-sm font-medium">When to Write</div>
</div>
<Section.SubSectionNested>
<div className="flex gap-3">
<Input value="Customer Name" className="w-full" />
<Input value="Text" className="w-full" />
<Input value="John Doe" className="w-full" />
<Input value="On Create" className="w-full" />
</div>
</Section.SubSectionNested>
</Section.SubSectionContent>
)}
</Section.SubSection>
</Section.Content>
)}
</Section>
</RenderComponentWithSnippet>
);
};
export const SubSectionExampleSnippet = `
"use client";
import { useState } from "react";
import { Section } from "@calcom/ui/components/section";
import { Switch } from "@calcom/ui/components/form";
const SectionWithSubsections = () => {
const [isMainOpen, setIsMainOpen] = useState(true);
const [isFirstSubsectionOpen, setIsFirstSubsectionOpen] = useState(true);
const [isSecondSubsectionOpen, setIsSecondSubsectionOpen] = useState(true);
return (
<Section>
<Section.Header
icon="calendar"
title="Section with Subsections"
description="This section contains nested subsections">
<Switch size="sm" checked={isMainOpen} onCheckedChange={setIsMainOpen} />
</Section.Header>
{isMainOpen && (
<Section.Content>
<Section.SubSection>
<Section.SubSectionHeader icon="zap" title="Automatic Sync (Justify Start)" justify="start">
<Switch size="sm" />
</Section.SubSectionHeader>
</Section.SubSection>
<Section.SubSection>
<Section.SubSectionHeader icon="user" title="Create new records for guests added">
<Switch
size="sm"
checked={isFirstSubsectionOpen}
onCheckedChange={setIsFirstSubsectionOpen}
/>
</Section.SubSectionHeader>
{isFirstSubsectionOpen && (
<Section.SubSectionContent>
<p>This is the content of the first subsection.</p>
</Section.SubSectionContent>
)}
</Section.SubSection>
<Section.SubSection>
<Section.SubSectionHeader icon="settings" title="Open Settings">
<Switch
size="sm"
checked={isSecondSubsectionOpen}
onCheckedChange={setIsSecondSubsectionOpen}
/>
</Section.SubSectionHeader>
{isSecondSubsectionOpen && (
<Section.SubSectionContent>
<div className="flex gap-3 px-3 py-1.5">
<div className="text-subtle w-full text-sm font-medium">Field Name</div>
<div className="text-subtle w-full text-sm font-medium">Field Type</div>
<div className="text-subtle w-full text-sm font-medium">Value</div>
<div className="text-subtle w-full text-sm font-medium">When to Write</div>
</div>
<Section.SubSectionNested>
<div className="flex gap-3">
<Input value="Customer Name" className="w-full" />
<Input value="Text" className="w-full" />
<Input value="John Doe" className="w-full" />
<Input value="On Create" className="w-full" />
</div>
</Section.SubSectionNested>
</Section.SubSectionContent>
)}
</Section.SubSection>
</Section.Content>
)}
</Section>
);
};
`;
@@ -1,63 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { Switch } from "@calcom/ui/components/form";
import { Section } from "@calcom/ui/components/section";
export const WithIconExample = () => {
const [isOpen, setIsOpen] = useState(true);
return (
<RenderComponentWithSnippet>
<Section>
<Section.Header
icon="calendar"
title="Section with Icon"
description="This section includes an icon in the header. Toggle to see auto-animation.">
<Switch size="sm" checked={isOpen} onCheckedChange={setIsOpen} />
</Section.Header>
{isOpen && (
<Section.Content>
<p>
This section demonstrates how to include an icon in the header and uses FormKit auto-animate for
smooth transitions when toggling content visibility.
</p>
</Section.Content>
)}
</Section>
</RenderComponentWithSnippet>
);
};
export const WithIconExampleSnippet = `
"use client";
import { useState } from "react";
import { Section } from "@calcom/ui/components/section";
import { Switch } from "@calcom/ui/components/form";
const SectionWithIcon = () => {
const [isOpen, setIsOpen] = useState(true);
return (
<Section>
<Section.Header
icon="calendar"
title="Section with Icon"
description="This section includes an icon in the header. Toggle to see auto-animation.">
<Switch size="sm" checked={isOpen} onCheckedChange={setIsOpen} />
</Section.Header>
{isOpen && (
<Section.Content>
<p>
This section demonstrates how to include an icon in the header and uses FormKit auto-animate for
smooth transitions when toggling content visibility.
</p>
</Section.Content>
)}
</Section>
);
};
`;
@@ -1,89 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useCallback, useState } from "react";
import { Select } from "@calcom/ui/components/form";
import { showToast } from "@calcom/ui/components/toast";
const countries = [
{ value: "us", label: "United States" },
{ value: "uk", label: "United Kingdom" },
{ value: "ca", label: "Canada" },
{ value: "fr", label: "France" },
{ value: "de", label: "Germany" },
{ value: "it", label: "Italy" },
{ value: "es", label: "Spain" },
{ value: "au", label: "Australia" },
{ value: "jp", label: "Japan" },
{ value: "br", label: "Brazil" },
];
// Simulate an API call with delay
const searchCountries = (query: string) => {
return new Promise<{ value: string; label: string }[]>((resolve) => {
setTimeout(() => {
const filtered = countries.filter((country) =>
country.label.toLowerCase().includes(query.toLowerCase())
);
resolve(filtered);
}, 1000); // Simulate network delay
});
};
export const AsyncExample: React.FC = () => {
const [asyncOptions, setAsyncOptions] = useState<{ value: string; label: string }[]>([]);
const [isLoading, setIsLoading] = useState(false);
const handleValueChange = (newValue: unknown, actionMeta: { action: string }) => {
showToast(`Selected: ${JSON.stringify(newValue)}, Action: ${actionMeta.action}`, "success");
};
const loadOptions = useCallback(async (inputValue: string) => {
setIsLoading(true);
try {
const results = await searchCountries(inputValue);
setAsyncOptions(results);
} catch (error) {
showToast("Error loading options", "error");
} finally {
setIsLoading(false);
}
}, []);
return (
<RenderComponentWithSnippet>
<div className="space-y-4 md:w-80">
<Select
options={asyncOptions}
onInputChange={(value) => {
if (value) {
loadOptions(value);
}
}}
isLoading={isLoading}
placeholder="Search for a country..."
noOptionsMessage={({ inputValue }) =>
inputValue ? "No countries found" : "Start typing to search..."
}
onChange={handleValueChange}
/>
<Select
options={asyncOptions}
onInputChange={(value) => {
if (value) {
loadOptions(value);
}
}}
isLoading={isLoading}
placeholder="Search for a country... (small)"
noOptionsMessage={({ inputValue }) =>
inputValue ? "No countries found" : "Start typing to search..."
}
onChange={handleValueChange}
size="sm"
/>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,41 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { useState } from "react";
import { Select } from "@calcom/ui/components/form";
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
{ value: "mint", label: "Mint" },
{ value: "coffee", label: "Coffee" },
];
export const BasicExample: React.FC = () => {
const [singleValue, setSingleValue] = useState<{ value: string; label: string } | null>(null);
return (
<RenderComponentWithSnippet>
<div className="space-y-4 md:w-80">
<Select
options={options}
value={singleValue}
onChange={(newValue) => setSingleValue(newValue)}
isClearable
placeholder="Choose a flavor..."
size="md"
/>
<Select
options={options}
value={singleValue}
onChange={(newValue) => setSingleValue(newValue)}
isClearable
placeholder="Small size select..."
size="sm"
/>
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,27 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Select, SelectField } from "@calcom/ui/components/form";
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
{ value: "mint", label: "Mint" },
{ value: "coffee", label: "Coffee" },
];
export const DisabledExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-4 md:w-80">
<Select options={options} isDisabled placeholder="This select is disabled..." />
<SelectField
label="Disabled Field"
options={options}
isDisabled
placeholder="This field is disabled..."
/>
</div>
</RenderComponentWithSnippet>
);
@@ -1,63 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { SelectField } from "@calcom/ui/components/form";
import { showToast } from "@calcom/ui/components/toast";
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
{ value: "mint", label: "Mint" },
{ value: "coffee", label: "Coffee" },
];
export const FieldExample: React.FC = () => {
const handleValueChange = (newValue: unknown, actionMeta: { action: string }) => {
showToast(`Selected: ${JSON.stringify(newValue)}, Action: ${actionMeta.action}`, "success");
};
return (
<RenderComponentWithSnippet>
<div className="space-y-4 md:w-80">
<SelectField
label="Flavor"
options={options}
onChange={handleValueChange}
placeholder="Choose a flavor..."
/>
<SelectField
label="Required Field"
options={options}
required
onChange={handleValueChange}
placeholder="This field is required..."
/>
<SelectField label="With Error" options={options} />
</div>
<div className="mt-4 space-y-4 md:w-80">
<SelectField
label="Flavor"
options={options}
onChange={handleValueChange}
placeholder="Choose a flavor... (small)"
size="sm"
/>
<SelectField
label="Required Field"
options={options}
required
onChange={handleValueChange}
placeholder="This field is required... (small)"
size="sm"
/>
<SelectField label="With Error (small)" options={options} size="sm" />
</div>
</RenderComponentWithSnippet>
);
};
@@ -1,32 +0,0 @@
"use client";
import { RenderComponentWithSnippet } from "@/app/components/render";
import { Select } from "@calcom/ui/components/form";
const groupedOptions = [
{
label: "Fruits",
options: [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "orange", label: "Orange" },
],
},
{
label: "Vegetables",
options: [
{ value: "carrot", label: "Carrot" },
{ value: "broccoli", label: "Broccoli" },
{ value: "spinach", label: "Spinach" },
],
},
];
export const GroupedExample: React.FC = () => (
<RenderComponentWithSnippet>
<div className="space-y-4 md:w-80">
<Select options={groupedOptions} placeholder="Choose food..." />
</div>
</RenderComponentWithSnippet>
);

Some files were not shown because too many files have changed in this diff Show More