import { Plus, Trash } from "lucide-react"; import type * as React from "react"; import { useTranslation } from "react-i18next"; import { SendouButton } from "~/components/elements/Button"; import { FormMessage } from "~/components/FormMessage"; import type { FormFieldProps } from "../types"; import styles from "./ArrayFormField.module.css"; import { useTranslatedTexts } from "./FormFieldWrapper"; type ArrayFormFieldProps = Omit, "field"> & { name: string; value: unknown[]; onChange: (value: unknown[]) => void; renderItem: (index: number, name: string) => React.ReactNode; isObjectArray?: boolean; sortable?: boolean; itemInitialValue?: unknown; }; export function ArrayFormField({ label, name, bottomText, error, min = 0, max, value, onChange, renderItem, isObjectArray, sortable, itemInitialValue, }: ArrayFormFieldProps) { const { t } = useTranslation(["common"]); const { translatedLabel, translatedBottomText, translatedError } = useTranslatedTexts({ label, bottomText, error }); const count = value.length; const handleAdd = () => { const baseValue = itemInitialValue !== undefined ? itemInitialValue : isObjectArray ? {} : undefined; const newItemValue = typeof baseValue === "object" && baseValue !== null ? { ...(baseValue as Record), _key: crypto.randomUUID(), } : baseValue; onChange([...value, newItemValue]); }; const handleRemoveAt = (index: number) => { onChange(value.filter((_, i) => i !== index)); }; const itemKey = (idx: number) => { if (!isObjectArray) return idx; return ((value[idx] as Record)?._key as string) ?? idx; }; return (
{translatedLabel ? (
{translatedLabel}
) : null} {Array.from({ length: count }).map((_, idx) => isObjectArray ? ( min} onRemove={() => handleRemoveAt(idx)} sortable={sortable} > {renderItem(idx, `${name}[${idx}]`)} ) : (
{renderItem(idx, `${name}[${idx}]`)}
{count > min ? ( } aria-label="Remove item" size="small" variant="minimal-destructive" onPress={() => handleRemoveAt(idx)} /> ) : null}
), )} {translatedError ? ( {translatedError} ) : null} {translatedBottomText && !translatedError ? ( {translatedBottomText} ) : null} } onPress={handleAdd} isDisabled={count >= max} className="m-0-auto" > {t("common:actions.add")}
); } function ArrayItemFieldset({ index, children, canRemove, onRemove, sortable, }: { index: number; children: React.ReactNode; canRemove: boolean; onRemove: () => void; sortable?: boolean; }) { return (
{sortable ? : null} #{index + 1} {canRemove ? ( } aria-label="Remove item" size="small" variant="minimal-destructive" onPress={onRemove} /> ) : null}
{children}
); }