/
Item.tsx
99 lines (86 loc) · 2.65 KB
/
Item.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import { invariant } from "hey-listen"
import * as React from "react"
import {
ReactHTML,
FunctionComponent,
useContext,
useEffect,
useRef,
forwardRef,
} from "react"
import { ReorderContext } from "../../context/ReorderContext"
import { Box } from "../../projection/geometry/types"
import { motion } from "../../render/dom/motion"
import { HTMLMotionProps } from "../../render/html/types"
import { useConstant } from "../../utils/use-constant"
import { useMotionValue } from "../../value/use-motion-value"
import { useTransform } from "../../value/use-transform"
import { isMotionValue } from "../../value/utils/is-motion-value"
export interface Props<V> {
/**
* A HTML element to render this component as. Defaults to `"li"`.
*
* @public
*/
as?: keyof ReactHTML
/**
* The value in the list that this component represents.
*
* @public
*/
value: V
}
function useDefaultMotionValue(value: any, defaultValue: number = 0) {
return isMotionValue(value) ? value : useMotionValue(defaultValue)
}
export function ReorderItem<V>(
{
children,
style,
value,
as = "li",
onDrag,
...props
}: Props<V> & HTMLMotionProps<any> & React.PropsWithChildren<{}>,
externalRef?: React.Ref<any>
) {
const Component = useConstant(() => motion(as)) as FunctionComponent<
HTMLMotionProps<any> & { ref?: React.Ref<any> }
>
const context = useContext(ReorderContext)
const point = {
x: useDefaultMotionValue(style?.x),
y: useDefaultMotionValue(style?.y),
}
const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) =>
latestX || latestY ? 1 : "unset"
)
const layout = useRef<Box | null>(null)
invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group")
const { axis, registerItem, updateOrder } = context!
useEffect(() => {
registerItem(value, layout.current!)
}, [context])
return (
<Component
drag={axis}
{...props}
dragSnapToOrigin
style={{ ...style, x: point.x, y: point.y, zIndex }}
layout
onDrag={(event, gesturePoint) => {
const { velocity } = gesturePoint
velocity[axis] &&
updateOrder(value, point[axis].get(), velocity[axis])
onDrag?.(event, gesturePoint)
}}
onLayoutMeasure={(measured) => {
layout.current = measured
}}
ref={externalRef}
>
{children}
</Component>
)
}
export const Item = forwardRef(ReorderItem)