- Input: with label, error, hint, a11y attributes - Textarea: resizable with label, error, hint - Card: with CardHeader, CardTitle, CardDescription sub-components - Label: with required indicator - Select: native select with chevron icon, label, error - Separator: horizontal/vertical with ARIA roles - All components use --bl-* design token CSS variables - 12 total components in @bytelyst/ui
56 lines
1.8 KiB
TypeScript
56 lines
1.8 KiB
TypeScript
import * as React from 'react';
|
|
import { clsx } from 'clsx';
|
|
|
|
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
label?: string;
|
|
error?: string;
|
|
hint?: string;
|
|
}
|
|
|
|
export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
({ label, error, hint, className, id, ...props }, ref) => {
|
|
const textareaId =
|
|
id ?? (label ? `textarea-${label.toLowerCase().replace(/\s+/g, '-')}` : undefined);
|
|
|
|
return (
|
|
<div className="space-y-1">
|
|
{label && (
|
|
<label
|
|
htmlFor={textareaId}
|
|
className="block text-sm font-medium text-[var(--bl-text-secondary,#a0a0b0)]"
|
|
>
|
|
{label}
|
|
</label>
|
|
)}
|
|
<textarea
|
|
ref={ref}
|
|
id={textareaId}
|
|
className={clsx(
|
|
'w-full rounded-md border px-3 py-2 text-sm outline-none transition-colors resize-y',
|
|
'bg-[var(--bl-surface-card,#1a1a2e)] text-[var(--bl-text-primary,#fff)]',
|
|
'placeholder:text-[var(--bl-text-tertiary,#555)]',
|
|
'focus:ring-2 focus:ring-[var(--bl-accent,#5A8CFF)] focus:ring-offset-0',
|
|
error ? 'border-red-500' : 'border-[var(--bl-border,#2a2a4a)]',
|
|
className
|
|
)}
|
|
aria-invalid={error ? 'true' : undefined}
|
|
aria-describedby={error ? `${textareaId}-error` : hint ? `${textareaId}-hint` : undefined}
|
|
{...props}
|
|
/>
|
|
{error && (
|
|
<p id={`${textareaId}-error`} className="text-xs text-red-400" role="alert">
|
|
{error}
|
|
</p>
|
|
)}
|
|
{hint && !error && (
|
|
<p id={`${textareaId}-hint`} className="text-xs text-[var(--bl-text-tertiary,#555)]">
|
|
{hint}
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
|
|
Textarea.displayName = 'Textarea';
|