{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "gummy-textarea",
  "type": "registry:ui",
  "title": "Gummy Textarea",
  "description": "Native multiline editing surface with connected state material, validation, resizing, and character count.",
  "registryDependencies": [
    "https://gummyui.dev/r/gummy-label.json",
    "https://gummyui.dev/r/gummy-form-styles.json"
  ],
  "files": [
    {
      "path": "components/ui/GummyTextarea.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { GummyLabel } from \"./GummyLabel\";\n\nexport type GummyTextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement> & {\n  label: React.ReactNode;\n  description?: React.ReactNode;\n  errorMessage?: React.ReactNode;\n  successMessage?: React.ReactNode;\n  optional?: boolean;\n  resize?: \"none\" | \"vertical\" | \"both\";\n  showCount?: boolean;\n  wrapperClassName?: string;\n};\n\nfunction joinClassNames(...values: Array<string | false | null | undefined>) {\n  return values.filter(Boolean).join(\" \");\n}\n\nfunction joinIds(...values: Array<string | undefined>) {\n  return values.filter(Boolean).join(\" \") || undefined;\n}\n\nexport const GummyTextarea = React.forwardRef<\n  HTMLTextAreaElement,\n  GummyTextareaProps\n>(function GummyTextarea(\n  {\n    label,\n    description,\n    errorMessage,\n    successMessage,\n    optional = false,\n    resize = \"vertical\",\n    showCount = false,\n    wrapperClassName,\n    className,\n    id: providedId,\n    required,\n    disabled,\n    readOnly,\n    value,\n    defaultValue,\n    maxLength,\n    onChange,\n    \"aria-describedby\": ariaDescribedBy,\n    \"aria-errormessage\": ariaErrorMessage,\n    \"aria-invalid\": ariaInvalid,\n    ...textareaProps\n  },\n  ref,\n) {\n  const generatedId = React.useId().replace(/:/g, \"\");\n  const id = providedId ?? `gummy-textarea-${generatedId}`;\n  const descriptionId = description ? `${id}-description` : undefined;\n  const errorId = errorMessage ? `${id}-error` : undefined;\n  const successId =\n    !errorMessage && successMessage ? `${id}-success` : undefined;\n  const countId = showCount ? `${id}-count` : undefined;\n  const status = errorMessage\n    ? \"error\"\n    : successMessage\n      ? \"success\"\n      : \"default\";\n  const [uncontrolledValue, setUncontrolledValue] = React.useState(() =>\n    defaultValue === undefined || defaultValue === null ? \"\" : String(defaultValue),\n  );\n  const currentValue = value === undefined ? uncontrolledValue : String(value);\n\n  return (\n    <div\n      className={joinClassNames(\"gummy-textarea\", wrapperClassName)}\n      data-status={status}\n      data-disabled={disabled || undefined}\n      data-read-only={readOnly || undefined}\n      data-filled={currentValue.length > 0 || undefined}\n      data-resize={resize}\n    >\n      <GummyLabel\n        htmlFor={id}\n        required={required}\n        optional={optional}\n        disabled={disabled}\n        readOnly={readOnly}\n      >\n        {label}\n      </GummyLabel>\n      <div className=\"gummy-textarea__shell\">\n        <span className=\"gummy-textarea__pool\" aria-hidden=\"true\" />\n        <textarea\n          {...textareaProps}\n          ref={ref}\n          id={id}\n          className={joinClassNames(\"gummy-textarea__control\", className)}\n          required={required}\n          disabled={disabled}\n          readOnly={readOnly}\n          value={value}\n          defaultValue={value === undefined ? defaultValue : undefined}\n          maxLength={maxLength}\n          aria-describedby={joinIds(\n            ariaDescribedBy,\n            descriptionId,\n            errorId,\n            successId,\n            countId,\n          )}\n          aria-errormessage={ariaErrorMessage ?? errorId}\n          aria-invalid={ariaInvalid ?? (errorMessage ? true : undefined)}\n          onChange={(event) => {\n            if (value === undefined) setUncontrolledValue(event.currentTarget.value);\n            onChange?.(event);\n          }}\n        />\n        {status !== \"default\" ? (\n          <span className=\"gummy-textarea__status\" aria-hidden=\"true\">\n            {status === \"error\" ? \"!\" : \"✓\"}\n          </span>\n        ) : null}\n      </div>\n      <div className=\"gummy-textarea__support\">\n        <div>\n          {description ? (\n            <p className=\"gummy-textarea__message\" id={descriptionId}>\n              {description}\n            </p>\n          ) : null}\n          {errorMessage ? (\n            <p\n              className=\"gummy-textarea__message gummy-textarea__message--error\"\n              id={errorId}\n              role=\"alert\"\n            >\n              <span className=\"gummy-form-message-mark\" aria-hidden=\"true\" />\n              <span>{errorMessage}</span>\n            </p>\n          ) : successId ? (\n            <p\n              className=\"gummy-textarea__message gummy-textarea__message--success\"\n              id={successId}\n            >\n              <span className=\"gummy-form-message-mark\" aria-hidden=\"true\" />\n              <span>{successMessage}</span>\n            </p>\n          ) : null}\n        </div>\n        {showCount ? (\n          <span className=\"gummy-textarea__count\" id={countId} aria-live=\"polite\">\n            {currentValue.length}\n            {maxLength ? ` / ${maxLength}` : null}\n          </span>\n        ) : null}\n      </div>\n    </div>\n  );\n});\n\nGummyTextarea.displayName = \"GummyTextarea\";\n"
    }
  ]
}
