80 lines
2.1 KiB
TypeScript
80 lines
2.1 KiB
TypeScript
|
|
import { RadioGroup, RadioGroupItem } from "@repo/shadcn-ui/components/radio-group";
|
||
|
|
|
||
|
|
import { EntityTable } from "./entity-list";
|
||
|
|
import type { PickerOption } from "./option-picker-dialog";
|
||
|
|
|
||
|
|
/** Listado de selección única con búsqueda; el diálogo conserva la confirmación provisional. */
|
||
|
|
export function OptionPickerList({
|
||
|
|
title,
|
||
|
|
options,
|
||
|
|
value,
|
||
|
|
onChange,
|
||
|
|
labels,
|
||
|
|
locale,
|
||
|
|
loading = false,
|
||
|
|
}: {
|
||
|
|
title: string;
|
||
|
|
options: readonly PickerOption[];
|
||
|
|
value: string;
|
||
|
|
onChange: (id: string) => void;
|
||
|
|
labels: { selection: string; name: string; description: string; search: string };
|
||
|
|
locale: string;
|
||
|
|
loading?: boolean;
|
||
|
|
}) {
|
||
|
|
const choose = (id: string) => {
|
||
|
|
if (!loading && options.some((option) => option.id === id && !option.disabled)) {
|
||
|
|
onChange(id);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<RadioGroup
|
||
|
|
aria-label={title}
|
||
|
|
className="flex min-h-96 flex-1 flex-col"
|
||
|
|
onValueChange={(id) => {
|
||
|
|
if (typeof id === "string") {
|
||
|
|
choose(id);
|
||
|
|
}
|
||
|
|
}}
|
||
|
|
value={value}
|
||
|
|
>
|
||
|
|
<EntityTable
|
||
|
|
columns={[
|
||
|
|
{
|
||
|
|
id: "selection",
|
||
|
|
label: labels.selection,
|
||
|
|
hideable: false,
|
||
|
|
render: (option) => (
|
||
|
|
<RadioGroupItem
|
||
|
|
aria-label={option.label}
|
||
|
|
disabled={loading || option.disabled}
|
||
|
|
value={option.id}
|
||
|
|
/>
|
||
|
|
),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "name",
|
||
|
|
label: labels.name,
|
||
|
|
value: (option) => option.label,
|
||
|
|
render: (option) => <span className="font-medium">{option.label}</span>,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "description",
|
||
|
|
label: labels.description,
|
||
|
|
value: (option) => option.description,
|
||
|
|
render: (option) => option.description || "—",
|
||
|
|
},
|
||
|
|
]}
|
||
|
|
items={options}
|
||
|
|
layout="fill"
|
||
|
|
loading={loading}
|
||
|
|
locale={locale}
|
||
|
|
onRowClick={(option) => choose(option.id)}
|
||
|
|
rowClassName={(option) => (option.disabled ? "cursor-not-allowed!" : "")}
|
||
|
|
searchPlaceholder={labels.search}
|
||
|
|
title={title}
|
||
|
|
/>
|
||
|
|
</RadioGroup>
|
||
|
|
);
|
||
|
|
}
|