mirror of
https://github.com/supabase/supabase.git
synced 2026-07-05 16:04:27 +08:00
* init site * add nav menu * add props table component * init all the examples • also includes moving a bunch of ./ui files to ./ui-patterns * update package • might need to remove some of these * Update rehype-component.ts * move nav * more changes * add new source information to contentlayer * fix copy buttons * add text-confirm. start new concept of "fragments" * move base path. add homepage. add theme selection * added colors * add form-item-layout * temporary fix code blocks in light mode. they currently don't switch theme to theme * start adding code themes * Update dark.json * update code block themes * fix animations * add cdmk search * export registry of icons in ./icons package * add comments * add icon copy stuff * Update icons.tsx * more docs * more docs. cleaned up colors and icons docs * Update theming.mdx * add more docs * update * add package * Update tailwind.config.js * update content * Update drawer-demo.tsx * Update aspect-ratio-demo.tsx * add new blocks * Update source-panel.tsx * Update source-panel.tsx * Update source-panel.tsx * add new source block * Update source-panel.tsx * Update _app.tsx * Update page.tsx * Delete layout-old.tsx * remove old themes * remove old ui registry * comment out nav items * Update package-lock.json * Update AssistantChatForm.tsx * move back * move again * update * Update index.tsx * Update package-lock.json * Update package-lock.json * Update package-lock.json * update package * udpate prettier * fix tag issues * Update package-lock.json * update to contentlayer2 * update package-lock.json * fix type errors * ignore spelling * Update avoid-typos.yml * Update avoid-typos.yml * Update package-lock.json * Use ui type definitions. * move tsconfig base stuff. fix content layer issue * Update package-lock.json * Update package-lock.json --------- Co-authored-by: Alaister Young <a@alaisteryoung.com> Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
152 lines
5.0 KiB
TypeScript
152 lines
5.0 KiB
TypeScript
import { Loader2 } from 'lucide-react'
|
|
import React, { ChangeEvent, createRef, useEffect } from 'react'
|
|
import { TextArea } from 'ui/src/components/shadcn/ui/text-area'
|
|
import { cn } from 'ui/src/lib/utils'
|
|
|
|
export interface FormProps extends React.FormHTMLAttributes<HTMLFormElement> {
|
|
/* The ref for the textarea */
|
|
textAreaRef: React.RefObject<HTMLTextAreaElement>
|
|
/* The loading state of the form */
|
|
loading: boolean
|
|
/* The disabled state of the form */
|
|
disabled?: boolean
|
|
/* The value of the textarea */
|
|
value?: string
|
|
/* The function to handle the value change */
|
|
onValueChange: (value: ChangeEvent<HTMLTextAreaElement>) => void
|
|
/* Used to stop onSubmit event when Command popover is open. Use with AssistantCommandsPopover */
|
|
commandsOpen?: boolean
|
|
/* Used to close Command popover when onSubmit event happens. Use with AssistantCommandsPopover */
|
|
setCommandsOpen?: (value: boolean) => void
|
|
/* The icon to display on the left of the textarea */
|
|
icon?: React.ReactNode
|
|
/* The function to handle the form submission */
|
|
onSubmit: React.FormHTMLAttributes<HTMLFormElement>['onSubmit']
|
|
/* The placeholder of the textarea */
|
|
placeholder?: string
|
|
}
|
|
|
|
const AssistantChatForm = React.forwardRef<HTMLFormElement, FormProps>(
|
|
(
|
|
{
|
|
loading = false,
|
|
disabled = false,
|
|
value = '',
|
|
textAreaRef,
|
|
commandsOpen = false,
|
|
icon = null,
|
|
onValueChange,
|
|
setCommandsOpen,
|
|
onSubmit,
|
|
placeholder,
|
|
...props
|
|
},
|
|
ref
|
|
) => {
|
|
const formRef = createRef<HTMLFormElement>()
|
|
const submitRef = createRef<HTMLButtonElement>()
|
|
|
|
/**
|
|
* This effect is used to resize the textarea based on the content
|
|
*/
|
|
useEffect(() => {
|
|
if (textAreaRef) {
|
|
if (!value && textAreaRef && textAreaRef.current) {
|
|
textAreaRef.current.style.height = '40px'
|
|
} else if (textAreaRef && textAreaRef.current) {
|
|
const newHeight = textAreaRef.current.scrollHeight + 'px'
|
|
textAreaRef.current.style.height = newHeight
|
|
}
|
|
}
|
|
}, [value, textAreaRef])
|
|
|
|
/**
|
|
* This effect is used to focus the textarea when the component mounts
|
|
*/
|
|
useEffect(() => {
|
|
textAreaRef?.current?.focus()
|
|
}, [value, textAreaRef])
|
|
|
|
/**
|
|
* This function is used to handle the "Enter" key press
|
|
*/
|
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
// Check if the pressed key is "Enter" (key code 13) without the "Shift" key
|
|
// also checks if the commands popover is open
|
|
if (event.key === 'Enter' && !event.shiftKey && !commandsOpen) {
|
|
event.preventDefault()
|
|
if (submitRef.current) {
|
|
submitRef.current.click()
|
|
}
|
|
}
|
|
|
|
// handles closing the commands popover if open
|
|
if (event.key === 'Enter' && commandsOpen) {
|
|
if (setCommandsOpen) setCommandsOpen(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<form ref={formRef} className="relative" {...props} onSubmit={onSubmit}>
|
|
{icon && (
|
|
<div className={cn('absolute', 'top-2 left-2', 'ml-1 w-6 h-6 rounded-full bg-dbnew')}>
|
|
{icon}
|
|
</div>
|
|
)}
|
|
<TextArea
|
|
ref={textAreaRef}
|
|
autoFocus
|
|
rows={1}
|
|
disabled={disabled}
|
|
contentEditable
|
|
aria-expanded={false}
|
|
className={cn(
|
|
icon ? 'pl-12' : '',
|
|
'transition-all text-sm pr-10 rounded-[18px] resize-none box-border leading-6'
|
|
)}
|
|
placeholder={placeholder}
|
|
spellCheck={false}
|
|
value={value}
|
|
onChange={(event: ChangeEvent<HTMLTextAreaElement>) => onValueChange(event)}
|
|
onKeyDown={handleKeyDown}
|
|
/>
|
|
<div className="absolute right-1.5 top-1.5 flex gap-3 items-center">
|
|
{loading && (
|
|
<Loader2 size={22} className="animate-spin w-7 h-7 text-muted" strokeWidth={1} />
|
|
)}
|
|
|
|
<button
|
|
ref={submitRef}
|
|
type="submit"
|
|
className={cn(
|
|
'transition-all',
|
|
'flex items-center justify-center w-7 h-7 border border-control rounded-full mr-0.5 p-1.5 background-alternative',
|
|
!value ? 'text-muted opacity-50' : 'text-default opacity-100',
|
|
loading && 'hidden'
|
|
)}
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 16 16"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
fillRule="evenodd"
|
|
clipRule="evenodd"
|
|
d="M13.5 3V2.25H15V3V10C15 10.5523 14.5522 11 14 11H3.56062L5.53029 12.9697L6.06062 13.5L4.99996 14.5607L4.46963 14.0303L1.39641 10.9571C1.00588 10.5666 1.00588 9.93342 1.39641 9.54289L4.46963 6.46967L4.99996 5.93934L6.06062 7L5.53029 7.53033L3.56062 9.5H13.5V3Z"
|
|
fill="currentColor"
|
|
></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
)
|
|
}
|
|
)
|
|
|
|
AssistantChatForm.displayName = 'AssistantChatForm'
|
|
|
|
export { AssistantChatForm }
|