### What problem does this PR solve? Feat: Add ChatInput component #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.17.0
| @@ -0,0 +1,46 @@ | |||
| import { useEventListener } from 'ahooks'; | |||
| import { Mic, Paperclip, Send } from 'lucide-react'; | |||
| import { useRef, useState } from 'react'; | |||
| import { Button } from './ui/button'; | |||
| import { Textarea } from './ui/textarea'; | |||
| export function ChatInput() { | |||
| const textareaRef = useRef<HTMLTextAreaElement>(null); | |||
| const [textareaHeight, setTextareaHeight] = useState<number>(40); | |||
| useEventListener( | |||
| 'keydown', | |||
| (ev) => { | |||
| if (ev.shiftKey && ev.code === 'Enter') { | |||
| setTextareaHeight((h) => { | |||
| return h + 10; | |||
| }); | |||
| } | |||
| }, | |||
| { | |||
| target: textareaRef, | |||
| }, | |||
| ); | |||
| return ( | |||
| <section className="flex items-end bg-colors-background-neutral-strong px-4 py-3 rounded-xl m-8"> | |||
| <Button variant={'icon'} className="w-10 h-10"> | |||
| <Mic /> | |||
| </Button> | |||
| <Textarea | |||
| ref={textareaRef} | |||
| placeholder="Tell us a little bit about yourself " | |||
| className="resize-none focus-visible:ring-0 focus-visible:ring-offset-0 bg-transparent border-none min-h-0 max-h-20" | |||
| style={{ height: textareaHeight }} | |||
| /> | |||
| <div className="flex gap-2"> | |||
| <Button variant={'icon'} size={'icon'}> | |||
| <Paperclip /> | |||
| </Button> | |||
| <Button variant={'tertiary'} size={'icon'}> | |||
| <Send /> | |||
| </Button> | |||
| </div> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -9,7 +9,7 @@ const Textarea = React.forwardRef< | |||
| return ( | |||
| <textarea | |||
| className={cn( | |||
| 'flex min-h-[80px] w-full rounded-md border border-input bg-colors-background-inverse-weak px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm', | |||
| 'flex min-h-[80px] w-full rounded-md border border-input bg-colors-background-inverse-weak px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm overflow-hidden', | |||
| className, | |||
| )} | |||
| ref={ref} | |||
| @@ -1,3 +1,4 @@ | |||
| import { Button } from '@/components/ui/button'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { FormProvider, useForm } from 'react-hook-form'; | |||
| import { z } from 'zod'; | |||
| @@ -35,7 +36,7 @@ export function AppSettings() { | |||
| <div className="text-2xl font-bold mb-4 text-colors-text-neutral-strong px-6"> | |||
| App settings | |||
| </div> | |||
| <div className="overflow-auto max-h-[88vh] px-6 "> | |||
| <div className="overflow-auto max-h-[81vh] px-6 "> | |||
| <FormProvider {...form}> | |||
| <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6"> | |||
| <ChatBasicSetting></ChatBasicSetting> | |||
| @@ -44,6 +45,14 @@ export function AppSettings() { | |||
| </form> | |||
| </FormProvider> | |||
| </div> | |||
| <div className="p-6 text-center"> | |||
| <p className="text-colors-text-neutral-weak mb-1"> | |||
| There are unsaved changes | |||
| </p> | |||
| <Button variant={'tertiary'} className="w-full"> | |||
| Update | |||
| </Button> | |||
| </div> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -1,3 +1,9 @@ | |||
| import { ChatInput } from '@/components/chat-input'; | |||
| export function ChatBox() { | |||
| return <section className="border-x flex-1">ChatBox</section>; | |||
| return ( | |||
| <section className="border-x flex-1"> | |||
| <ChatInput></ChatInput> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -35,6 +35,7 @@ module.exports = { | |||
| 'colors-text-core-standard': 'var(--colors-text-core-standard)', | |||
| 'colors-text-neutral-strong': 'var(--colors-text-neutral-strong)', | |||
| 'colors-text-neutral-standard': 'var(--colors-text-neutral-standard)', | |||
| 'colors-text-neutral-weak': 'var(--colors-text-neutral-weak)', | |||
| 'colors-text-functional-danger': 'var(--colors-text-functional-danger)', | |||
| 'colors-text-inverse-strong': 'var(--colors-text-inverse-strong)', | |||
| 'colors-text-persist-light': 'var(--colors-text-persist-light)', | |||
| @@ -41,6 +41,7 @@ | |||
| --colors-background-inverse-strong: rgba(11, 10, 18, 0.8); | |||
| --colors-background-inverse-weak: rgba(17, 16, 23, 0.1); | |||
| --colors-background-neutral-standard: white; | |||
| --colors-background-neutral-strong: rgba(226, 223, 246, 1); | |||
| --colors-background-functional-solid-danger: rgba(222, 17, 53, 1); | |||
| --colors-background-core-strong: rgba(98, 72, 246, 1); | |||
| --colors-background-sentiment-solid-primary: rgba(127, 105, 255, 1); | |||
| @@ -53,7 +54,8 @@ | |||
| --colors-text-core-standard: rgba(127, 105, 255, 1); | |||
| --colors-text-neutral-strong: rgba(17, 16, 23, 1); | |||
| --colors-text-neutral-standard: rgba(53, 51, 65, 1); | |||
| --colors-text-neutral-standard: rgba(152, 148, 176, 1); | |||
| --colors-text-neutral-weak: rgba(170, 160, 197, 1); | |||
| --colors-text-functional-danger: rgba(255, 81, 81, 1); | |||
| --colors-text-inverse-strong: rgba(255, 255, 255, 1); | |||
| --colors-text-persist-light: rgba(255, 255, 255, 1); | |||
| @@ -136,6 +138,7 @@ | |||
| --colors-text-core-standard: rgba(137, 126, 255, 1); | |||
| --colors-text-neutral-strong: rgba(255, 255, 255, 1); | |||
| --colors-text-neutral-standard: rgba(230, 227, 246, 1); | |||
| --colors-text-neutral-weak: rgba(170, 160, 197, 1); | |||
| --colors-text-functional-danger: rgba(255, 81, 81, 1); | |||
| --colors-text-inverse-strong: rgba(17, 16, 23, 1); | |||
| --colors-text-persist-light: rgba(255, 255, 255, 1); | |||