143 lines
4.1 KiB
TypeScript
143 lines
4.1 KiB
TypeScript
import clsx from 'clsx'
|
|
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'
|
|
import { MenuIcon } from '@heroicons/react/solid'
|
|
|
|
import { Col } from 'web/components/layout/col'
|
|
import { Row } from 'web/components/layout/row'
|
|
import { Subtitle } from 'web/components/subtitle'
|
|
import { useMemberGroups } from 'web/hooks/use-group'
|
|
import { filterDefined } from 'common/util/array'
|
|
import { keyBy } from 'lodash'
|
|
import { User } from 'common/user'
|
|
import { Group } from 'common/group'
|
|
|
|
export function ArrangeHome(props: {
|
|
user: User | null
|
|
homeSections: { visible: string[]; hidden: string[] }
|
|
setHomeSections: (homeSections: {
|
|
visible: string[]
|
|
hidden: string[]
|
|
}) => void
|
|
}) {
|
|
const { user, homeSections, setHomeSections } = props
|
|
|
|
const groups = useMemberGroups(user?.id) ?? []
|
|
const { itemsById, visibleItems, hiddenItems } = getHomeItems(
|
|
groups,
|
|
homeSections
|
|
)
|
|
|
|
return (
|
|
<DragDropContext
|
|
onDragEnd={(e) => {
|
|
console.log('drag end', e)
|
|
const { destination, source, draggableId } = e
|
|
if (!destination) return
|
|
|
|
const item = itemsById[draggableId]
|
|
|
|
const newHomeSections = {
|
|
visible: visibleItems.map((item) => item.id),
|
|
hidden: hiddenItems.map((item) => item.id),
|
|
}
|
|
|
|
const sourceSection = source.droppableId as 'visible' | 'hidden'
|
|
newHomeSections[sourceSection].splice(source.index, 1)
|
|
|
|
const destSection = destination.droppableId as 'visible' | 'hidden'
|
|
newHomeSections[destSection].splice(destination.index, 0, item.id)
|
|
|
|
setHomeSections(newHomeSections)
|
|
}}
|
|
>
|
|
<Row className="relative max-w-lg gap-4">
|
|
<DraggableList items={visibleItems} title="Visible" />
|
|
<DraggableList items={hiddenItems} title="Hidden" />
|
|
</Row>
|
|
</DragDropContext>
|
|
)
|
|
}
|
|
|
|
function DraggableList(props: {
|
|
title: string
|
|
items: { id: string; label: string }[]
|
|
}) {
|
|
const { title, items } = props
|
|
return (
|
|
<Droppable droppableId={title.toLowerCase()}>
|
|
{(provided, snapshot) => (
|
|
<Col
|
|
{...provided.droppableProps}
|
|
ref={provided.innerRef}
|
|
className={clsx(
|
|
'width-[220px] flex-1 items-start rounded bg-gray-50 p-2',
|
|
snapshot.isDraggingOver && 'bg-gray-100'
|
|
)}
|
|
>
|
|
<Subtitle text={title} className="mx-2 !my-2" />
|
|
{items.map((item, index) => (
|
|
<Draggable key={item.id} draggableId={item.id} index={index}>
|
|
{(provided, snapshot) => (
|
|
<div
|
|
ref={provided.innerRef}
|
|
{...provided.draggableProps}
|
|
{...provided.dragHandleProps}
|
|
style={provided.draggableProps.style}
|
|
className={clsx(
|
|
'flex flex-row items-center gap-4 rounded bg-gray-50 p-2',
|
|
snapshot.isDragging && 'z-[9000] bg-gray-300'
|
|
)}
|
|
>
|
|
<MenuIcon
|
|
className="h-5 w-5 flex-shrink-0 text-gray-500"
|
|
aria-hidden="true"
|
|
/>{' '}
|
|
{item.label}
|
|
</div>
|
|
)}
|
|
</Draggable>
|
|
))}
|
|
{provided.placeholder}
|
|
</Col>
|
|
)}
|
|
</Droppable>
|
|
)
|
|
}
|
|
|
|
export const getHomeItems = (
|
|
groups: Group[],
|
|
homeSections: { visible: string[]; hidden: string[] }
|
|
) => {
|
|
const items = [
|
|
{ label: 'Trending', id: 'score' },
|
|
{ label: 'Newest', id: 'newest' },
|
|
{ label: 'Close date', id: 'close-date' },
|
|
{ label: 'Your bets', id: 'your-bets' },
|
|
...groups.map((g) => ({
|
|
label: g.name,
|
|
id: g.id,
|
|
})),
|
|
]
|
|
const itemsById = keyBy(items, 'id')
|
|
|
|
const { visible, hidden } = homeSections
|
|
|
|
const [visibleItems, hiddenItems] = [
|
|
filterDefined(visible.map((id) => itemsById[id])),
|
|
filterDefined(hidden.map((id) => itemsById[id])),
|
|
]
|
|
|
|
// Add unmentioned items to the visible list.
|
|
visibleItems.push(
|
|
...items.filter(
|
|
(item) => !visibleItems.includes(item) && !hiddenItems.includes(item)
|
|
)
|
|
)
|
|
|
|
return {
|
|
visibleItems,
|
|
hiddenItems,
|
|
itemsById,
|
|
}
|
|
}
|