cherry-studio/packages/ui/src/components/primitives/textarea.tsx
MyPrototypeWhat 05c26fbff2 refactor(ui): update utility imports to use internal lib
- Changed utility imports from '@cherrystudio/ui/utils' to '@cherrystudio/ui/lib/utils' across multiple components for better organization.
- Introduced a new internal utility module for class name merging, ensuring consistent usage across the UI components.
- Updated relevant components to reflect the new import paths, enhancing maintainability and clarity.
2025-12-29 18:58:13 +08:00

117 lines
3.5 KiB
TypeScript

import { cn } from '@cherrystudio/ui/lib/utils'
import { composeEventHandlers } from '@radix-ui/primitive'
import { useCallbackRef } from '@radix-ui/react-use-callback-ref'
import { useControllableState } from '@radix-ui/react-use-controllable-state'
import { cva } from 'class-variance-authority'
import * as React from 'react'
/* -------------------------------------------------------------------------------------------------
* Variants
* -----------------------------------------------------------------------------------------------*/
const textareaVariants = cva(
cn(
'flex field-sizing-content min-h-16 w-full border bg-transparent px-4 py-3 text-lg shadow-xs transition-[color,box-shadow] outline-none resize-y',
'rounded-xs',
'border-input text-foreground placeholder:text-foreground-secondary',
'focus-visible:border-primary focus-visible:ring-ring focus-visible:ring-[3px]',
'disabled:cursor-not-allowed disabled:opacity-50',
'md:text-sm'
),
{
variants: {
hasError: {
true: 'aria-invalid:border-destructive aria-invalid:ring-destructive/20',
false: ''
}
},
defaultVariants: {
hasError: false
}
}
)
/* -------------------------------------------------------------------------------------------------
* TextareaInput
* -----------------------------------------------------------------------------------------------*/
const INPUT_NAME = 'TextareaInput'
interface TextareaInputProps extends Omit<React.ComponentPropsWithoutRef<'textarea'>, 'value' | 'defaultValue'> {
value?: string
defaultValue?: string
onValueChange?: (value: string) => void
hasError?: boolean
ref?: React.Ref<HTMLTextAreaElement>
}
function TextareaInput({
value: valueProp,
defaultValue,
onValueChange,
hasError = false,
className,
ref,
...props
}: TextareaInputProps) {
const [value = '', setValue] = useControllableState({
prop: valueProp,
defaultProp: defaultValue ?? '',
onChange: onValueChange
})
const handleChange = useCallbackRef((event: React.ChangeEvent<HTMLTextAreaElement>) => {
const newValue = event.target.value
if (props.maxLength && newValue.length > props.maxLength) {
return
}
setValue(newValue)
})
return (
<textarea
data-slot="textarea-input"
{...props}
ref={ref}
value={value}
onChange={composeEventHandlers(props.onChange, handleChange)}
aria-invalid={hasError}
className={cn(textareaVariants({ hasError }), className)}
/>
)
}
TextareaInput.displayName = INPUT_NAME
/* -------------------------------------------------------------------------------------------------
* TextareaCharCount
* -----------------------------------------------------------------------------------------------*/
const CHAR_COUNT_NAME = 'TextareaCharCount'
interface TextareaCharCountProps extends React.ComponentPropsWithoutRef<'div'> {
value?: string
maxLength?: number
}
function TextareaCharCount({ value = '', maxLength, className, ...props }: TextareaCharCountProps) {
return (
<div
data-slot="textarea-char-count"
{...props}
className={cn('absolute bottom-2 right-2 text-xs text-muted-foreground', className)}>
{value.length}/{maxLength}
</div>
)
}
TextareaCharCount.displayName = CHAR_COUNT_NAME
/* ---------------------------------------------------------------------------------------------- */
const Input = TextareaInput
const CharCount = TextareaCharCount
export { CharCount, Input }
export type { TextareaCharCountProps, TextareaInputProps }