|
'use client'; |
|
|
|
import { Button } from '@/components/ui/button'; |
|
import { Input } from '@/components/ui/input'; |
|
import { |
|
ChevronLeftIcon, |
|
ChevronRightIcon, |
|
SquareCheckIcon, |
|
SquareIcon, |
|
} from 'lucide-react'; |
|
import React from 'react'; |
|
|
|
export type TransferListItemType = { |
|
key: string; |
|
label: string; |
|
selected?: boolean; |
|
}; |
|
|
|
export default function TransferList({ |
|
items, |
|
}: { |
|
items: TransferListItemType[]; |
|
}) { |
|
const [leftList, setLeftList] = React.useState<TransferListItemType[]>(items); |
|
const [rightList, setRightList] = React.useState<TransferListItemType[]>([]); |
|
const [leftSearch, setLeftSearch] = React.useState(''); |
|
const [rightSearch, setRightSearch] = React.useState(''); |
|
|
|
const moveToRight = () => { |
|
const selectedItems = leftList.filter((item) => item.selected); |
|
setRightList([...rightList, ...selectedItems]); |
|
setLeftList(leftList.filter((item) => !item.selected)); |
|
}; |
|
|
|
const moveToLeft = () => { |
|
const selectedItems = rightList.filter((item) => item.selected); |
|
setLeftList([...leftList, ...selectedItems]); |
|
setRightList(rightList.filter((item) => !item.selected)); |
|
}; |
|
|
|
const toggleSelection = ( |
|
list: TransferListItemType[], |
|
setList: React.Dispatch<React.SetStateAction<TransferListItemType[]>>, |
|
key: string, |
|
) => { |
|
const updatedList = list.map((item) => { |
|
if (item.key === key) { |
|
return { ...item, selected: !item.selected }; |
|
} |
|
return item; |
|
}); |
|
|
|
setList(updatedList); |
|
}; |
|
|
|
return ( |
|
<div className="flex space-x-4"> |
|
<div className="w-1/2 shadow-sm bg-background rounded-sm"> |
|
<div className="flex items-center justify-between"> |
|
<Input |
|
placeholder="Search" |
|
className="rounded-br-none rounded-bl-none rounded-tr-none focus-visible:ring-0 focus-visible:border-blue-500" |
|
value={leftSearch} |
|
onChange={(e) => setLeftSearch(e.target.value)} |
|
/> |
|
<Button |
|
className="rounded-tl-none rounded-bl-none rounded-br-none border-l-0" |
|
onClick={moveToRight} |
|
size="icon" |
|
variant="outline" |
|
> |
|
<ChevronRightIcon className="h-4 w-4" /> |
|
</Button> |
|
</div> |
|
<ul className="h-[200px] border-l border-r border-b rounded-br-sm rounded-bl-sm p-1.5 overflow-y-scroll"> |
|
{leftList |
|
.filter((item) => |
|
item.label.toLowerCase().includes(leftSearch.toLowerCase()), |
|
) |
|
.map((item) => ( |
|
<li |
|
className="flex items-center gap-1.5 text-sm hover:bg-muted rounded-sm" |
|
key={item.key} |
|
> |
|
<button |
|
type={'button'} |
|
className="flex items-center gap-1.5 w-full p-1.5" |
|
onClick={() => |
|
toggleSelection(leftList, setLeftList, item.key) |
|
} |
|
> |
|
{item.selected ? ( |
|
<SquareCheckIcon className="h-5 w-5 text-muted-foreground/50" /> |
|
) : ( |
|
<SquareIcon className="h-5 w-5 text-muted-foreground/50" /> |
|
)} |
|
{item.label} |
|
</button> |
|
</li> |
|
))} |
|
</ul> |
|
</div> |
|
|
|
<div className="w-1/2 shadow-sm bg-background rounded-sm"> |
|
<div className="flex items-center justify-between"> |
|
<Button |
|
className="rounded-tr-none rounded-br-none rounded-bl-none border-r-0" |
|
onClick={moveToLeft} |
|
size="icon" |
|
variant="outline" |
|
> |
|
<ChevronLeftIcon className="h-4 w-4" /> |
|
</Button> |
|
<Input |
|
placeholder="Search" |
|
className="rounded-bl-none rounded-br-none rounded-tl-none focus-visible:ring-0 focus-visible:border-blue-500" |
|
value={rightSearch} |
|
onChange={(e) => setRightSearch(e.target.value)} |
|
/> |
|
</div> |
|
<ul className="h-[200px] border-l border-r border-b rounded-br-sm rounded-bl-sm p-1.5 overflow-y-scroll"> |
|
{rightList |
|
.filter((item) => |
|
item.label.toLowerCase().includes(rightSearch.toLowerCase()), |
|
) |
|
.map((item) => ( |
|
<li |
|
className="flex items-center gap-1.5 text-sm hover:bg-muted rounded-sm" |
|
key={item.key} |
|
> |
|
<button |
|
type="button" |
|
className="flex items-center gap-1.5 w-full p-1.5" |
|
onClick={() => |
|
toggleSelection(rightList, setRightList, item.key) |
|
} |
|
> |
|
{item.selected ? ( |
|
<SquareCheckIcon className="h-4 w-4 text-muted-foreground/50" /> |
|
) : ( |
|
<SquareIcon className="h-4 w-4 text-muted-foreground/50" /> |
|
)} |
|
{item.label} |
|
</button> |
|
</li> |
|
))} |
|
</ul> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|