Skip to content

Commit

Permalink
Merge pull request #55 from pengooseDev/pengooseDev
Browse files Browse the repository at this point in the history
refactor: improve ChatInput handling for composition-based languages
  • Loading branch information
jakobhoeg authored Sep 28, 2024
2 parents 6484bed + d8086df commit a4d830f
Show file tree
Hide file tree
Showing 5 changed files with 8 additions and 42 deletions.
14 changes: 2 additions & 12 deletions apps/docs/components/ui/chat/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,14 @@ import * as React from "react";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";

interface ChatInputProps {
className?: string;
value?: string;
onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
placeholder?: string;
}
interface ChatInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement>{}

const ChatInput = React.forwardRef<HTMLTextAreaElement, ChatInputProps>(
({ className, value, onKeyDown, onChange, placeholder, ...props }, ref) => (
({ className, ...props }, ref) => (
<Textarea
autoComplete="off"
value={value}
ref={ref}
onKeyDown={onKeyDown}
onChange={onChange}
name="message"
placeholder={placeholder}
className={cn(
"max-h-12 px-4 py-3 bg-background text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 w-full rounded-md flex items-center h-16 resize-none",
className,
Expand Down
4 changes: 2 additions & 2 deletions apps/www/public/registry/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"files": [
{
"name": "chat-bubble.tsx",
"content": "import * as React from \"react\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Avatar, AvatarImage, AvatarFallback } from \"@/components/ui/avatar\";\r\nimport MessageLoading from \"./message-loading\";\r\nimport { Button, ButtonProps } from \"../button\";\r\n\r\n// ChatBubble\r\nconst chatBubbleVariant = cva(\"flex gap-2 max-w-[60%] items-end relative group\", {\r\n variants: {\r\n variant: {\r\n received: \"self-start\",\r\n sent: \"self-end flex-row-reverse\",\r\n },\r\n layout: {\r\n default: \"\",\r\n ai: \"max-w-full w-full items-center\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"received\",\r\n layout: \"default\",\r\n },\r\n});\r\n\r\ninterface ChatBubbleProps\r\n extends React.HTMLAttributes<HTMLDivElement>,\r\n VariantProps<typeof chatBubbleVariant> { }\r\n\r\nconst ChatBubble = React.forwardRef<HTMLDivElement, ChatBubbleProps>(\r\n ({ className, variant, layout, children, ...props }, ref) => (\r\n <div\r\n className={cn(chatBubbleVariant({ variant, layout, className }), \"relative group\")}\r\n ref={ref}\r\n {...props}\r\n >\r\n {React.Children.map(children, child =>\r\n React.isValidElement(child) && typeof child.type !== 'string'\r\n ? React.cloneElement(child, { variant, layout } as React.ComponentProps<typeof child.type>)\r\n : child\r\n )}\r\n </div>\r\n ),\r\n);\r\nChatBubble.displayName = \"ChatBubble\";\r\n\r\n// ChatBubbleAvatar\r\ninterface ChatBubbleAvatarProps {\r\n src?: string;\r\n fallback?: string;\r\n className?: string;\r\n}\r\n\r\nconst ChatBubbleAvatar: React.FC<ChatBubbleAvatarProps> = ({\r\n src,\r\n fallback,\r\n className,\r\n}) => (\r\n <Avatar className={className}>\r\n <AvatarImage src={src} alt=\"Avatar\" />\r\n <AvatarFallback>{fallback}</AvatarFallback>\r\n </Avatar>\r\n);\r\n\r\n// ChatBubbleMessage\r\nconst chatBubbleMessageVariants = cva(\"p-4\", {\r\n variants: {\r\n variant: {\r\n received:\r\n \"bg-secondary text-secondary-foreground rounded-r-lg rounded-tl-lg\",\r\n sent: \"bg-primary text-primary-foreground rounded-l-lg rounded-tr-lg\",\r\n },\r\n layout: {\r\n default: \"\",\r\n ai: \"border-t w-full rounded-none bg-transparent\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"received\",\r\n layout: \"default\",\r\n },\r\n});\r\n\r\ninterface ChatBubbleMessageProps\r\n extends React.HTMLAttributes<HTMLDivElement>,\r\n VariantProps<typeof chatBubbleMessageVariants> {\r\n isLoading?: boolean;\r\n}\r\n\r\nconst ChatBubbleMessage = React.forwardRef<\r\n HTMLDivElement,\r\n ChatBubbleMessageProps\r\n>(\r\n (\r\n { className, variant, layout, isLoading = false, children, ...props },\r\n ref,\r\n ) => (\r\n <div\r\n className={cn(\r\n chatBubbleMessageVariants({ variant, layout, className }),\r\n \"break-words max-w-full whitespace-pre-wrap\",\r\n )}\r\n ref={ref}\r\n {...props}\r\n >\r\n {isLoading ? (\r\n <div className=\"flex items-center space-x-2\">\r\n <MessageLoading />\r\n </div>\r\n ) : (\r\n children\r\n )}\r\n </div>\r\n ),\r\n);\r\nChatBubbleMessage.displayName = \"ChatBubbleMessage\";\r\n\r\n// ChatBubbleTimestamp\r\ninterface ChatBubbleTimestampProps\r\n extends React.HTMLAttributes<HTMLDivElement> {\r\n timestamp: string;\r\n}\r\n\r\nconst ChatBubbleTimestamp: React.FC<ChatBubbleTimestampProps> = ({\r\n timestamp,\r\n className,\r\n ...props\r\n}) => (\r\n <div className={cn(\"text-xs mt-2 text-right\", className)} {...props}>\r\n {timestamp}\r\n </div>\r\n);\r\n\r\n// ChatBubbleAction\r\ntype ChatBubbleActionProps = ButtonProps & {\r\n icon: React.ReactNode;\r\n};\r\n\r\nconst ChatBubbleAction: React.FC<ChatBubbleActionProps> = ({\r\n icon,\r\n onClick,\r\n className,\r\n variant = \"ghost\",\r\n size = \"icon\",\r\n ...props\r\n}) => (\r\n <Button\r\n variant={variant}\r\n size={size}\r\n className={className}\r\n onClick={onClick}\r\n {...props}\r\n >\r\n {icon}\r\n </Button>\r\n);\r\n\r\ninterface ChatBubbleActionWrapperProps {\r\n variant: \"sent\" | \"received\";\r\n className?: string;\r\n children: React.ReactNode;\r\n}\r\n\r\n// ChatBubbleActionWrapper\r\nconst ChatBubbleActionWrapper: React.FC<ChatBubbleActionWrapperProps> = ({ variant, className, children }) => (\r\n <div className={cn(\r\n \"absolute top-1/2 -translate-y-1/2 flex opacity-0 group-hover:opacity-100 transition-opacity duration-200\",\r\n variant === \"sent\" ? \"-left-1 -translate-x-full flex-row-reverse\" : \"-right-1 translate-x-full\",\r\n className\r\n )}>\r\n {children}\r\n </div>\r\n);\r\n\r\nexport {\r\n ChatBubble,\r\n ChatBubbleAvatar,\r\n ChatBubbleMessage,\r\n ChatBubbleTimestamp,\r\n chatBubbleVariant,\r\n chatBubbleMessageVariants,\r\n ChatBubbleAction,\r\n ChatBubbleActionWrapper,\r\n};\r\n"
"content": "import * as React from \"react\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Avatar, AvatarImage, AvatarFallback } from \"@/components/ui/avatar\";\r\nimport MessageLoading from \"./message-loading\";\r\nimport { Button, ButtonProps } from \"../button\";\r\n\r\n// ChatBubble\r\nconst chatBubbleVariant = cva(\r\n \"flex gap-2 max-w-[60%] items-end relative group\",\r\n {\r\n variants: {\r\n variant: {\r\n received: \"self-start\",\r\n sent: \"self-end flex-row-reverse\",\r\n },\r\n layout: {\r\n default: \"\",\r\n ai: \"max-w-full w-full items-center\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"received\",\r\n layout: \"default\",\r\n },\r\n },\r\n);\r\n\r\ninterface ChatBubbleProps\r\n extends React.HTMLAttributes<HTMLDivElement>,\r\n VariantProps<typeof chatBubbleVariant> {}\r\n\r\nconst ChatBubble = React.forwardRef<HTMLDivElement, ChatBubbleProps>(\r\n ({ className, variant, layout, children, ...props }, ref) => (\r\n <div\r\n className={cn(\r\n chatBubbleVariant({ variant, layout, className }),\r\n \"relative group\",\r\n )}\r\n ref={ref}\r\n {...props}\r\n >\r\n {React.Children.map(children, (child) =>\r\n React.isValidElement(child) && typeof child.type !== \"string\"\r\n ? React.cloneElement(child, {\r\n variant,\r\n layout,\r\n } as React.ComponentProps<typeof child.type>)\r\n : child,\r\n )}\r\n </div>\r\n ),\r\n);\r\nChatBubble.displayName = \"ChatBubble\";\r\n\r\n// ChatBubbleAvatar\r\ninterface ChatBubbleAvatarProps {\r\n src?: string;\r\n fallback?: string;\r\n className?: string;\r\n}\r\n\r\nconst ChatBubbleAvatar: React.FC<ChatBubbleAvatarProps> = ({\r\n src,\r\n fallback,\r\n className,\r\n}) => (\r\n <Avatar className={className}>\r\n <AvatarImage src={src} alt=\"Avatar\" />\r\n <AvatarFallback>{fallback}</AvatarFallback>\r\n </Avatar>\r\n);\r\n\r\n// ChatBubbleMessage\r\nconst chatBubbleMessageVariants = cva(\"p-4\", {\r\n variants: {\r\n variant: {\r\n received:\r\n \"bg-secondary text-secondary-foreground rounded-r-lg rounded-tl-lg\",\r\n sent: \"bg-primary text-primary-foreground rounded-l-lg rounded-tr-lg\",\r\n },\r\n layout: {\r\n default: \"\",\r\n ai: \"border-t w-full rounded-none bg-transparent\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"received\",\r\n layout: \"default\",\r\n },\r\n});\r\n\r\ninterface ChatBubbleMessageProps\r\n extends React.HTMLAttributes<HTMLDivElement>,\r\n VariantProps<typeof chatBubbleMessageVariants> {\r\n isLoading?: boolean;\r\n}\r\n\r\nconst ChatBubbleMessage = React.forwardRef<\r\n HTMLDivElement,\r\n ChatBubbleMessageProps\r\n>(\r\n (\r\n { className, variant, layout, isLoading = false, children, ...props },\r\n ref,\r\n ) => (\r\n <div\r\n className={cn(\r\n chatBubbleMessageVariants({ variant, layout, className }),\r\n \"break-words max-w-full whitespace-pre-wrap\",\r\n )}\r\n ref={ref}\r\n {...props}\r\n >\r\n {isLoading ? (\r\n <div className=\"flex items-center space-x-2\">\r\n <MessageLoading />\r\n </div>\r\n ) : (\r\n children\r\n )}\r\n </div>\r\n ),\r\n);\r\nChatBubbleMessage.displayName = \"ChatBubbleMessage\";\r\n\r\n// ChatBubbleTimestamp\r\ninterface ChatBubbleTimestampProps\r\n extends React.HTMLAttributes<HTMLDivElement> {\r\n timestamp: string;\r\n}\r\n\r\nconst ChatBubbleTimestamp: React.FC<ChatBubbleTimestampProps> = ({\r\n timestamp,\r\n className,\r\n ...props\r\n}) => (\r\n <div className={cn(\"text-xs mt-2 text-right\", className)} {...props}>\r\n {timestamp}\r\n </div>\r\n);\r\n\r\n// ChatBubbleAction\r\ntype ChatBubbleActionProps = ButtonProps & {\r\n icon: React.ReactNode;\r\n};\r\n\r\nconst ChatBubbleAction: React.FC<ChatBubbleActionProps> = ({\r\n icon,\r\n onClick,\r\n className,\r\n variant = \"ghost\",\r\n size = \"icon\",\r\n ...props\r\n}) => (\r\n <Button\r\n variant={variant}\r\n size={size}\r\n className={className}\r\n onClick={onClick}\r\n {...props}\r\n >\r\n {icon}\r\n </Button>\r\n);\r\n\r\ninterface ChatBubbleActionWrapperProps\r\n extends React.HTMLAttributes<HTMLDivElement> {\r\n variant?: \"sent\" | \"received\";\r\n className?: string;\r\n}\r\n\r\nconst ChatBubbleActionWrapper = React.forwardRef<\r\n HTMLDivElement,\r\n ChatBubbleActionWrapperProps\r\n>(({ variant, className, children, ...props }, ref) => (\r\n <div\r\n ref={ref}\r\n className={cn(\r\n \"absolute top-1/2 -translate-y-1/2 flex opacity-0 group-hover:opacity-100 transition-opacity duration-200\",\r\n variant === \"sent\"\r\n ? \"-left-1 -translate-x-full flex-row-reverse\"\r\n : \"-right-1 translate-x-full\",\r\n className,\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n </div>\r\n));\r\nChatBubbleActionWrapper.displayName = \"ChatBubbleActionWrapper\";\r\n\r\nexport {\r\n ChatBubble,\r\n ChatBubbleAvatar,\r\n ChatBubbleMessage,\r\n ChatBubbleTimestamp,\r\n chatBubbleVariant,\r\n chatBubbleMessageVariants,\r\n ChatBubbleAction,\r\n ChatBubbleActionWrapper,\r\n};\r\n"
}
]
},
Expand All @@ -18,7 +18,7 @@
"files": [
{
"name": "chat-input.tsx",
"content": "import * as React from \"react\";\r\nimport { Textarea } from \"@/components/ui/textarea\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ninterface ChatInputProps {\r\n className?: string;\r\n value?: string;\r\n onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;\r\n onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;\r\n placeholder?: string;\r\n}\r\n\r\nconst ChatInput = React.forwardRef<HTMLTextAreaElement, ChatInputProps>(\r\n ({ className, value, onKeyDown, onChange, placeholder, ...props }, ref) => (\r\n <Textarea\r\n autoComplete=\"off\"\r\n value={value}\r\n ref={ref}\r\n onKeyDown={onKeyDown}\r\n onChange={onChange}\r\n name=\"message\"\r\n placeholder={placeholder}\r\n className={cn(\r\n \"max-h-12 px-4 py-3 bg-background text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 w-full rounded-md flex items-center h-16 resize-none\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n ),\r\n);\r\nChatInput.displayName = \"ChatInput\";\r\n\r\nexport { ChatInput };\r\n"
"content": "import * as React from \"react\";\r\nimport { Textarea } from \"@/components/ui/textarea\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ninterface ChatInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement>{}\r\n\r\nconst ChatInput = React.forwardRef<HTMLTextAreaElement, ChatInputProps>(\r\n ({ className, ...props }, ref) => (\r\n <Textarea\r\n autoComplete=\"off\"\r\n ref={ref}\r\n name=\"message\"\r\n className={cn(\r\n \"max-h-12 px-4 py-3 bg-background text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 w-full rounded-md flex items-center h-16 resize-none\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n ),\r\n);\r\nChatInput.displayName = \"ChatInput\";\r\n\r\nexport { ChatInput };\r\n"
}
]
},
Expand Down
14 changes: 2 additions & 12 deletions apps/www/src/components/ui/chat/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,14 @@ import * as React from "react";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";

interface ChatInputProps {
className?: string;
value?: string;
onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
placeholder?: string;
}
interface ChatInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement>{}

const ChatInput = React.forwardRef<HTMLTextAreaElement, ChatInputProps>(
({ className, value, onKeyDown, onChange, placeholder, ...props }, ref) => (
({ className, ...props }, ref) => (
<Textarea
autoComplete="off"
value={value}
ref={ref}
onKeyDown={onKeyDown}
onChange={onChange}
name="message"
placeholder={placeholder}
className={cn(
"max-h-12 px-4 py-3 bg-background text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 w-full rounded-md flex items-center h-16 resize-none",
className,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,14 @@ import * as React from "react";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";

interface ChatInputProps {
className?: string;
value?: string;
onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
placeholder?: string;
}
interface ChatInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement>{}

const ChatInput = React.forwardRef<HTMLTextAreaElement, ChatInputProps>(
({ className, value, onKeyDown, onChange, placeholder, ...props }, ref) => (
({ className, ...props }, ref) => (
<Textarea
autoComplete="off"
value={value}
ref={ref}
onKeyDown={onKeyDown}
onChange={onChange}
name="message"
placeholder={placeholder}
className={cn(
"max-h-12 px-4 py-3 bg-background text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 w-full rounded-md flex items-center h-16 resize-none",
className,
Expand Down
4 changes: 0 additions & 4 deletions packages/cli/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,3 @@ npx shadcn-chat-cli add [component]
```

The components will be installed in a subdirectory of the `components` folder: `src/components/ui/chat`

# Usage & Examples

All of the component primitives are unstyled and you can add styling in any way you'd like - for instance with `className`.

0 comments on commit a4d830f

Please sign in to comment.