93 lines
2.3 KiB
TypeScript
93 lines
2.3 KiB
TypeScript
import { Button } from "@repo/shadcn-ui/components/button";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@repo/shadcn-ui/components/select";
|
|
import { PlusIcon } from "lucide-react";
|
|
|
|
import { ButtonGroup } from "./button-group";
|
|
|
|
export type SelectOption = { value: string; label: string; disabled?: boolean };
|
|
export type SelectAction = { label: string; onClick: () => void; disabled?: boolean };
|
|
|
|
/** Selección controlada con una acción adyacente; el consumidor gestiona el alta y su contexto. */
|
|
export function SelectWithAction({
|
|
id,
|
|
label,
|
|
options,
|
|
placeholder,
|
|
value,
|
|
onValueChange,
|
|
disabled = false,
|
|
action,
|
|
required = false,
|
|
invalid = false,
|
|
describedBy,
|
|
}: {
|
|
id: string;
|
|
label: string;
|
|
options: readonly SelectOption[];
|
|
placeholder: string;
|
|
value: string;
|
|
onValueChange: (value: string) => void;
|
|
disabled?: boolean;
|
|
action?: SelectAction | undefined;
|
|
required?: boolean;
|
|
invalid?: boolean;
|
|
describedBy?: string | undefined;
|
|
}) {
|
|
const select = (
|
|
<Select
|
|
disabled={disabled}
|
|
items={[{ value: "", label: placeholder }, ...options]}
|
|
onValueChange={(selected) => {
|
|
if (typeof selected === "string") {
|
|
onValueChange(selected);
|
|
}
|
|
}}
|
|
required={required}
|
|
value={value}
|
|
>
|
|
<SelectTrigger
|
|
aria-describedby={describedBy}
|
|
aria-invalid={invalid || undefined}
|
|
aria-label={label}
|
|
className="w-full min-w-0 flex-1"
|
|
id={id}
|
|
>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="">{placeholder}</SelectItem>
|
|
{options.map((option) => (
|
|
<SelectItem disabled={option.disabled} key={option.value} value={option.value}>
|
|
{option.label}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
);
|
|
if (!action) {
|
|
return select;
|
|
}
|
|
return (
|
|
<ButtonGroup aria-label={label} className="w-full min-w-0">
|
|
{select}
|
|
<Button
|
|
aria-label={action.label}
|
|
disabled={disabled || action.disabled}
|
|
onClick={action.onClick}
|
|
size="icon"
|
|
title={action.label}
|
|
type="button"
|
|
variant="outline"
|
|
>
|
|
<PlusIcon aria-hidden="true" />
|
|
</Button>
|
|
</ButtonGroup>
|
|
);
|
|
}
|