{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "array",
  "title": "ArrayField",
  "description": "Repeatable array field for BuzzForm v2.",
  "dependencies": [
    "@dnd-kit/core",
    "@dnd-kit/sortable",
    "@dnd-kit/utilities"
  ],
  "registryDependencies": [
    "@buzzform/init",
    "card",
    "collapsible",
    "badge",
    "alert-dialog"
  ],
  "files": [
    {
      "path": "registry/shadcn/fields/array.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport type { ArrayFieldDef, CoreField } from \"@buildnbuzz/form-react\";\nimport {\n  useDataField,\n  RenderFields,\n  useNestedErrorCount,\n} from \"@buildnbuzz/form-react\";\nimport { resolveExpr } from \"@buildnbuzz/form-react\";\nimport {\n  DndContext,\n  closestCenter,\n  KeyboardSensor,\n  PointerSensor,\n  useSensor,\n  useSensors,\n  type DragEndEvent,\n} from \"@dnd-kit/core\";\nimport {\n  SortableContext,\n  sortableKeyboardCoordinates,\n  verticalListSortingStrategy,\n  useSortable,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\";\nimport { Card, CardContent, CardHeader } from \"@/components/ui/card\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n} from \"@/components/ui/alert-dialog\";\nimport { IconPlaceholder } from \"@/components/icon-placeholder\";\nimport {\n  Field,\n  FieldDescription,\n  FieldError,\n  FieldGroup,\n  FieldLegend,\n  FieldSet,\n} from \"@/components/ui/field\";\nimport { cn } from \"@/lib/utils\";\n\n// ---------------------------------------------------------------------------\n// UI options\n// ---------------------------------------------------------------------------\n\ninterface ArrayUi {\n  /** Visual style for the array container. Defaults to `\"default\"`. */\n  variant?: \"default\" | \"minimal\";\n  /** Row header label — use `{ $data: \"fieldName\" }` to show a field value. Falls back to `\"Item #N\"`. */\n  rowLabel?: unknown;\n  /** Enable drag-to-reorder. Defaults to `true`. */\n  isSortable?: boolean;\n  /** Show error-count badge in the container header. Defaults to `true`. */\n  showErrorBadge?: boolean;\n  /** Show a duplicate button per row. Defaults to `false`. */\n  allowDuplicate?: boolean;\n  /** Require confirmation before deleting all items. Defaults to `true`. */\n  confirmDelete?: boolean;\n  /** Text and label overrides. */\n  labels?: {\n    /** Label for the add button. Defaults to `\"Add Item\"`. */\n    add?: React.ReactNode;\n    /** Empty state message. Defaults to `\"No items added yet.\"`. */\n    empty?: React.ReactNode;\n    /** Fallback row label when dynamic path is missing or empty. Defaults to `\"Item #N\"`. */\n    rowFallback?: string;\n    /** Title for the delete confirmation dialog. */\n    confirmDeleteTitle?: React.ReactNode;\n    /** Description for the delete confirmation dialog. */\n    confirmDeleteDescription?: React.ReactNode;\n    /** Label for the cancel button in delete confirmation. */\n    confirmDeleteCancelLabel?: string;\n    /** Label for the confirm button in delete confirmation. */\n    confirmDeleteActionLabel?: string;\n  };\n  /** Controls required asterisk visibility. */\n  asterisk?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// ArrayItem props type\n// ---------------------------------------------------------------------------\n\ninterface ArrayItemProps {\n  id: string;\n  index: number;\n  field: ArrayFieldDef;\n  form: ReturnType<typeof useDataField>[\"form\"];\n  fieldApiName: string;\n  rowLabel: unknown;\n  rowFallback?: string;\n  contextData: unknown;\n  isSortable: boolean;\n  isDisabled: boolean;\n  isReadOnly: boolean;\n  canRemove: boolean;\n  canDuplicate: boolean;\n  allowDuplicate: boolean;\n  onRemove: (index: number) => void;\n  onDuplicate: (index: number) => void;\n}\n\n// ---------------------------------------------------------------------------\n// ArrayItem\n// ---------------------------------------------------------------------------\n\nfunction ArrayItem({\n  id,\n  index,\n  field,\n  form,\n  fieldApiName,\n  rowLabel,\n  rowFallback,\n  contextData,\n  isSortable,\n  isDisabled,\n  isReadOnly,\n  canRemove,\n  canDuplicate,\n  allowDuplicate,\n  onRemove,\n  onDuplicate,\n}: ArrayItemProps) {\n  const [isOpen, setIsOpen] = React.useState(true);\n  const rowPath = `${fieldApiName}.${index}`;\n\n  const rowData = form.getFieldValue(rowPath as never);\n  const rowFormData =\n    rowData && typeof rowData === \"object\"\n      ? (rowData as Record<string, unknown>)\n      : { value: rowData };\n\n  const defaultRowLabel = rowFallback ?? `Item ${index + 1}`;\n\n  const resolvedLabel = rowLabel\n    ? String(\n        resolveExpr(\n          rowLabel as string,\n          { data: rowFormData, context: (contextData ?? {}) as Record<string, unknown> },\n        ) ?? \"\",\n      ).trim() || defaultRowLabel\n    : field.primitive === true && rowData !== undefined && rowData !== null\n      ? String(rowData)\n      : defaultRowLabel;\n\n  const errorCount = useNestedErrorCount(field.fields as CoreField[], rowPath);\n\n  const {\n    attributes,\n    listeners,\n    setNodeRef,\n    transform,\n    transition,\n    isDragging,\n  } = useSortable({ id, disabled: !isSortable || isDisabled });\n\n  return (\n    <div\n      ref={setNodeRef}\n      style={{\n        transform: CSS.Transform.toString(transform),\n        transition,\n        opacity: isDragging ? 0.5 : 1,\n      }}\n    >\n      <Collapsible open={isOpen} onOpenChange={setIsOpen}>\n        <div className=\"bg-card border rounded-lg overflow-hidden\">\n          <CardHeader\n            className={cn(\n              \"flex flex-row items-center gap-2 px-3 py-2 bg-muted/50\",\n              isOpen && \"border-b\",\n            )}\n          >\n            {isSortable && !isDisabled && (\n              <span\n                {...attributes}\n                {...listeners}\n                className=\"cursor-grab active:cursor-grabbing text-muted-foreground hover:text-foreground shrink-0\"\n              >\n                <IconPlaceholder\n                  lucide=\"GripVertical\"\n                  hugeicons=\"DragDropIcon\"\n                  tabler=\"IconGripVertical\"\n                  phosphor=\"DotsSixVertical\"\n                  remixicon=\"RiDraggable\"\n                  className=\"size-4\"\n                />\n              </span>\n            )}\n\n            <CollapsibleTrigger className=\"flex items-center gap-2 flex-1 min-w-0 text-left cursor-pointer\">\n              <IconPlaceholder\n                lucide=\"ChevronDown\"\n                hugeicons=\"ArrowDown01Icon\"\n                tabler=\"IconChevronDown\"\n                phosphor=\"CaretDown\"\n                remixicon=\"RiArrowDownSLine\"\n                className={cn(\n                  \"size-3.5 text-muted-foreground transition-transform duration-200 shrink-0\",\n                  !isOpen && \"-rotate-90\",\n                )}\n              />\n              <span className=\"text-sm font-medium truncate\">\n                {resolvedLabel}\n              </span>\n              {errorCount > 0 && (\n                <Badge\n                  variant=\"destructive\"\n                  className=\"h-5 px-1.5 text-xs ml-1 shrink-0\"\n                >\n                  {errorCount}\n                </Badge>\n              )}\n            </CollapsibleTrigger>\n\n            {!isReadOnly && (\n              <div className=\"flex items-center gap-0.5 shrink-0\">\n                {allowDuplicate && (\n                  <Button\n                    type=\"button\"\n                    variant=\"ghost\"\n                    size=\"icon\"\n                    className=\"h-7 w-7\"\n                    onClick={() => onDuplicate(index)}\n                    disabled={!canDuplicate || isDisabled}\n                    title=\"Duplicate\"\n                  >\n                    <IconPlaceholder\n                      lucide=\"Copy\"\n                      hugeicons=\"Copy01Icon\"\n                      tabler=\"IconCopy\"\n                      phosphor=\"Copy\"\n                      remixicon=\"RiFileCopyLine\"\n                      className=\"size-3.5\"\n                    />\n                  </Button>\n                )}\n                <Button\n                  type=\"button\"\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  className=\"h-7 w-7 text-muted-foreground hover:text-destructive\"\n                  onClick={() => onRemove(index)}\n                  disabled={!canRemove || isDisabled}\n                  title=\"Remove\"\n                >\n                  <IconPlaceholder\n                    lucide=\"Trash2\"\n                    hugeicons=\"Delete02Icon\"\n                    tabler=\"IconTrash\"\n                    phosphor=\"Trash\"\n                    remixicon=\"RiDeleteBinLine\"\n                    className=\"size-3.5\"\n                  />\n                </Button>\n              </div>\n            )}\n          </CardHeader>\n\n          <CollapsibleContent>\n            <div className=\"p-4 flex flex-col gap-4\">\n              <RenderFields\n                fields={field.fields as CoreField[]}\n                form={form}\n                basePath={rowPath}\n              />\n            </div>\n          </CollapsibleContent>\n        </div>\n      </Collapsible>\n    </div>\n  );\n}\n\nfunction MinimalArrayItem({\n  id,\n  index,\n  field,\n  form,\n  fieldApiName,\n  rowLabel,\n  rowFallback,\n  contextData,\n  isSortable,\n  isDisabled,\n  isReadOnly,\n  canRemove,\n  canDuplicate,\n  allowDuplicate,\n  onRemove,\n  onDuplicate,\n}: ArrayItemProps) {\n  const rowPath = `${fieldApiName}.${index}`;\n  const rowData = form.getFieldValue(rowPath as never);\n  const rowFormData =\n    rowData && typeof rowData === \"object\"\n      ? (rowData as Record<string, unknown>)\n      : { value: rowData };\n\n  const defaultRowLabel = rowFallback ?? `Item ${index + 1}`;\n\n  const resolvedLabel = rowLabel\n    ? String(\n        resolveExpr(\n          rowLabel as string,\n          { data: rowFormData, context: (contextData ?? {}) as Record<string, unknown> },\n        ) ?? \"\",\n      ).trim() || defaultRowLabel\n    : \"\";\n\n  const {\n    attributes,\n    listeners,\n    setNodeRef,\n    transform,\n    transition,\n    isDragging,\n  } = useSortable({ id, disabled: !isSortable || isDisabled });\n\n  return (\n    <div\n      ref={setNodeRef}\n      style={{\n        transform: CSS.Transform.toString(transform),\n        transition,\n        opacity: isDragging ? 0.5 : 1,\n      }}\n      className=\"flex w-full items-start gap-3 py-1 group/array-item\"\n    >\n      <div className=\"flex-1 min-w-0\">\n        {resolvedLabel && field.primitive !== true && (\n          <p className=\"text-xs text-muted-foreground mb-2\">{resolvedLabel}</p>\n        )}\n        <div\n          className={cn(\n            \"flex flex-col gap-4 w-full\",\n            field.primitive === true &&\n              \"**:data-[slot=field-label]:hidden **:data-[slot=field-description]:hidden\",\n          )}\n        >\n          <RenderFields\n            fields={field.fields as CoreField[]}\n            form={form}\n            basePath={rowPath}\n          />\n        </div>\n      </div>\n\n      {!isReadOnly && (\n        <div\n          className={cn(\n            \"flex items-center gap-1 shrink-0\",\n            field.primitive === true ? \"mt-0.5\" : \"mt-7\",\n          )}\n        >\n          {isSortable && !isDisabled && (\n            <Button\n              type=\"button\"\n              variant=\"ghost\"\n              size=\"icon\"\n              className=\"h-7 w-7\"\n              {...attributes}\n              {...listeners}\n              title=\"Drag\"\n            >\n              <IconPlaceholder\n                lucide=\"GripVertical\"\n                hugeicons=\"DragDropIcon\"\n                tabler=\"IconGripVertical\"\n                phosphor=\"DotsSixVertical\"\n                remixicon=\"RiDraggable\"\n                className=\"size-3.5\"\n              />\n            </Button>\n          )}\n          {allowDuplicate && (\n            <Button\n              type=\"button\"\n              variant=\"ghost\"\n              size=\"icon\"\n              className=\"h-7 w-7\"\n              onClick={() => onDuplicate(index)}\n              disabled={!canDuplicate || isDisabled}\n              title=\"Duplicate\"\n            >\n              <IconPlaceholder\n                lucide=\"Copy\"\n                hugeicons=\"Copy01Icon\"\n                tabler=\"IconCopy\"\n                phosphor=\"Copy\"\n                remixicon=\"RiFileCopyLine\"\n                className=\"size-3.5\"\n              />\n            </Button>\n          )}\n          <Button\n            type=\"button\"\n            variant=\"ghost\"\n            size=\"icon\"\n            className=\"h-7 w-7 text-muted-foreground hover:text-destructive\"\n            onClick={() => onRemove(index)}\n            disabled={!canRemove || isDisabled}\n            title=\"Remove\"\n          >\n            <IconPlaceholder\n              lucide=\"Trash2\"\n              hugeicons=\"Delete02Icon\"\n              tabler=\"IconTrash\"\n              phosphor=\"Trash\"\n              remixicon=\"RiDeleteBinLine\"\n              className=\"size-3.5\"\n            />\n          </Button>\n        </div>\n      )}\n    </div>\n  );\n}\n\ninterface ArrayFieldState {\n  fieldApi: ReturnType<typeof useDataField>[\"fieldApi\"];\n  field: ArrayFieldDef;\n  form: ReturnType<typeof useDataField>[\"form\"];\n  isDisabled: boolean;\n  isReadOnly: boolean;\n  isRequired: boolean;\n  label?: React.ReactNode;\n  description?: React.ReactNode;\n  errors: ReturnType<typeof useDataField>[\"errors\"];\n  isInvalid: boolean;\n  descriptionId?: string;\n  errorId?: string;\n  contextData: unknown;\n}\n\nfunction DefaultArrayField(state: ArrayFieldState) {\n  const {\n    fieldApi,\n    field,\n    form,\n    isDisabled,\n    isReadOnly,\n    isRequired,\n    label,\n    description,\n    errors,\n    isInvalid,\n    descriptionId,\n    errorId,\n    contextData,\n  } = state;\n\n  const ui = field.ui as ArrayUi | undefined;\n  const labels = ui?.labels;\n\n  const addLabel = labels?.add ?? \"Add Item\";\n  const emptyMessage = labels?.empty ?? \"No items added yet.\";\n  const isSortable = ui?.isSortable !== false;\n  const showErrorBadge = ui?.showErrorBadge !== false;\n  const allowDuplicate = ui?.allowDuplicate ?? false;\n  const confirmDelete = ui?.confirmDelete !== false;\n\n  const items = Array.isArray(fieldApi.state.value) ? fieldApi.state.value : [];\n\n  const confirmDeleteTitle = labels?.confirmDeleteTitle ?? \"Delete all items?\";\n  const confirmDeleteDescription =\n    labels?.confirmDeleteDescription ??\n    (items.length === 1\n      ? \"This will remove the item. This cannot be undone.\"\n      : `This will remove all ${items.length} items. This cannot be undone.`);\n  const confirmDeleteCancelLabel = labels?.confirmDeleteCancelLabel ?? \"Cancel\";\n  const confirmDeleteActionLabel =\n    labels?.confirmDeleteActionLabel ?? \"Delete All\";\n\n  const [showDeleteAllDialog, setShowDeleteAllDialog] = React.useState(false);\n\n  const canAddMore =\n    field.maxItems === undefined || items.length < field.maxItems;\n  const canRemoveAny =\n    field.minItems === undefined || items.length > field.minItems;\n\n  // Stable DnD IDs keyed by position — sufficient since we reorder in-place\n  const dndId = React.useId();\n  const itemIds = items.map((_, i) => `${fieldApi.name}-${i}`);\n\n  const sensors = useSensors(\n    useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),\n    useSensor(KeyboardSensor, {\n      coordinateGetter: sortableKeyboardCoordinates,\n    }),\n  );\n\n  const handleDragEnd = ({ active, over }: DragEndEvent) => {\n    if (!over || active.id === over.id) return;\n    const from = itemIds.indexOf(active.id as string);\n    const to = itemIds.indexOf(over.id as string);\n    if (from !== -1 && to !== -1) fieldApi.moveValue(from, to);\n  };\n\n  const handleAdd = () => {\n    if (!isDisabled && !isReadOnly && canAddMore)\n      fieldApi.pushValue(undefined as never);\n  };\n\n  const handleRemove = (index: number) => fieldApi.removeValue(index);\n\n  const handleDuplicate = (index: number) => {\n    if (canAddMore) fieldApi.insertValue(index + 1, items[index] as never);\n  };\n\n  const handleDeleteAll = () => {\n    if (confirmDelete) {\n      setShowDeleteAllDialog(true);\n    } else {\n      for (let i = items.length - 1; i >= 0; i--) fieldApi.removeValue(i);\n    }\n  };\n\n  const totalErrorCount = useNestedErrorCount(\n    field.fields as CoreField[],\n    fieldApi.name,\n  );\n  const isEffectivelyRequired =\n    isRequired || (typeof field.minItems === \"number\" && field.minItems >= 1);\n\n  return (\n    <>\n      <AlertDialog\n        open={showDeleteAllDialog}\n        onOpenChange={setShowDeleteAllDialog}\n      >\n        <AlertDialogContent size=\"sm\">\n          <AlertDialogHeader>\n            <AlertDialogTitle>{confirmDeleteTitle}</AlertDialogTitle>\n            <AlertDialogDescription>\n              {confirmDeleteDescription}\n            </AlertDialogDescription>\n          </AlertDialogHeader>\n          <AlertDialogFooter>\n            <AlertDialogCancel>{confirmDeleteCancelLabel}</AlertDialogCancel>\n            <AlertDialogAction\n              variant=\"destructive\"\n              onClick={() => {\n                for (let i = items.length - 1; i >= 0; i--)\n                  fieldApi.removeValue(i);\n                setShowDeleteAllDialog(false);\n              }}\n            >\n              {confirmDeleteActionLabel}\n            </AlertDialogAction>\n          </AlertDialogFooter>\n        </AlertDialogContent>\n      </AlertDialog>\n\n      <FieldGroup data-field={fieldApi.name}>\n        <Field data-invalid={isInvalid} data-disabled={isDisabled}>\n          <FieldSet disabled={isDisabled} className=\"contents\">\n            <Card className=\"py-0 gap-0\">\n              {/* Header */}\n              <CardHeader className=\"px-4 py-3 flex flex-row items-center justify-between bg-muted/50 border-b gap-3\">\n                <div className=\"flex items-center gap-2 min-w-0 flex-1\">\n                  {label && (\n                    <FieldLegend className=\"text-sm font-semibold truncate mb-0\">\n                      {label}\n                      {isEffectivelyRequired && ui?.asterisk !== false && (\n                        <span className=\"text-destructive ml-1\">*</span>\n                      )}\n                    </FieldLegend>\n                  )}\n                  {items.length > 0 && (\n                    <Badge\n                      variant=\"secondary\"\n                      className=\"h-5 px-1.5 text-xs shrink-0\"\n                    >\n                      {items.length}\n                    </Badge>\n                  )}\n                  {showErrorBadge && totalErrorCount > 0 && (\n                    <Badge\n                      variant=\"destructive\"\n                      className=\"h-5 px-1.5 text-xs shrink-0\"\n                    >\n                      {totalErrorCount}\n                    </Badge>\n                  )}\n                </div>\n\n                {!isReadOnly && (\n                  <div className=\"flex items-center gap-1 shrink-0\">\n                    {items.length > 0 && canRemoveAny && (\n                      <Button\n                        type=\"button\"\n                        variant=\"ghost\"\n                        size=\"icon\"\n                        className=\"h-7 w-7 text-muted-foreground hover:text-destructive\"\n                        onClick={handleDeleteAll}\n                        disabled={isDisabled}\n                        title=\"Delete all\"\n                      >\n                        <IconPlaceholder\n                          lucide=\"Trash2\"\n                          hugeicons=\"Delete02Icon\"\n                          tabler=\"IconTrash\"\n                          phosphor=\"Trash\"\n                          remixicon=\"RiDeleteBinLine\"\n                          className=\"size-4\"\n                        />\n                      </Button>\n                    )}\n                    <Button\n                      type=\"button\"\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      className=\"h-7 w-7\"\n                      onClick={handleAdd}\n                      disabled={!canAddMore || isDisabled}\n                      title={typeof addLabel === \"string\" ? addLabel : \"Add\"}\n                    >\n                      <IconPlaceholder\n                        lucide=\"Plus\"\n                        hugeicons=\"Add01Icon\"\n                        tabler=\"IconPlus\"\n                        phosphor=\"Plus\"\n                        remixicon=\"RiAddLine\"\n                        className=\"size-4\"\n                      />\n                    </Button>\n                  </div>\n                )}\n              </CardHeader>\n\n              <CardContent className=\"p-4\">\n                {description && !isInvalid && (\n                  <FieldDescription id={descriptionId} className=\"mb-4\">\n                    {description}\n                  </FieldDescription>\n                )}\n                {isInvalid && (\n                  <FieldError id={errorId} errors={errors} className=\"mb-4\" />\n                )}\n\n                {items.length === 0 ? (\n                  <div className=\"flex flex-col items-center justify-center py-10 border-2 border-dashed rounded-lg\">\n                    <p className=\"text-sm text-muted-foreground mb-3\">\n                      {emptyMessage}\n                    </p>\n                    {!isReadOnly && (\n                      <Button\n                        type=\"button\"\n                        variant=\"outline\"\n                        size=\"sm\"\n                        onClick={handleAdd}\n                        disabled={!canAddMore || isDisabled}\n                      >\n                        <IconPlaceholder\n                          lucide=\"Plus\"\n                          hugeicons=\"Add01Icon\"\n                          tabler=\"IconPlus\"\n                          phosphor=\"Plus\"\n                          remixicon=\"RiAddLine\"\n                          className=\"size-4 mr-1.5\"\n                        />\n                        {addLabel}\n                      </Button>\n                    )}\n                  </div>\n                ) : (\n                  <>\n                    <DndContext\n                      id={dndId}\n                      sensors={sensors}\n                      collisionDetection={closestCenter}\n                      onDragEnd={handleDragEnd}\n                    >\n                      <SortableContext\n                        items={itemIds}\n                        strategy={verticalListSortingStrategy}\n                      >\n                        <div className=\"flex flex-col gap-3\">\n                          {items.map((_, index) => (\n                            <ArrayItem\n                              key={itemIds[index]}\n                              id={itemIds[index]!}\n                              index={index}\n                              field={field}\n                              form={form}\n                              fieldApiName={fieldApi.name}\n                              rowLabel={ui?.rowLabel}\n                              rowFallback={labels?.rowFallback}\n                              contextData={contextData}\n                              isSortable={isSortable}\n                              isDisabled={isDisabled}\n                              isReadOnly={isReadOnly}\n                              canRemove={canRemoveAny}\n                              canDuplicate={canAddMore}\n                              allowDuplicate={allowDuplicate}\n                              onRemove={handleRemove}\n                              onDuplicate={handleDuplicate}\n                            />\n                          ))}\n                        </div>\n                      </SortableContext>\n                    </DndContext>\n\n                    {!isReadOnly && (\n                      <Button\n                        type=\"button\"\n                        variant=\"outline\"\n                        className=\"w-full mt-4 border-dashed text-muted-foreground hover:text-foreground\"\n                        onClick={handleAdd}\n                        disabled={!canAddMore || isDisabled}\n                      >\n                        <IconPlaceholder\n                          lucide=\"Plus\"\n                          hugeicons=\"Add01Icon\"\n                          tabler=\"IconPlus\"\n                          phosphor=\"Plus\"\n                          remixicon=\"RiAddLine\"\n                          className=\"size-4 mr-2\"\n                        />\n                        {addLabel}\n                      </Button>\n                    )}\n                  </>\n                )}\n              </CardContent>\n            </Card>\n          </FieldSet>\n        </Field>\n      </FieldGroup>\n    </>\n  );\n}\n\nfunction MinimalArrayField(state: ArrayFieldState) {\n  const {\n    fieldApi,\n    field,\n    form,\n    isDisabled,\n    isReadOnly,\n    isRequired,\n    label,\n    description,\n    errors,\n    isInvalid,\n    descriptionId,\n    errorId,\n    contextData,\n  } = state;\n\n  const ui = field.ui as ArrayUi | undefined;\n  const labels = ui?.labels;\n\n  const addLabel = labels?.add ?? \"Add Item\";\n  const emptyMessage = labels?.empty ?? \"No items added yet.\";\n  const isSortable = ui?.isSortable !== false;\n  const showErrorBadge = ui?.showErrorBadge !== false;\n  const allowDuplicate = ui?.allowDuplicate ?? false;\n\n  const items = Array.isArray(fieldApi.state.value) ? fieldApi.state.value : [];\n\n  const canAddMore =\n    field.maxItems === undefined || items.length < field.maxItems;\n  const canRemoveAny =\n    field.minItems === undefined || items.length > field.minItems;\n\n  // Stable DnD IDs keyed by position — sufficient since we reorder in-place\n  const dndId = React.useId();\n  const itemIds = items.map((_, i) => `${fieldApi.name}-${i}`);\n\n  const sensors = useSensors(\n    useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),\n    useSensor(KeyboardSensor, {\n      coordinateGetter: sortableKeyboardCoordinates,\n    }),\n  );\n\n  const handleDragEnd = ({ active, over }: DragEndEvent) => {\n    if (!over || active.id === over.id) return;\n    const from = itemIds.indexOf(active.id as string);\n    const to = itemIds.indexOf(over.id as string);\n    if (from !== -1 && to !== -1) fieldApi.moveValue(from, to);\n  };\n\n  const handleAdd = () => {\n    if (!isDisabled && !isReadOnly && canAddMore)\n      fieldApi.pushValue(undefined as never);\n  };\n\n  const handleRemove = (index: number) => fieldApi.removeValue(index);\n\n  const handleDuplicate = (index: number) => {\n    if (canAddMore) fieldApi.insertValue(index + 1, items[index] as never);\n  };\n\n  const totalErrorCount = useNestedErrorCount(\n    field.fields as CoreField[],\n    fieldApi.name,\n  );\n  const isEffectivelyRequired =\n    isRequired || (typeof field.minItems === \"number\" && field.minItems >= 1);\n\n  return (\n    <FieldGroup data-field={fieldApi.name}>\n      <Field data-invalid={isInvalid} data-disabled={isDisabled}>\n        <FieldSet disabled={isDisabled} className=\"flex flex-col gap-4\">\n          <div className=\"flex items-center gap-2\">\n            {label && (\n              <FieldLegend className=\"text-sm font-semibold mb-0\">\n                {label}\n                {isEffectivelyRequired && ui?.asterisk !== false && (\n                  <span className=\"text-destructive ml-1\">*</span>\n                )}\n              </FieldLegend>\n            )}\n            {items.length > 0 && (\n              <Badge variant=\"secondary\" className=\"h-5 px-1.5 text-xs\">\n                {items.length}\n              </Badge>\n            )}\n            {showErrorBadge && totalErrorCount > 0 && (\n              <Badge variant=\"destructive\" className=\"h-5 px-1.5 text-xs\">\n                {totalErrorCount}\n              </Badge>\n            )}\n            {!isReadOnly && (\n              <div className=\"ml-auto flex items-center gap-1\">\n                <Button\n                  type=\"button\"\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  className=\"h-7 w-7\"\n                  onClick={handleAdd}\n                  disabled={!canAddMore || isDisabled}\n                  title={typeof addLabel === \"string\" ? addLabel : \"Add Item\"}\n                >\n                  <IconPlaceholder\n                    lucide=\"Plus\"\n                    hugeicons=\"Add01Icon\"\n                    tabler=\"IconPlus\"\n                    phosphor=\"Plus\"\n                    remixicon=\"RiAddLine\"\n                    className=\"size-4\"\n                  />\n                </Button>\n              </div>\n            )}\n          </div>\n\n          {description && !isInvalid && (\n            <FieldDescription id={descriptionId}>\n              {description}\n            </FieldDescription>\n          )}\n          {isInvalid && <FieldError id={errorId} errors={errors} />}\n\n          {items.length === 0 ? (\n            <div className=\"flex items-center justify-between border border-dashed rounded-lg px-3 py-3\">\n              <p className=\"text-sm text-muted-foreground\">{emptyMessage}</p>\n              {!isReadOnly && (\n                <Button\n                  type=\"button\"\n                  variant=\"outline\"\n                  size=\"sm\"\n                  onClick={handleAdd}\n                  disabled={!canAddMore || isDisabled}\n                >\n                  <IconPlaceholder\n                    lucide=\"Plus\"\n                    hugeicons=\"Add01Icon\"\n                    tabler=\"IconPlus\"\n                    phosphor=\"Plus\"\n                    remixicon=\"RiAddLine\"\n                    className=\"size-4 mr-1.5\"\n                  />\n                  {addLabel}\n                </Button>\n              )}\n            </div>\n          ) : (\n            <DndContext\n              id={dndId}\n              sensors={sensors}\n              collisionDetection={closestCenter}\n              onDragEnd={handleDragEnd}\n            >\n              <SortableContext\n                items={itemIds}\n                strategy={verticalListSortingStrategy}\n              >\n                <div className=\"flex flex-col gap-3\">\n                  {items.map((_, index) => (\n                    <MinimalArrayItem\n                      key={itemIds[index]}\n                      id={itemIds[index]!}\n                      index={index}\n                      field={field}\n                      form={form}\n                      fieldApiName={fieldApi.name}\n                      rowLabel={ui?.rowLabel}\n                      rowFallback={labels?.rowFallback}\n                      contextData={contextData}\n                      isSortable={isSortable}\n                      isDisabled={isDisabled}\n                      isReadOnly={isReadOnly}\n                      canRemove={canRemoveAny}\n                      canDuplicate={canAddMore}\n                      allowDuplicate={allowDuplicate}\n                      onRemove={handleRemove}\n                      onDuplicate={handleDuplicate}\n                    />\n                  ))}\n                </div>\n              </SortableContext>\n            </DndContext>\n          )}\n        </FieldSet>\n      </Field>\n    </FieldGroup>\n  );\n}\n\nexport function ArrayField() {\n  const {\n    fieldApi,\n    field,\n    form,\n    isDisabled,\n    isReadOnly,\n    isRequired,\n    label,\n    description,\n    errors,\n    isInvalid,\n    descriptionId,\n    errorId,\n    contextData,\n  } = useDataField<ArrayFieldDef>();\n\n  const state: ArrayFieldState = {\n    fieldApi,\n    field,\n    form,\n    isDisabled,\n    isReadOnly,\n    isRequired,\n    label,\n    description,\n    errors,\n    isInvalid,\n    descriptionId,\n    errorId,\n    contextData,\n  };\n\n  const ui = field.ui as ArrayUi | undefined;\n  const variant = ui?.variant ?? \"default\";\n\n  if (variant === \"minimal\") return <MinimalArrayField {...state} />;\n  return <DefaultArrayField {...state} />;\n}\n",
      "type": "registry:component",
      "target": "components/buzzform/fields/array.tsx"
    }
  ],
  "type": "registry:ui"
}