Spaces:
Paused
Paused
| 'use client'; | |
| import type { ComponentProps } from 'react'; | |
| import { createContext, useContext } from 'react'; | |
| import { ChevronsUpDownIcon } from 'lucide-react'; | |
| import { cn } from '@/lib/utils'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; | |
| import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; | |
| import { Shimmer } from './shimmer'; | |
| type PlanContextValue = { | |
| isStreaming: boolean; | |
| }; | |
| const PlanContext = createContext<PlanContextValue | null>(null); | |
| const usePlan = () => { | |
| const context = useContext(PlanContext); | |
| if (!context) { | |
| throw new Error('Plan components must be used within Plan'); | |
| } | |
| return context; | |
| }; | |
| export type PlanProps = ComponentProps<typeof Collapsible> & { | |
| isStreaming?: boolean; | |
| }; | |
| export const Plan = ({ className, isStreaming = false, children, ...props }: PlanProps) => ( | |
| <PlanContext.Provider value={{ isStreaming }}> | |
| <Collapsible asChild data-slot='plan' {...props}> | |
| <Card className={cn('shadow-none', className)}>{children}</Card> | |
| </Collapsible> | |
| </PlanContext.Provider> | |
| ); | |
| export type PlanHeaderProps = ComponentProps<typeof CardHeader>; | |
| export const PlanHeader = ({ className, ...props }: PlanHeaderProps) => ( | |
| <CardHeader className={cn('flex items-start justify-between', className)} data-slot='plan-header' {...props} /> | |
| ); | |
| export type PlanTitleProps = Omit<ComponentProps<typeof CardTitle>, 'children'> & { | |
| children: string; | |
| }; | |
| export const PlanTitle = ({ children, ...props }: PlanTitleProps) => { | |
| const { isStreaming } = usePlan(); | |
| return ( | |
| <CardTitle data-slot='plan-title' {...props}> | |
| {isStreaming ? <Shimmer>{children}</Shimmer> : children} | |
| </CardTitle> | |
| ); | |
| }; | |
| export type PlanDescriptionProps = Omit<ComponentProps<typeof CardDescription>, 'children'> & { | |
| children: string; | |
| }; | |
| export const PlanDescription = ({ className, children, ...props }: PlanDescriptionProps) => { | |
| const { isStreaming } = usePlan(); | |
| return ( | |
| <CardDescription className={cn('text-balance', className)} data-slot='plan-description' {...props}> | |
| {isStreaming ? <Shimmer>{children}</Shimmer> : children} | |
| </CardDescription> | |
| ); | |
| }; | |
| export type PlanActionProps = ComponentProps<typeof CardAction>; | |
| export const PlanAction = (props: PlanActionProps) => <CardAction data-slot='plan-action' {...props} />; | |
| export type PlanContentProps = ComponentProps<typeof CardContent>; | |
| export const PlanContent = (props: PlanContentProps) => ( | |
| <CollapsibleContent asChild> | |
| <CardContent data-slot='plan-content' {...props} /> | |
| </CollapsibleContent> | |
| ); | |
| export type PlanFooterProps = ComponentProps<'div'>; | |
| export const PlanFooter = (props: PlanFooterProps) => <CardFooter data-slot='plan-footer' {...props} />; | |
| export type PlanTriggerProps = ComponentProps<typeof CollapsibleTrigger>; | |
| export const PlanTrigger = ({ className, ...props }: PlanTriggerProps) => ( | |
| <CollapsibleTrigger asChild> | |
| <Button className={cn('size-8', className)} data-slot='plan-trigger' size='icon' variant='ghost' {...props}> | |
| <ChevronsUpDownIcon className='size-4' /> | |
| <span className='sr-only'>Toggle plan</span> | |
| </Button> | |
| </CollapsibleTrigger> | |
| ); | |