{
  "$schema": "https://cubixflow.ir/schema/registry-item.json",
  "name": "text-field",
  "type": "registry:ui",
  "title": "Text Field",
  "description": "A labeled text input with description and error message for accessible forms.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/cubix/text-field.tsx",
      "type": "registry:ui",
      "target": "components/cubix/text-field.tsx",
      "content": "\"use client\"\n\n/*\n  Cubix Text Field - Persian-first labeled text input.\n\n  Weight 400 and tracking-normal keep IRANSans XV readable in fixed-height\n  controls; Latin letter-spacing and medium weight look heavy on Arab script.\n  Icons use data-icon=\"inline-start\" | \"inline-end\" like Button (logical inset).\n*/\nimport * as React from \"react\"\nimport { Field as FieldPrimitive } from \"@base-ui/react/field\"\nimport { Input as InputPrimitive } from \"@base-ui/react/input\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\n/*\n  Icon-side inline padding matches Button / vertical icon inset:\n  default: 8px · lg: 12px\n*/\n\nconst textFieldControlVariants = cva(\n  \"group/text-field-control relative flex w-full min-w-0 items-center rounded-lg border border-input bg-transparent transition-colors outline-none has-disabled:cursor-not-allowed has-disabled:border-transparent has-disabled:bg-muted has-[[data-slot=text-field-input]:focus-visible]:border-primary has-[[data-slot=text-field-input]:focus-visible]:ring-3 has-[[data-slot=text-field-input]:focus-visible]:ring-primary/20 has-[[data-slot=text-field-input]:focus-visible]:ring-offset-1 has-[[data-slot=text-field-input]:focus-visible]:ring-offset-background has-[[data-slot=text-field-input][aria-invalid=true]]:border-destructive has-[[data-slot=text-field-input][aria-invalid=true]]:ring-0 has-[[data-slot=text-field-input][aria-invalid=true]:focus-visible]:border-destructive has-[[data-slot=text-field-input][aria-invalid=true]:focus-visible]:ring-0 dark:bg-input/30 dark:has-disabled:bg-muted dark:has-[[data-slot=text-field-input][aria-invalid=true]]:border-destructive/50 dark:has-[[data-slot=text-field-input][aria-invalid=true]:focus-visible]:border-destructive/50 dark:has-[[data-slot=text-field-input][aria-invalid=true]:focus-visible]:ring-0 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:text-muted-foreground [&_svg:not([class*='size-'])]:size-4\",\n  {\n    variants: {\n      size: {\n        default:\n          \"h-10 gap-2 px-3 has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2 [&_svg:not([class*='size-'])]:size-6\",\n        lg: \"h-12 gap-2.5 px-3.5 has-data-[icon=inline-end]:pe-3 has-data-[icon=inline-start]:ps-3 [&_svg:not([class*='size-'])]:size-6\",\n      },\n    },\n    defaultVariants: {\n      size: \"default\",\n    },\n  }\n)\n\nconst textFieldInputVariants = cva(\n  \"w-full min-w-0 rounded-lg border border-input bg-transparent font-normal leading-none tracking-normal text-foreground transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-description file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-primary focus-visible:ring-3 focus-visible:ring-primary/20 focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:border-transparent disabled:bg-muted disabled:text-muted-foreground disabled:placeholder:text-muted-foreground aria-invalid:border-destructive aria-invalid:ring-0 aria-invalid:focus-visible:border-destructive aria-invalid:focus-visible:ring-0 dark:bg-input/30 dark:disabled:bg-muted dark:aria-invalid:border-destructive/50 dark:aria-invalid:focus-visible:border-destructive/50\",\n  {\n    variants: {\n      size: {\n        default: \"h-10 px-3 text-label md:text-label\",\n        lg: \"h-12 px-3.5 text-label md:text-label\",\n      },\n      inControl: {\n        true: \"h-full flex-1 rounded-none border-0 bg-transparent p-0 shadow-none ring-0 focus-visible:border-0 focus-visible:ring-0 focus-visible:ring-offset-0 aria-invalid:border-0 aria-invalid:ring-0 aria-invalid:ring-offset-0 disabled:bg-transparent dark:bg-transparent dark:disabled:bg-transparent\",\n        false: \"\",\n      },\n    },\n    defaultVariants: {\n      size: \"default\",\n      inControl: false,\n    },\n  }\n)\n\ntype TextFieldSize = NonNullable<\n  VariantProps<typeof textFieldInputVariants>[\"size\"]\n>\n\nconst TextFieldSizeContext = React.createContext<TextFieldSize>(\"default\")\nconst TextFieldControlContext = React.createContext(false)\n\nfunction TextField({\n  className,\n  size = \"default\",\n  ...props\n}: React.ComponentProps<typeof FieldPrimitive.Root> & {\n  size?: TextFieldSize\n}) {\n  return (\n    <TextFieldSizeContext.Provider value={size}>\n      <FieldPrimitive.Root\n        data-slot=\"text-field\"\n        data-size={size}\n        className={cn(\n          \"group/text-field flex w-full flex-col gap-2 data-disabled:cursor-not-allowed data-disabled:opacity-70\",\n          className\n        )}\n        {...props}\n      />\n    </TextFieldSizeContext.Provider>\n  )\n}\n\nfunction TextFieldLabel({\n  className,\n  ...props\n}: React.ComponentProps<typeof FieldPrimitive.Label>) {\n  return (\n    <FieldPrimitive.Label\n      data-slot=\"text-field-label\"\n      className={cn(\n        \"flex w-fit items-center gap-2 text-label leading-none font-normal text-foreground select-none group-data-disabled/text-field:cursor-not-allowed group-data-disabled/text-field:text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TextFieldControl({\n  className,\n  size: sizeProp,\n  onClick,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  size?: TextFieldSize\n}) {\n  const contextSize = React.useContext(TextFieldSizeContext)\n  const size = sizeProp ?? contextSize\n\n  return (\n    <TextFieldControlContext.Provider value={true}>\n      <div\n        data-slot=\"text-field-control\"\n        data-size={size}\n        className={cn(textFieldControlVariants({ size }), className)}\n        onClick={(event) => {\n          onClick?.(event)\n          if (event.defaultPrevented) return\n          if ((event.target as HTMLElement).closest(\"button,a,input,textarea\")) {\n            return\n          }\n          const input = event.currentTarget.querySelector<HTMLInputElement>(\n            \"[data-slot=text-field-input]\"\n          )\n          if (!input || input.disabled || input.readOnly) return\n          input.focus()\n        }}\n        {...props}\n      />\n    </TextFieldControlContext.Provider>\n  )\n}\n\nfunction TextFieldInput({\n  className,\n  size: sizeProp,\n  ...props\n}: Omit<React.ComponentProps<typeof InputPrimitive>, \"size\"> & {\n  size?: TextFieldSize\n}) {\n  const contextSize = React.useContext(TextFieldSizeContext)\n  const size = sizeProp ?? contextSize\n  const inControl = React.useContext(TextFieldControlContext)\n\n  return (\n    <InputPrimitive\n      data-slot=\"text-field-input\"\n      data-size={size}\n      className={cn(\n        textFieldInputVariants({ size, inControl }),\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction clearTextFieldInput(input: HTMLInputElement) {\n  const descriptor = Object.getOwnPropertyDescriptor(\n    HTMLInputElement.prototype,\n    \"value\"\n  )\n  descriptor?.set?.call(input, \"\")\n  input.dispatchEvent(new Event(\"input\", { bubbles: true }))\n  input.dispatchEvent(new Event(\"change\", { bubbles: true }))\n}\n\nfunction TextFieldClearIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"currentColor\"\n      aria-hidden=\"true\"\n      className={className}\n    >\n      <path d=\"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z\" />\n    </svg>\n  )\n}\n\nfunction TextFieldClear({\n  className,\n  onClick,\n  \"aria-label\": ariaLabel = \"Clear\",\n  ...props\n}: React.ComponentProps<\"button\">) {\n  const ref = React.useRef<HTMLButtonElement>(null)\n  const [show, setShow] = React.useState(false)\n\n  React.useLayoutEffect(() => {\n    const control = ref.current?.closest(\"[data-slot=text-field-control]\")\n    const input = control?.querySelector<HTMLInputElement>(\n      \"[data-slot=text-field-input]\"\n    )\n    if (!input) return\n\n    const sync = () => {\n      setShow(Boolean(input.value) && !input.disabled && !input.readOnly)\n    }\n    sync()\n    input.addEventListener(\"input\", sync)\n    input.addEventListener(\"change\", sync)\n    return () => {\n      input.removeEventListener(\"input\", sync)\n      input.removeEventListener(\"change\", sync)\n    }\n  }, [])\n\n  return (\n    <button\n      ref={ref}\n      type=\"button\"\n      data-slot=\"text-field-clear\"\n      data-icon={show ? \"inline-end\" : undefined}\n      aria-label={ariaLabel}\n      tabIndex={show ? 0 : -1}\n      hidden={!show}\n      className={cn(\n        \"inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-primary/20 disabled:pointer-events-none\",\n        className\n      )}\n      onClick={(event) => {\n        onClick?.(event)\n        if (event.defaultPrevented) return\n        const input = event.currentTarget\n          .closest(\"[data-slot=text-field-control]\")\n          ?.querySelector<HTMLInputElement>(\"[data-slot=text-field-input]\")\n        if (!input) return\n        clearTextFieldInput(input)\n        setShow(false)\n        input.focus()\n      }}\n      {...props}\n    >\n      <TextFieldClearIcon className=\"size-4\" />\n    </button>\n  )\n}\n\nfunction TextFieldDescription({\n  className,\n  ...props\n}: React.ComponentProps<typeof FieldPrimitive.Description>) {\n  return (\n    <FieldPrimitive.Description\n      data-slot=\"text-field-description\"\n      className={cn(\n        \"m-0 text-caption font-normal text-muted-foreground group-data-disabled/text-field:cursor-not-allowed\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TextFieldError({\n  className,\n  children,\n  match,\n  ...props\n}: React.ComponentProps<typeof FieldPrimitive.Error>) {\n  return (\n    <FieldPrimitive.Validity>\n      {(validity) => {\n        const isInvalid = validity.validity.valid === false\n        if (!isInvalid && match !== true) {\n          return null\n        }\n\n        return (\n          <FieldPrimitive.Error\n            data-slot=\"text-field-error\"\n            match={match ?? true}\n            className={cn(\n              \"m-0 text-caption font-normal text-destructive\",\n              className\n            )}\n            {...props}\n          >\n            {children}\n          </FieldPrimitive.Error>\n        )\n      }}\n    </FieldPrimitive.Validity>\n  )\n}\n\nexport {\n  TextField,\n  TextFieldLabel,\n  TextFieldControl,\n  TextFieldInput,\n  TextFieldClear,\n  TextFieldDescription,\n  TextFieldError,\n  textFieldControlVariants,\n  textFieldInputVariants,\n}\n"
    }
  ]
}

