Skip to content

Commit

Permalink
feat: logic for transfering created
Browse files Browse the repository at this point in the history
  • Loading branch information
g-saracca committed Aug 8, 2024
1 parent 393e926 commit 946bf2b
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { useState } from 'react'
import { ListGroup } from 'react-bootstrap'
import { Button } from '../button/Button'
import { Row } from '../grid/Row'
import { Col } from '../grid/Col'
import { Form } from '../form/Form'

function not(a: readonly TransferListItem[], b: readonly TransferListItem[]) {
return a.filter((item) => !b.some((bItem) => bItem.value === item.value))
}

function intersection(a: readonly TransferListItem[], b: readonly TransferListItem[]) {
return a.filter((item) => b.some((bItem) => bItem.value === item.value))
}

export interface TransferListItem {
value: string | number
label: string
}

export interface TransferListProps {
availableItems: TransferListItem[]
defaultSelected?: TransferListItem[]
onChange?: (selected: TransferListItem[]) => void
}

export const TransferList = ({
availableItems,
defaultSelected = [],
onChange
}: TransferListProps) => {
const [checked, setChecked] = useState<readonly TransferListItem[]>([])
const [left, setLeft] = useState<readonly TransferListItem[]>(
not(availableItems, defaultSelected)
)
const [right, setRight] = useState<readonly TransferListItem[]>(
intersection(availableItems, defaultSelected)
)

const leftChecked = intersection(checked, left)
const rightChecked = intersection(checked, right)

const handleToggle = (item: TransferListItem) => () => {
const currentIndex = checked.findIndex((checkedItem) => checkedItem.value === item.value)
const newChecked = [...checked]

if (currentIndex === -1) {
newChecked.push(item)
} else {
newChecked.splice(currentIndex, 1)
}

setChecked(newChecked)
}

const handleAllRight = () => {
setRight(right.concat(left))
onChange && onChange(right.concat(left))
setLeft([])
}

const handleCheckedRight = () => {
setRight(right.concat(leftChecked))
onChange && onChange(right.concat(leftChecked))
setLeft(not(left, leftChecked))
setChecked(not(checked, leftChecked))
}

const handleCheckedLeft = () => {
setLeft(left.concat(rightChecked))
setRight(not(right, rightChecked))
onChange && onChange(not(right, rightChecked))
setChecked(not(checked, rightChecked))
}

const handleAllLeft = () => {
setLeft(left.concat(right))
setRight([])
onChange && onChange([])
}

const customList = (items: readonly TransferListItem[]) => (
<div style={{ width: 200, height: 230, overflow: 'auto' }}>
<ListGroup as="ul">
{items.map((item: TransferListItem) => {
const labelId = `transfer-list-item-${item.value}-label`

return (
<ListGroup.Item as="li" key={item.value}>
<Form.Group.Checkbox
label={item.label}
onChange={handleToggle(item)}
id={labelId}
checked={checked.indexOf(item) !== -1}
tabIndex={-1}
/>
</ListGroup.Item>
)
})}
</ListGroup>
</div>
)

return (
<Row>
<Col>{customList(left)}</Col>
<Col>
<Col>
<Button onClick={handleAllRight} disabled={left.length === 0} aria-label="move all right">
</Button>
<Button
onClick={handleCheckedRight}
disabled={leftChecked.length === 0}
aria-label="move selected right">
&gt;
</Button>
<Button
onClick={handleCheckedLeft}
disabled={rightChecked.length === 0}
aria-label="move selected left">
&lt;
</Button>
<Button onClick={handleAllLeft} disabled={right.length === 0} aria-label="move all left">
</Button>
</Col>
</Col>
<Col>{customList(right)}</Col>
</Row>
)
}
1 change: 1 addition & 0 deletions packages/design-system/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export { SelectAdvanced } from './components/select-advanced/SelectAdvanced'
export { Card } from './components/card/Card'
export { ProgressBar } from './components/progress-bar/ProgressBar'
export { Stack } from './components/stack/Stack'
export { TransferList } from './components/transfer-list/TransferList'
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { Meta, StoryObj } from '@storybook/react'
import { TransferList, TransferListItem } from '../../components/transfer-list/TransferList'

/**
* ## Description
* The transfer list component is an element that allows users to transfer on or more items between two lists.
* The list on the left is the source list and the list on the right is the target list.
* The right list can be empty or have some items already.
*/

const meta: Meta<typeof TransferList> = {
title: 'Transfer List',
component: TransferList,
tags: ['autodocs']
}

const availableItems: TransferListItem[] = [
{
label: 'Item 1',
value: 1
},
{
label: 'Item 2',
value: 2
},
{
label: 'Item 3',
value: 3
},
{
label: 'Item 4',
value: 4
},
{
label: 'Item 5',
value: 5
},
{
label: 'Item 6',
value: 6
}
]

const defaultSelected: TransferListItem[] = [
{
label: 'Item 4',
value: 4
},
{
label: 'Item 5',
value: 5
}
]

export default meta
type Story = StoryObj<typeof TransferList>

const onChangeFn = (items: TransferListItem[]) => {
console.log(items)
}

export const Single: Story = {
render: () => (
<TransferList
availableItems={availableItems}
defaultSelected={defaultSelected}
onChange={onChangeFn}
/>
)
}

0 comments on commit 946bf2b

Please sign in to comment.