From 38724ef12799d351147c41a6a0322d381f292f02 Mon Sep 17 00:00:00 2001 From: Karol Chudzik Date: Thu, 31 Oct 2024 23:29:35 +0100 Subject: [PATCH] Docs fixes and added examples --- apps/www/__registry__/index.tsx | 66 + .../content/docs/components/chat-message.mdx | 2 +- .../content/docs/components/copy-button.mdx | 2 +- .../docs/components/markdown-renderer.mdx | 4 +- .../content/docs/components/message-input.mdx | 2 +- .../content/docs/components/message-list.mdx | 2 +- .../docs/components/prompt-suggestions.mdx | 2 +- apps/www/content/docs/components/sidebar.mdx | 1313 ----------------- .../docs/components/typing-indicator.mdx | 2 +- apps/www/content/docs/index.mdx | 2 +- .../default/example/chat-message-demo.tsx | 11 +- .../default/example/copy-button-demo.tsx | 10 + .../example/markdown-renderer-demo.tsx | 29 + .../default/example/message-list-demo.tsx | 40 + .../example/prompt-suggestions-demo.tsx | 23 + .../default/example/typing-indicator-demo.tsx | 5 + apps/www/registry/index.ts | 25 + 17 files changed, 217 insertions(+), 1323 deletions(-) delete mode 100644 apps/www/content/docs/components/sidebar.mdx create mode 100644 apps/www/registry/default/example/copy-button-demo.tsx create mode 100644 apps/www/registry/default/example/markdown-renderer-demo.tsx create mode 100644 apps/www/registry/default/example/message-list-demo.tsx create mode 100644 apps/www/registry/default/example/prompt-suggestions-demo.tsx create mode 100644 apps/www/registry/default/example/typing-indicator-demo.tsx diff --git a/apps/www/__registry__/index.tsx b/apps/www/__registry__/index.tsx index 678703b..cb9e9a0 100644 --- a/apps/www/__registry__/index.tsx +++ b/apps/www/__registry__/index.tsx @@ -148,4 +148,70 @@ export const Index: Record = { subcategory: "undefined", chunks: [] }, + "chat-message-demo": { + name: "chat-message-demo", + type: "registry:example", + registryDependencies: undefined, + files: ["registry/default/example/chat-message-demo.tsx"], + component: React.lazy(() => import("@/registry/default/example/chat-message-demo.tsx")), + source: "", + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "message-list-demo": { + name: "message-list-demo", + type: "registry:example", + registryDependencies: undefined, + files: ["registry/default/example/message-list-demo.tsx"], + component: React.lazy(() => import("@/registry/default/example/message-list-demo.tsx")), + source: "", + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typing-indicator-demo": { + name: "typing-indicator-demo", + type: "registry:example", + registryDependencies: undefined, + files: ["registry/default/example/typing-indicator-demo.tsx"], + component: React.lazy(() => import("@/registry/default/example/typing-indicator-demo.tsx")), + source: "", + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "markdown-renderer-demo": { + name: "markdown-renderer-demo", + type: "registry:example", + registryDependencies: undefined, + files: ["registry/default/example/markdown-renderer-demo.tsx"], + component: React.lazy(() => import("@/registry/default/example/markdown-renderer-demo.tsx")), + source: "", + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "prompt-suggestions-demo": { + name: "prompt-suggestions-demo", + type: "registry:example", + registryDependencies: undefined, + files: ["registry/default/example/prompt-suggestions-demo.tsx"], + component: React.lazy(() => import("@/registry/default/example/prompt-suggestions-demo.tsx")), + source: "", + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "copy-button-demo": { + name: "copy-button-demo", + type: "registry:example", + registryDependencies: undefined, + files: ["registry/default/example/copy-button-demo.tsx"], + component: React.lazy(() => import("@/registry/default/example/copy-button-demo.tsx")), + source: "", + category: "undefined", + subcategory: "undefined", + chunks: [] + }, } diff --git a/apps/www/content/docs/components/chat-message.mdx b/apps/www/content/docs/components/chat-message.mdx index 759c7c1..7d6792d 100644 --- a/apps/www/content/docs/components/chat-message.mdx +++ b/apps/www/content/docs/components/chat-message.mdx @@ -1,5 +1,5 @@ --- -title: ChatMessage +title: Chat Message description: A customizable message bubble component for chat interfaces with support for user and AI messages. component: true --- diff --git a/apps/www/content/docs/components/copy-button.mdx b/apps/www/content/docs/components/copy-button.mdx index 03afc03..2344936 100644 --- a/apps/www/content/docs/components/copy-button.mdx +++ b/apps/www/content/docs/components/copy-button.mdx @@ -1,5 +1,5 @@ --- -title: CopyButton +title: Copy Button description: A button component that copies content to clipboard with visual feedback. component: true --- diff --git a/apps/www/content/docs/components/markdown-renderer.mdx b/apps/www/content/docs/components/markdown-renderer.mdx index 71fd927..0679a8e 100644 --- a/apps/www/content/docs/components/markdown-renderer.mdx +++ b/apps/www/content/docs/components/markdown-renderer.mdx @@ -1,11 +1,11 @@ --- -title: MarkdownRenderer +title: Markdown Renderer description: A customizable component for rendering Markdown content with consistent styling. component: true --- diff --git a/apps/www/content/docs/components/message-input.mdx b/apps/www/content/docs/components/message-input.mdx index bc775e7..72e738f 100644 --- a/apps/www/content/docs/components/message-input.mdx +++ b/apps/www/content/docs/components/message-input.mdx @@ -1,5 +1,5 @@ --- -title: MessageInput +title: Message Input description: A textarea component with file attachment support, auto-resizing, and drag-and-drop capabilities. component: true --- diff --git a/apps/www/content/docs/components/message-list.mdx b/apps/www/content/docs/components/message-list.mdx index 22084e2..ed3f528 100644 --- a/apps/www/content/docs/components/message-list.mdx +++ b/apps/www/content/docs/components/message-list.mdx @@ -1,5 +1,5 @@ --- -title: MessageList +title: Message List description: A component for rendering a list of chat messages with a typing indicator. component: true --- diff --git a/apps/www/content/docs/components/prompt-suggestions.mdx b/apps/www/content/docs/components/prompt-suggestions.mdx index ea700a5..f82d3a0 100644 --- a/apps/www/content/docs/components/prompt-suggestions.mdx +++ b/apps/www/content/docs/components/prompt-suggestions.mdx @@ -1,5 +1,5 @@ --- -title: PromptSuggestions +title: Prompt Suggestions description: A component that displays clickable prompt suggestions for empty chat states. component: true --- diff --git a/apps/www/content/docs/components/sidebar.mdx b/apps/www/content/docs/components/sidebar.mdx deleted file mode 100644 index 1112f70..0000000 --- a/apps/www/content/docs/components/sidebar.mdx +++ /dev/null @@ -1,1313 +0,0 @@ ---- -title: Sidebar -description: A composable, themeable and customizable sidebar component. -component: true ---- - -
- -
- A sidebar that collapses to icons. -
-
- -Sidebars are one of the most complex components to build. They are central -to any application and often contain a lot of moving parts. - -I don't like building sidebars. So I built 30+ of them. All kinds of -configurations. Then I extracted the core components into `sidebar.tsx`. - -We now have a solid foundation to build on top of. Composable. Themeable. -Customizable. - -[Browse the Blocks Library](/blocks). - -## Installation - - - - - CLI - Manual - - - - - -Run the following command to install `sidebar.tsx` - -```bash -npx shadcn@latest add sidebar -``` - -Add the following colors to your CSS file - -The command above should install the colors for you. If not, copy and paste the following in your CSS file. - -We'll go over the colors later in the [theming section](/docs/components/sidebar#theming). - -```css title="app/globals.css" -@layer base { - :root { - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; - --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } - - .dark { - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; - } -} -``` - - - - - - - - - -Copy and paste the following code into your project. - - - -Update the import paths to match your project setup. - -Add the following colors to your CSS file - -We'll go over the colors later in the [theming section](/docs/components/sidebar#theming). - -```css title="app/globals.css" -@layer base { - :root { - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; - --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } - - .dark { - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; - } -} -``` - - - - - - - -## Structure - -A `Sidebar` component is composed of the following parts: - -- `SidebarProvider` - Handles collapsible state. -- `Sidebar` - The sidebar container. -- `SidebarHeader` and `SidebarFooter` - Sticky at the top and bottom of the sidebar. -- `SidebarContent` - Scrollable content. -- `SidebarGroup` - Section within the `SidebarContent`. -- `SidebarTrigger` - Trigger for the `Sidebar`. - -Sidebar Structure -Sidebar Structure - -## Usage - -```tsx showLineNumbers title="app/layout.tsx" -import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar" -import { AppSidebar } from "@/components/app-sidebar" - -export default function Layout({ children }: { children: React.ReactNode }) { - return ( - - -
- - {children} -
-
- ) -} -``` - -```tsx showLineNumbers title="components/app-sidebar.tsx" -import { - Sidebar, - SidebarContent, - SidebarFooter, - SidebarGroup, - SidebarHeader, -} from "@/components/ui/sidebar" - -export function AppSidebar() { - return ( - - - - - - - - - ) -} -``` - -## Your First Sidebar - -Let's start with the most basic sidebar. A collapsible sidebar with a menu. - - - - - Add a `SidebarProvider` and `SidebarTrigger` at the root of your application. - - -```tsx showLineNumbers title="app/layout.tsx" -import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar" -import { AppSidebar } from "@/components/app-sidebar" - -export default function Layout({ children }: { children: React.ReactNode }) { - return ( - - -
- - {children} -
-
- ) -} -``` - -Create a new sidebar component at `components/app-sidebar.tsx`. - -```tsx showLineNumbers title="components/app-sidebar.tsx" -import { Sidebar, SidebarContent } from "@/components/ui/sidebar" - -export function AppSidebar() { - return ( - - - - ) -} -``` - -Now, let's add a `SidebarMenu` to the sidebar. - -We'll use the `SidebarMenu` component in a `SidebarGroup`. - -```tsx showLineNumbers title="components/app-sidebar.tsx" -import { Calendar, Home, Inbox, Search, Settings } from "lucide-react" - -import { - Sidebar, - SidebarContent, - SidebarGroup, - SidebarGroupContent, - SidebarGroupLabel, - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, -} from "@/components/ui/sidebar" - -// Menu items. -const items = [ - { - title: "Home", - url: "#", - icon: Home, - }, - { - title: "Inbox", - url: "#", - icon: Inbox, - }, - { - title: "Calendar", - url: "#", - icon: Calendar, - }, - { - title: "Search", - url: "#", - icon: Search, - }, - { - title: "Settings", - url: "#", - icon: Settings, - }, -] - -export function AppSidebar() { - return ( - - - - Application - - - {items.map((item) => ( - - - - - {item.title} - - - - ))} - - - - - - ) -} -``` - -You've created your first sidebar. - -
- -
- Your first sidebar. -
-
- -
- -## Components - -The components in `sidebar.tsx` are built to be composable i.e you build your sidebar by putting the provided components together. They also compose well with other shadcn/ui components such as `DropdownMenu`, `Collapsible` or `Dialog` etc. - -**If you need to change the code in `sidebar.tsx`, you are encouraged to do so. The code is yours. Use `sidebar.tsx` as a starting point and build your own.** - -In the next sections, we'll go over each component and how to use them. - -## SidebarProvider - -The `SidebarProvider` component is used to provide the sidebar context to the `Sidebar` component. You should always wrap your application in a `SidebarProvider` component. - -### Props - -| Name | Type | Description | -| -------------- | ------------------------- | -------------------------------------------- | -| `defaultOpen` | `boolean` | Default open state of the sidebar. | -| `open` | `boolean` | Open state of the sidebar (controlled). | -| `onOpenChange` | `(open: boolean) => void` | Sets open state of the sidebar (controlled). | - -### Width - -If you have a single sidebar in your application, you can use the `SIDEBAR_WIDTH` and `SIDEBAR_WIDTH_MOBILE` variables in `sidebar.tsx` to set the width of the sidebar. - -```tsx showLineNumbers title="components/ui/sidebar.tsx" -const SIDEBAR_WIDTH = "16rem" -const SIDEBAR_WIDTH_MOBILE = "18rem" -``` - -For multiple sidebars in your application, you can use the `style` prop to set the width of the sidebar. - -To set the width of the sidebar, you can use the `--sidebar-width` and `--sidebar-width-mobile` CSS variables in the `style` prop. - -```tsx showLineNumbers title="components/ui/sidebar.tsx" - - - -``` - -This will handle the width of the sidebar but also the layout spacing. - -### Keyboard Shortcut - -The `SIDEBAR_KEYBOARD_SHORTCUT` variable is used to set the keyboard shortcut used to open and close the sidebar. - -To trigger the sidebar, you use the `cmd+b` keyboard shortcut on Mac and `ctrl+b` on Windows. - -You can change the keyboard shortcut by updating the `SIDEBAR_KEYBOARD_SHORTCUT` variable. - -```tsx showLineNumbers title="components/ui/sidebar.tsx" -const SIDEBAR_KEYBOARD_SHORTCUT = "b" -``` - -## Sidebar - -The main `Sidebar` component used to render a collapsible sidebar. - -```tsx showLineNumbers -import { Sidebar } from "@/components/ui/sidebar" - -export function AppSidebar() { - return -} -``` - -### Props - -| Property | Type | Description | -| ------------- | --------------------------------- | --------------------------------- | -| `side` | `left` or `right` | The side of the sidebar. | -| `variant` | `sidebar`, `floating`, or `inset` | The variant of the sidebar. | -| `collapsible` | `offcanvas`, `icon`, or `none` | Collapsible state of the sidebar. | - -### side - -Use the `side` prop to change the side of the sidebar. - -Available options are `left` and `right`. - -```tsx showLineNumbers -import { Sidebar } from "@/components/ui/sidebar" - -export function AppSidebar() { - return -} -``` - -### variant - -Use the `variant` prop to change the variant of the sidebar. - -Available options are `sidebar`, `floating` and `inset`. - -```tsx showLineNumbers -import { Sidebar } from "@/components/ui/sidebar" - -export function AppSidebar() { - return -} -``` - - - **Note:** If you use the `inset` variant, remember to wrap your main content - in a `SidebarInset` component. - - -```tsx showLineNumbers - - - -
{children}
-
-
-``` - -### collapsible - -Use the `collapsible` prop to make the sidebar collapsible. - -Available options are `offcanvas`, `icon` and `none`. - -```tsx showLineNumbers -import { Sidebar } from "@/components/ui/sidebar" - -export function AppSidebar() { - return -} -``` - -| Prop | Description | -| ----------- | ------------------------------------------------------------ | -| `offcanvas` | A collapsible sidebar that slides in from the left or right. | -| `icon` | A sidebar that collapses to icons. | -| `none` | A non-collapsible sidebar. | - -## useSidebar - -The `useSidebar` hook is used to control the sidebar. - -```tsx showLineNumbers -import { useSidebar } from "@/components/ui/sidebar" - -export function AppSidebar() { - const { - state, - open, - setOpen, - openMobile, - setOpenMobile, - isMobile, - toggleSidebar, - } = useSidebar() -} -``` - -| Property | Type | Description | -| --------------- | ------------------------- | --------------------------------------------- | -| `state` | `expanded` or `collapsed` | The current state of the sidebar. | -| `open` | `boolean` | Whether the sidebar is open. | -| `setOpen` | `(open: boolean) => void` | Sets the open state of the sidebar. | -| `openMobile` | `boolean` | Whether the sidebar is open on mobile. | -| `setOpenMobile` | `(open: boolean) => void` | Sets the open state of the sidebar on mobile. | -| `isMobile` | `boolean` | Whether the sidebar is on mobile. | -| `toggleSidebar` | `() => void` | Toggles the sidebar. Desktop and mobile. | - -## SidebarHeader - -Use the `SidebarHeader` component to add a sticky header to the sidebar. - -The following example adds a `` to the `SidebarHeader`. - -
- -
- A sidebar header with a dropdown menu. -
-
- -```tsx showLineNumbers title="components/app-sidebar.tsx" - - - - - - - - Select Workspace - - - - - - Acme Inc - - - Acme Corp. - - - - - - - -``` - -## SidebarFooter - -Use the `SidebarFooter` component to add a sticky footer to the sidebar. - -The following example adds a `` to the `SidebarFooter`. - -
- -
- A sidebar footer with a dropdown menu. -
-
- -```tsx showLineNumbers title="components/app-sidebar.tsx" -export function AppSidebar() { - return ( - - - - - - - - - - - Username - - - - - - Account - - - Billing - - - Sign out - - - - - - - - - ) -} -``` - -## SidebarContent - -The `SidebarContent` component is used to wrap the content of the sidebar. This is where you add your `SidebarGroup` components. It is scrollable. - -```tsx showLineNumbers -import { Sidebar, SidebarContent } from "@/components/ui/sidebar" - -export function AppSidebar() { - return ( - - - - - - - ) -} -``` - -## SidebarGroup - -Use the `SidebarGroup` component to create a section within the sidebar. - -A `SidebarGroup` has a `SidebarGroupLabel`, a `SidebarGroupContent` and an optional `SidebarGroupAction`. - -
- -
- A sidebar group. -
-
- -```tsx showLineNumbers -import { Sidebar, SidebarContent, SidebarGroup } from "@/components/ui/sidebar" - -export function AppSidebar() { - return ( - - - - Application - - Add Project - - - - - - ) -} -``` - -## Collapsible SidebarGroup - -To make a `SidebarGroup` collapsible, wrap it in a `Collapsible`. - -
- -
- A collapsible sidebar group. -
-
- -```tsx showLineNumbers -export function AppSidebar() { - return ( - - - - - Help - - - - - - - - - ) -} -``` - - - **Note:** We wrap the `CollapsibleTrigger` in a `SidebarGroupLabel` to render - a button. - - -## SidebarGroupAction - -Use the `SidebarGroupAction` component to add an action button to the `SidebarGroup`. - -```tsx showLineNumbers {5-7} -export function AppSidebar() { - return ( - - Projects - - Add Project - - - - ) -} -``` - -
- -
- A sidebar group with an action button. -
-
- -## SidebarMenu - -The `SidebarMenu` component is used for building a menu within a `SidebarGroup`. - -A `SidebarMenu` component is composed of `SidebarMenuItem`, `SidebarMenuButton`, `` and `` components. - -Sidebar Menu -Sidebar Menu - -Here's an example of a `SidebarMenu` component rendering a list of projects. - -
- -
- A sidebar menu with a list of projects. -
-
- -```tsx showLineNumbers - - - - Projects - - - {projects.map((project) => ( - - - - - {project.name} - - - - ))} - - - - - -``` - -## SidebarMenuButton - -The `SidebarMenuButton` component is used to render a menu button within a `SidebarMenuItem`. - -### Link or Anchor - -By default, the `SidebarMenuButton` renders a button but you can use the `asChild` prop to render a different component such as a `Link` or an `a` tag. - -```tsx showLineNumbers - - Home - -``` - -### Icon and Label - -You can render an icon and a truncated label inside the button. Remember to wrap the label in a ``. - -```tsx showLineNumbers - - - - Home - - -``` - -### isActive - -Use the `isActive` prop to mark a menu item as active. - -```tsx showLineNumbers - - Home - -``` - -## SidebarMenuAction - -The `SidebarMenuAction` component is used to render a menu action within a `SidebarMenuItem`. - -This button works independently of the `SidebarMenuButton` i.e you can have the `` as a clickable link and the `` as a button. - -```tsx showLineNumbers - - - - - Home - - - - Add Project - - -``` - -### DropdownMenu - -Here's an example of a `SidebarMenuAction` component rendering a `DropdownMenu`. - -
- -
- A sidebar menu action with a dropdown menu. -
-
- -```tsx showLineNumbers - - - - - Home - - - - - - - - - - - Edit Project - - - Delete Project - - - - -``` - -## SidebarMenuSub - -The `SidebarMenuSub` component is used to render a submenu within a `SidebarMenu`. - -Use `` and `` to render a submenu item. - -
- -
- A sidebar menu with a submenu. -
-
- -```tsx showLineNumbers - - - - - - - - - - - -``` - -## Collapsible SidebarMenu - -To make a `SidebarMenu` component collapsible, wrap it and the `SidebarMenuSub` components in a `Collapsible`. - -
- -
- A collapsible sidebar menu. -
-
- -```tsx showLineNumbers - - - - - - - - - - - - - - -``` - -## SidebarMenuBadge - -The `SidebarMenuBadge` component is used to render a badge within a `SidebarMenuItem`. - -
- -
- A sidebar menu with a badge. -
-
- -```tsx showLineNumbers - - - 24 - -``` - -## SidebarMenuSkeleton - -The `SidebarMenuSkeleton` component is used to render a skeleton for a `SidebarMenu`. You can use this to show a loading state when using React Server Components, SWR or react-query. - -```tsx showLineNumbers -function NavProjectsSkeleton() { - return ( - - {Array.from({ length: 5 }).map((_, index) => ( - - - - ))} - - ) -} -``` - -## SidebarSeparator - -The `SidebarSeparator` component is used to render a separator within a `Sidebar`. - -```tsx showLineNumbers - - - - - - - - - -``` - -## SidebarTrigger - -Use the `SidebarTrigger` component to render a button that toggles the sidebar. - -The `SidebarTrigger` component must be used within a `SidebarProvider`. - -```tsx showLineNumbers - - -
- -
-
-``` - -### Custom Trigger - -To create a custom trigger, you can use the `useSidebar` hook. - -```tsx showLineNumbers -import { useSidebar } from "@/components/ui/sidebar" - -export function CustomTrigger() { - const { toggleSidebar } = useSidebar() - - return -} -``` - -## SidebarRail - -The `SidebarRail` component is used to render a rail within a `Sidebar`. This rail can be used to toggle the sidebar. - -```tsx showLineNumbers - - - - - - - - -``` - -## Data Fetching - -### React Server Components - -Here's an example of a `SidebarMenu` component rendering a list of projects using React Server Components. - -
- -
- A sidebar menu using React Server Components. -
-
- -```tsx showLineNumbers {6} title="Skeleton to show loading state." -function NavProjectsSkeleton() { - return ( - - {Array.from({ length: 5 }).map((_, index) => ( - - - - ))} - - ) -} -``` - -```tsx showLineNumbers {2} title="Server component fetching data." -async function NavProjects() { - const projects = await fetchProjects() - - return ( - - {projects.map((project) => ( - - - - - {project.name} - - - - ))} - - ) -} -``` - -```tsx showLineNumbers {8-10} title="Usage with React Suspense." -function AppSidebar() { - return ( - - - - Projects - - }> - - - - - - - ) -} -``` - -### SWR and React Query - -You can use the same approach with [SWR](https://swr.vercel.app/) or [react-query](https://tanstack.com/query/latest/docs/framework/react/overview). - -```tsx showLineNumbers title="SWR" -function NavProjects() { - const { data, isLoading } = useSWR("/api/projects", fetcher) - - if (isLoading) { - return ( - - {Array.from({ length: 5 }).map((_, index) => ( - - - - ))} - - ) - } - - if (!data) { - return ... - } - - return ( - - {data.map((project) => ( - - - - - {project.name} - - - - ))} - - ) -} -``` - -```tsx showLineNumbers title="React Query" -function NavProjects() { - const { data, isLoading } = useQuery() - - if (isLoading) { - return ( - - {Array.from({ length: 5 }).map((_, index) => ( - - - - ))} - - ) - } - - if (!data) { - return ... - } - - return ( - - {data.map((project) => ( - - - - - {project.name} - - - - ))} - - ) -} -``` - -## Controlled Sidebar - -Use the `open` and `onOpenChange` props to control the sidebar. - -
- -
- A controlled sidebar. -
-
- -```tsx showLineNumbers -export function AppSidebar() { - const [open, setOpen] = React.useState(false) - - return ( - - - - ) -} -``` - -## Theming - -We use the following CSS variables to theme the sidebar. - -```css -@layer base { - :root { - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; - --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } - - .dark { - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 0 0% 98%; - --sidebar-primary-foreground: 240 5.9% 10%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; - } -} -``` - -**We intentionally use different variables for the sidebar and the rest of the application** to make it easy to have a sidebar that is styled differently from the rest of the application. Think a sidebar with a darker shade from the main application. - -## Styling - -Here are some tips for styling the sidebar based on different states. - -- **Styling an element based on the sidebar collapsible state.** The following will hide the `SidebarGroup` when the sidebar is in `icon` mode. - -```tsx - - - - - -``` - -- **Styling a menu action based on the menu button active state.** The following will force the menu action to be visible when the menu button is active. - -```tsx - - - - -``` - -You can find more tips on using states for styling in this [Twitter thread](https://x.com/shadcn/status/1842329158879420864). - -## Chanelog - -### 2024-10-20 Typo in `useSidebar` hook. - -Fixed typo in `useSidebar` hook. - -```diff showLineNumbers title="sidebar.tsx" -- throw new Error("useSidebar must be used within a Sidebar.") -+ throw new Error("useSidebar must be used within a SidebarProvider.") -``` diff --git a/apps/www/content/docs/components/typing-indicator.mdx b/apps/www/content/docs/components/typing-indicator.mdx index 27dba25..620b32a 100644 --- a/apps/www/content/docs/components/typing-indicator.mdx +++ b/apps/www/content/docs/components/typing-indicator.mdx @@ -1,5 +1,5 @@ --- -title: TypingIndicator +title: Typing Indicator description: A simple animated typing indicator component. component: true --- diff --git a/apps/www/content/docs/index.mdx b/apps/www/content/docs/index.mdx index 4f2d394..2bea32a 100644 --- a/apps/www/content/docs/index.mdx +++ b/apps/www/content/docs/index.mdx @@ -46,7 +46,7 @@ The kit includes several key features: - Built-in prompt suggestions - Message actions (copy, rate response, etc.) - Elegant loading states and transitions -- Markdown support and code syntax highlighting in messages +- Markdown support in messages - File attachments and image preview - Dark mode support - TypeScript support diff --git a/apps/www/registry/default/example/chat-message-demo.tsx b/apps/www/registry/default/example/chat-message-demo.tsx index f008a06..ef03b63 100644 --- a/apps/www/registry/default/example/chat-message-demo.tsx +++ b/apps/www/registry/default/example/chat-message-demo.tsx @@ -1,5 +1,14 @@ import { ChatMessage } from "@/registry/default/ui/chat-message" export default function ChatMessageDemo() { - return + return ( +
+ + +
+ ) } diff --git a/apps/www/registry/default/example/copy-button-demo.tsx b/apps/www/registry/default/example/copy-button-demo.tsx new file mode 100644 index 0000000..f5099fe --- /dev/null +++ b/apps/www/registry/default/example/copy-button-demo.tsx @@ -0,0 +1,10 @@ +import { CopyButton } from "@/registry/default/ui/copy-button" + +export default function CopyButtonDemo() { + return ( + + ) +} diff --git a/apps/www/registry/default/example/markdown-renderer-demo.tsx b/apps/www/registry/default/example/markdown-renderer-demo.tsx new file mode 100644 index 0000000..693530e --- /dev/null +++ b/apps/www/registry/default/example/markdown-renderer-demo.tsx @@ -0,0 +1,29 @@ +import { MarkdownRenderer } from "@/registry/default/ui/markdown-renderer" + +export default function MarkdownRendererDemo() { + return ( +
+ + {`# Hello World + + This is a paragraph with **bold** and *italic* text. + + ## Lists + - Item 1 + - Item 2 + - Nested item + + ## Code + \`\`\`tsx + console.log("Hello World") + \`\`\` + + ## Tables + | Header 1 | Header 2 | + |----------|----------| + | Cell 1 | Cell 2 | + `} + +
+ ) +} diff --git a/apps/www/registry/default/example/message-list-demo.tsx b/apps/www/registry/default/example/message-list-demo.tsx new file mode 100644 index 0000000..028c7db --- /dev/null +++ b/apps/www/registry/default/example/message-list-demo.tsx @@ -0,0 +1,40 @@ +"use client" + +import { useEffect, useRef, useState } from "react" + +import { MessageList } from "@/registry/default/ui/message-list" + +export default function MessageListDemo() { + const effectRan = useRef(false) + const [isTyping, setIsTyping] = useState(true) + const [messages, setMessages] = useState([ + { + id: "1", + role: "user", + content: "Hello! What is your name?", + }, + ]) + + useEffect(() => { + if (effectRan.current) return + effectRan.current = true + + setTimeout(() => { + setIsTyping(false) + setMessages((messages) => [ + ...messages, + { + id: "2", + role: "assistant", + content: "Hello! I go by ChatGPT. How are you?", + }, + ]) + }, 1500) + }, []) + + return ( +
+ +
+ ) +} diff --git a/apps/www/registry/default/example/prompt-suggestions-demo.tsx b/apps/www/registry/default/example/prompt-suggestions-demo.tsx new file mode 100644 index 0000000..83dfe82 --- /dev/null +++ b/apps/www/registry/default/example/prompt-suggestions-demo.tsx @@ -0,0 +1,23 @@ +"use client" + +import { toast } from "sonner" + +import { PromptSuggestions } from "@/registry/default/ui/prompt-suggestions" + +export default function PromptSuggestionsDemo() { + return ( +
+ { + toast(`Clicked on "${message.content}"`) + }} + suggestions={[ + "What is the capital of France?", + "Who won the 2022 FIFA World Cup?", + "Give me a vegan lasagna recipe for 3 people.", + ]} + /> +
+ ) +} diff --git a/apps/www/registry/default/example/typing-indicator-demo.tsx b/apps/www/registry/default/example/typing-indicator-demo.tsx new file mode 100644 index 0000000..d1e351c --- /dev/null +++ b/apps/www/registry/default/example/typing-indicator-demo.tsx @@ -0,0 +1,5 @@ +import { TypingIndicator } from "@/registry/default/ui/typing-indicator" + +export default function TypingIndicatorDemo() { + return +} diff --git a/apps/www/registry/index.ts b/apps/www/registry/index.ts index 7731087..45de2c7 100644 --- a/apps/www/registry/index.ts +++ b/apps/www/registry/index.ts @@ -119,4 +119,29 @@ export const registry: Registry = [ type: "registry:example", files: ["example/chat-message-demo.tsx"], }, + { + name: "message-list-demo", + type: "registry:example", + files: ["example/message-list-demo.tsx"], + }, + { + name: "typing-indicator-demo", + type: "registry:example", + files: ["example/typing-indicator-demo.tsx"], + }, + { + name: "markdown-renderer-demo", + type: "registry:example", + files: ["example/markdown-renderer-demo.tsx"], + }, + { + name: "prompt-suggestions-demo", + type: "registry:example", + files: ["example/prompt-suggestions-demo.tsx"], + }, + { + name: "copy-button-demo", + type: "registry:example", + files: ["example/copy-button-demo.tsx"], + }, ]