You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
73 lines
1.8 KiB
73 lines
1.8 KiB
import { ReactElement, useRef } from "react"
|
|
import Draggable from "react-draggable"
|
|
|
|
export interface RackProps<E extends { key: string | number }> {
|
|
id: string
|
|
objects: E[]
|
|
onChange: (objects: E[]) => void
|
|
canDetach: (ref: HTMLDivElement) => boolean
|
|
onElementDetached: (ref: HTMLDivElement, el: E) => void
|
|
render: (e: E) => ReactElement
|
|
}
|
|
|
|
interface RackItemProps<E extends { key: string | number }> {
|
|
item: E
|
|
onTryDetach: (ref: HTMLDivElement, el: E) => void
|
|
render: (e: E) => ReactElement
|
|
}
|
|
|
|
/**
|
|
* A container of draggable objects
|
|
* */
|
|
export function Rack<E extends { key: string | number }>({
|
|
id,
|
|
objects,
|
|
onChange,
|
|
canDetach,
|
|
onElementDetached,
|
|
render,
|
|
}: RackProps<E>) {
|
|
return (
|
|
<div
|
|
id={id}
|
|
style={{
|
|
display: "flex",
|
|
}}>
|
|
{objects.map((element) => (
|
|
<RackItem
|
|
key={element.key}
|
|
item={element}
|
|
render={render}
|
|
onTryDetach={(ref, element) => {
|
|
if (!canDetach(ref)) return
|
|
|
|
const index = objects.findIndex(
|
|
(o) => o.key === element.key,
|
|
)
|
|
onChange(objects.toSpliced(index, 1))
|
|
|
|
onElementDetached(ref, element)
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function RackItem<E extends { key: string | number }>({
|
|
item,
|
|
onTryDetach,
|
|
render,
|
|
}: RackItemProps<E>) {
|
|
const divRef = useRef<HTMLDivElement>(null)
|
|
|
|
return (
|
|
<Draggable
|
|
position={{ x: 0, y: 0 }}
|
|
nodeRef={divRef}
|
|
onStop={() => onTryDetach(divRef.current!, item)}>
|
|
<div ref={divRef}>{render(item)}</div>
|
|
</Draggable>
|
|
)
|
|
}
|