Files
stack/packages/ui/src/components/Textarea.tsx
Jason Woltje f47dd8bc92 feat: add domains, ideas, layouts, widgets API modules
- Add DomainsModule with full CRUD, search, and activity logging
- Add IdeasModule with quick capture endpoint
- Add LayoutsModule for user dashboard layouts
- Add WidgetsModule for widget definitions (read-only)
- Update ActivityService with domain/idea logging methods
- Register all new modules in AppModule
2026-01-29 13:47:03 -06:00

73 lines
1.9 KiB
TypeScript

import type { TextareaHTMLAttributes } from "react";
export interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "size"> {
label?: string;
error?: string;
helperText?: string;
fullWidth?: boolean;
resize?: "none" | "both" | "horizontal" | "vertical";
}
export function Textarea({
label,
error,
helperText,
fullWidth = false,
resize = "vertical",
className = "",
id,
...props
}: TextareaProps) {
const textareaId = id || `textarea-${Math.random().toString(36).substr(2, 9)}`;
const errorId = error ? `${textareaId}-error` : undefined;
const helperId = helperText ? `${textareaId}-helper` : undefined;
const baseStyles = "px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors";
const widthStyles = fullWidth ? "w-full" : "";
const resizeStyles = {
none: "resize-none",
both: "resize",
horizontal: "resize-x",
vertical: "resize-y",
};
const errorStyles = error ? "border-red-500 focus:ring-red-500" : "border-gray-300";
const combinedClassName = [
baseStyles,
widthStyles,
resizeStyles[resize],
errorStyles,
className,
].filter(Boolean).join(" ");
return (
<div className={fullWidth ? "w-full" : ""}>
{label && (
<label
htmlFor={textareaId}
className="block text-sm font-medium text-gray-700 mb-1"
>
{label}
</label>
)}
<textarea
id={textareaId}
className={combinedClassName}
aria-invalid={error ? "true" : "false"}
aria-describedby={[errorId, helperId].filter(Boolean).join(" ") || undefined}
{...props}
/>
{error && (
<p id={errorId} className="mt-1 text-sm text-red-600" role="alert">
{error}
</p>
)}
{helperText && !error && (
<p id={helperId} className="mt-1 text-sm text-gray-500">
{helperText}
</p>
)}
</div>
);
}