@tremolo-ui/react 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1125 -1437
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +58 -93
- package/dist/index.d.cts +561 -243
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +561 -243
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1125 -1438
- package/dist/index.js.map +1 -1
- package/package.json +9 -10
- package/src/components/AnimationCanvas/index.tsx +84 -155
- package/src/components/Knob/ActiveLine.tsx +6 -6
- package/src/components/Knob/InactiveLine.tsx +9 -11
- package/src/components/Knob/SVGRoot.tsx +9 -18
- package/src/components/Knob/context.tsx +57 -89
- package/src/components/Knob/index.css +6 -2
- package/src/components/Knob/index.tsx +90 -61
- package/src/components/NumberInput/DecrementStepper.tsx +17 -50
- package/src/components/NumberInput/IncrementStepper.tsx +17 -50
- package/src/components/NumberInput/InputField.tsx +123 -0
- package/src/components/NumberInput/Stepper.tsx +90 -12
- package/src/components/NumberInput/context.tsx +74 -146
- package/src/components/NumberInput/index.css +38 -102
- package/src/components/NumberInput/index.tsx +252 -98
- package/src/components/NumberInput/stepperButton.tsx +90 -0
- package/src/components/Piano/index.tsx +272 -288
- package/src/components/Piano/keyboardShortcuts.ts +34 -4
- package/src/components/PointsEditor/Background.tsx +8 -2
- package/src/components/PointsEditor/Container.tsx +19 -9
- package/src/components/PointsEditor/Point.tsx +104 -24
- package/src/components/PointsEditor/context.tsx +28 -74
- package/src/components/PointsEditor/index.css +5 -0
- package/src/components/PointsEditor/index.tsx +77 -43
- package/src/components/Slider/{Scale.tsx → Marks.tsx} +8 -8
- package/src/components/Slider/{ScaleOption.tsx → MarksOption.tsx} +16 -18
- package/src/components/Slider/Thumb.tsx +55 -68
- package/src/components/Slider/Track.tsx +15 -15
- package/src/components/Slider/context.tsx +36 -51
- package/src/components/Slider/index.css +18 -5
- package/src/components/Slider/index.tsx +127 -146
- package/src/components/Slider/type.ts +4 -4
- package/src/components/XYPad/Area.tsx +16 -7
- package/src/components/XYPad/Thumb.tsx +62 -74
- package/src/components/XYPad/context.tsx +46 -0
- package/src/components/XYPad/index.css +12 -0
- package/src/components/XYPad/index.tsx +190 -230
- package/src/hooks/useDrag.ts +1 -1
- package/src/hooks/useDragValue.ts +149 -0
- package/src/hooks/useEventListener.ts +1 -1
- package/src/hooks/useWheel.ts +29 -9
- package/src/index.ts +15 -9
- package/src/components/AnimationCanvas/canvas.ts +0 -46
- package/src/components/NumberInput/InternalInput.tsx +0 -216
- package/src/components/NumberInput/type.ts +0 -58
- package/src/components/Piano/KeyLabel.tsx +0 -51
- package/src/components/Piano/context.tsx +0 -73
- package/src/components/Piano/key.tsx +0 -205
- package/src/components/_util/type.ts +0 -1
- package/src/hooks/useDragWithElement.ts +0 -100
- package/src/hooks/usePianoDrag.ts +0 -75
- package/src/hooks/useRefCallbackEvent.ts +0 -34
- /package/src/hooks/{useCallbackRef.ts → _internal/useCallbackRef.ts} +0 -0
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { createContext, ReactNode, useContext, useEffect, useRef } from 'react'
|
|
2
|
-
import { createStore, useStore } from 'zustand'
|
|
3
|
-
|
|
4
|
-
export type NoteRange = {
|
|
5
|
-
first: number
|
|
6
|
-
last: number
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
type State = {
|
|
10
|
-
noteRange: NoteRange
|
|
11
|
-
glissando: boolean
|
|
12
|
-
midiMax: number
|
|
13
|
-
fill: boolean
|
|
14
|
-
onPlayNote: (noteNumber: number, velocity?: number) => void
|
|
15
|
-
onStopNote: (noteNumber: number) => void
|
|
16
|
-
label: (note: number, index: number) => ReactNode
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
type Action = {
|
|
20
|
-
notePosition: (note: number) => number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
type PianoStore = ReturnType<typeof createPianoStore>
|
|
24
|
-
|
|
25
|
-
const createPianoStore = (initProps: Partial<State> & Action) => {
|
|
26
|
-
// const noteRange = initProps.noteRange || { first: 0, last: 127 }
|
|
27
|
-
const DEFAULT_PROPS: State = {
|
|
28
|
-
noteRange: { first: 0, last: 127 },
|
|
29
|
-
glissando: true,
|
|
30
|
-
midiMax: 127,
|
|
31
|
-
fill: false,
|
|
32
|
-
label: () => undefined,
|
|
33
|
-
onPlayNote: () => {},
|
|
34
|
-
onStopNote: () => {},
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return createStore<State & Action>()(() => ({
|
|
38
|
-
...DEFAULT_PROPS,
|
|
39
|
-
...initProps,
|
|
40
|
-
notePosition: initProps.notePosition,
|
|
41
|
-
}))
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const PianoContext = createContext<PianoStore | null>(null)
|
|
45
|
-
|
|
46
|
-
type PianoProviderProps = React.PropsWithChildren<Partial<State> & Action>
|
|
47
|
-
|
|
48
|
-
export function PianoProvider({ children, ...props }: PianoProviderProps) {
|
|
49
|
-
const storeRef = useRef<PianoStore>(null)
|
|
50
|
-
if (!storeRef.current) {
|
|
51
|
-
storeRef.current = createPianoStore(props)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (storeRef.current) {
|
|
56
|
-
storeRef.current.setState(props)
|
|
57
|
-
} else {
|
|
58
|
-
storeRef.current = createPianoStore(props)
|
|
59
|
-
}
|
|
60
|
-
}, [props])
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<PianoContext.Provider value={storeRef.current}>
|
|
64
|
-
{children}
|
|
65
|
-
</PianoContext.Provider>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function usePianoContext<T>(selector: (state: State & Action) => T): T {
|
|
70
|
-
const store = useContext(PianoContext)
|
|
71
|
-
if (!store) throw new Error('Missing PianoContext.Provider in the tree')
|
|
72
|
-
return useStore(store, selector)
|
|
73
|
-
}
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import clsx from 'clsx'
|
|
2
|
-
import React, {
|
|
3
|
-
CSSProperties,
|
|
4
|
-
ComponentPropsWithoutRef,
|
|
5
|
-
forwardRef,
|
|
6
|
-
useImperativeHandle,
|
|
7
|
-
useState,
|
|
8
|
-
ReactElement,
|
|
9
|
-
} from 'react'
|
|
10
|
-
|
|
11
|
-
import { usePianoContext } from './context'
|
|
12
|
-
import { KeyLabel, KeyLabelProps } from './KeyLabel'
|
|
13
|
-
|
|
14
|
-
export interface KeyProps {
|
|
15
|
-
noteNumber: number
|
|
16
|
-
|
|
17
|
-
width?: number
|
|
18
|
-
height?: number | `${number}%`
|
|
19
|
-
|
|
20
|
-
bg?: string
|
|
21
|
-
color?: string
|
|
22
|
-
activeBg?: string
|
|
23
|
-
activeColor?: string
|
|
24
|
-
|
|
25
|
-
style?: CSSProperties
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* \<KeyLabel />
|
|
29
|
-
*/
|
|
30
|
-
children?: ReactElement
|
|
31
|
-
|
|
32
|
-
/** @internal */
|
|
33
|
-
__width?: number
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface KeyMethods {
|
|
37
|
-
play: (velocity?: number) => void
|
|
38
|
-
stop: () => void
|
|
39
|
-
played: () => boolean
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
type Props = KeyProps & Omit<ComponentPropsWithoutRef<'div'>, keyof KeyProps>
|
|
43
|
-
type ImplProps = Props & { keyType: 'black' | 'white' }
|
|
44
|
-
|
|
45
|
-
export const defaultWhiteKeyWidth = 40
|
|
46
|
-
export const defaultBlackKeyWidth = 26
|
|
47
|
-
|
|
48
|
-
const KeyImpl = forwardRef<KeyMethods, ImplProps>(
|
|
49
|
-
(
|
|
50
|
-
{
|
|
51
|
-
keyType,
|
|
52
|
-
noteNumber,
|
|
53
|
-
width,
|
|
54
|
-
height,
|
|
55
|
-
bg,
|
|
56
|
-
color,
|
|
57
|
-
activeBg,
|
|
58
|
-
activeColor,
|
|
59
|
-
className,
|
|
60
|
-
style,
|
|
61
|
-
children,
|
|
62
|
-
__width,
|
|
63
|
-
...props
|
|
64
|
-
}: ImplProps,
|
|
65
|
-
ref,
|
|
66
|
-
) => {
|
|
67
|
-
const [played, setPlayed] = useState(false)
|
|
68
|
-
const midiMax = usePianoContext((s) => s.midiMax)
|
|
69
|
-
const fill = usePianoContext((s) => s.fill)
|
|
70
|
-
// const glissando = usePianoContext(s => s.glissando)
|
|
71
|
-
const onPlayNote = usePianoContext((s) => s.onPlayNote)
|
|
72
|
-
const onStopNote = usePianoContext((s) => s.onStopNote)
|
|
73
|
-
const label = usePianoContext((s) => s.label)
|
|
74
|
-
const notePosition = usePianoContext((s) => s.notePosition)
|
|
75
|
-
const position = notePosition(noteNumber)
|
|
76
|
-
|
|
77
|
-
const disabled = noteNumber > midiMax
|
|
78
|
-
const colors = {
|
|
79
|
-
'--bg': bg,
|
|
80
|
-
'--color': color,
|
|
81
|
-
'--active-bg': activeBg,
|
|
82
|
-
'--active-color': activeColor,
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
useImperativeHandle(ref, () => {
|
|
86
|
-
return {
|
|
87
|
-
play(velocity) {
|
|
88
|
-
if (disabled) return
|
|
89
|
-
setPlayed(true)
|
|
90
|
-
if (onPlayNote) onPlayNote(noteNumber, velocity)
|
|
91
|
-
},
|
|
92
|
-
stop() {
|
|
93
|
-
setPlayed(false)
|
|
94
|
-
if (onStopNote) onStopNote(noteNumber)
|
|
95
|
-
},
|
|
96
|
-
played() {
|
|
97
|
-
return played
|
|
98
|
-
},
|
|
99
|
-
}
|
|
100
|
-
}, [disabled, noteNumber, onPlayNote, onStopNote, played])
|
|
101
|
-
|
|
102
|
-
let keyLabelProps: KeyLabelProps = {}
|
|
103
|
-
if (children != undefined) {
|
|
104
|
-
React.Children.forEach(children, (child) => {
|
|
105
|
-
if (React.isValidElement(child)) {
|
|
106
|
-
if (child.type == KeyLabel) {
|
|
107
|
-
keyLabelProps = child.props as KeyLabelProps
|
|
108
|
-
} else {
|
|
109
|
-
throw new Error('only <KeyLabel>')
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
throw new Error('children is an invalid element.')
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return (
|
|
118
|
-
<div
|
|
119
|
-
className={clsx(`tremolo-piano-${keyType}-key`, className)}
|
|
120
|
-
aria-disabled={disabled}
|
|
121
|
-
data-active={played}
|
|
122
|
-
style={{
|
|
123
|
-
...colors,
|
|
124
|
-
left: position,
|
|
125
|
-
width: fill ? __width : width,
|
|
126
|
-
height: height,
|
|
127
|
-
...style,
|
|
128
|
-
}}
|
|
129
|
-
// onPointerDown={() => {
|
|
130
|
-
// if (disabled) return
|
|
131
|
-
// setPlayed(true)
|
|
132
|
-
// if (onPlayNote) onPlayNote(noteNumber)
|
|
133
|
-
// }}
|
|
134
|
-
// onPointerEnter={() => {
|
|
135
|
-
// if (disabled) return
|
|
136
|
-
// if (glissando) {
|
|
137
|
-
// setPlayed(true)
|
|
138
|
-
// if (onPlayNote) onPlayNote(noteNumber)
|
|
139
|
-
// }
|
|
140
|
-
// }}
|
|
141
|
-
// onPointerUp={() => {
|
|
142
|
-
// if (disabled) return
|
|
143
|
-
// setPlayed(false)
|
|
144
|
-
// if (onStopNote) onStopNote(noteNumber)
|
|
145
|
-
// }}
|
|
146
|
-
// onPointerLeave={() => {
|
|
147
|
-
// if (disabled) return
|
|
148
|
-
// if (glissando) {
|
|
149
|
-
// setPlayed(false)
|
|
150
|
-
// if (onStopNote) onStopNote(noteNumber)
|
|
151
|
-
// }
|
|
152
|
-
// }}
|
|
153
|
-
{...props}
|
|
154
|
-
>
|
|
155
|
-
<KeyLabel label={label} __note={noteNumber} {...keyLabelProps} />
|
|
156
|
-
</div>
|
|
157
|
-
)
|
|
158
|
-
},
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
export const WhiteKey = forwardRef<KeyMethods, Props>(
|
|
162
|
-
(
|
|
163
|
-
{
|
|
164
|
-
width = defaultWhiteKeyWidth,
|
|
165
|
-
height = '100%',
|
|
166
|
-
onContextMenu = (e) => e.preventDefault(),
|
|
167
|
-
...props
|
|
168
|
-
}: Props,
|
|
169
|
-
ref,
|
|
170
|
-
) => {
|
|
171
|
-
return (
|
|
172
|
-
<KeyImpl
|
|
173
|
-
ref={ref}
|
|
174
|
-
keyType="white"
|
|
175
|
-
width={width}
|
|
176
|
-
height={height}
|
|
177
|
-
onContextMenu={onContextMenu}
|
|
178
|
-
{...props}
|
|
179
|
-
/>
|
|
180
|
-
)
|
|
181
|
-
},
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
export const BlackKey = forwardRef<KeyMethods, Props>(
|
|
185
|
-
(
|
|
186
|
-
{
|
|
187
|
-
width = defaultBlackKeyWidth,
|
|
188
|
-
height = '60%',
|
|
189
|
-
onContextMenu = (e) => e.preventDefault(),
|
|
190
|
-
...props
|
|
191
|
-
}: Props,
|
|
192
|
-
ref,
|
|
193
|
-
) => {
|
|
194
|
-
return (
|
|
195
|
-
<KeyImpl
|
|
196
|
-
ref={ref}
|
|
197
|
-
keyType="black"
|
|
198
|
-
width={width}
|
|
199
|
-
height={height}
|
|
200
|
-
onContextMenu={onContextMenu}
|
|
201
|
-
{...props}
|
|
202
|
-
/>
|
|
203
|
-
)
|
|
204
|
-
},
|
|
205
|
-
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type Override<T, U> = T & Omit<U, keyof T>
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { RefObject, useCallback, useEffect, useRef, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
import { createDrag } from '@tremolo-ui/dom'
|
|
4
|
-
import { normalizeValue } from '@tremolo-ui/functions'
|
|
5
|
-
|
|
6
|
-
import { useCallbackRef } from './useCallbackRef'
|
|
7
|
-
|
|
8
|
-
interface UseDragWithElement<T extends Element> {
|
|
9
|
-
baseElementRef: RefObject<T | null>
|
|
10
|
-
/**
|
|
11
|
-
* Report the position on pointer down, before any movement.
|
|
12
|
-
*
|
|
13
|
-
* Enable it where the pointer position *is* the value, so that a plain click
|
|
14
|
-
* jumps to it. Leave it off where the element being dragged is an object in
|
|
15
|
-
* its own right, so that grabbing its edge does not shift it under the cursor.
|
|
16
|
-
*
|
|
17
|
-
* @default false
|
|
18
|
-
*/
|
|
19
|
-
updateOnPointerDown?: boolean
|
|
20
|
-
/** CSS cursor to show while dragging. Applied to the element itself. */
|
|
21
|
-
cursor?: string
|
|
22
|
-
onDrag: (normalizedX: number, normalizedY: number) => void
|
|
23
|
-
onDragStart?: (normalizedX: number, normalizedY: number) => void
|
|
24
|
-
onDragEnd?: (normalizedX: number, normalizedY: number) => void
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Track a pointer drag, reporting the position normalized against
|
|
29
|
-
* the bounding rect of `baseElementRef`.
|
|
30
|
-
*
|
|
31
|
-
* @returns a ref callback for the element that starts the drag, and whether a drag is in progress
|
|
32
|
-
*/
|
|
33
|
-
export function useDragWithElement<T extends Element>({
|
|
34
|
-
baseElementRef,
|
|
35
|
-
updateOnPointerDown = false,
|
|
36
|
-
cursor,
|
|
37
|
-
onDrag,
|
|
38
|
-
onDragStart,
|
|
39
|
-
onDragEnd,
|
|
40
|
-
}: UseDragWithElement<T>) {
|
|
41
|
-
const [dragging, setDragging] = useState(false)
|
|
42
|
-
const normalizedX = useRef(0)
|
|
43
|
-
const normalizedY = useRef(0)
|
|
44
|
-
|
|
45
|
-
const dragHandler = useCallbackRef(onDrag)
|
|
46
|
-
const dragStartHandler = useCallbackRef(onDragStart)
|
|
47
|
-
const dragEndHandler = useCallbackRef(onDragEnd)
|
|
48
|
-
|
|
49
|
-
const update = useCallback(
|
|
50
|
-
(clientX: number, clientY: number) => {
|
|
51
|
-
const base = baseElementRef.current
|
|
52
|
-
if (!base) return false
|
|
53
|
-
const { left, top, right, bottom } = base.getBoundingClientRect()
|
|
54
|
-
normalizedX.current = normalizeValue(clientX, left, right)
|
|
55
|
-
normalizedY.current = normalizeValue(clientY, top, bottom)
|
|
56
|
-
return true
|
|
57
|
-
},
|
|
58
|
-
[baseElementRef],
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
// See useDrag for why the node is held in state.
|
|
62
|
-
const [node, setNode] = useState<Element | null>(null)
|
|
63
|
-
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
if (!node) return
|
|
66
|
-
|
|
67
|
-
const instance = createDrag(node, {
|
|
68
|
-
cursor,
|
|
69
|
-
onDragStart: ({ clientX, clientY }) => {
|
|
70
|
-
setDragging(true)
|
|
71
|
-
const updated = update(clientX, clientY)
|
|
72
|
-
if (updated && updateOnPointerDown) {
|
|
73
|
-
dragHandler(normalizedX.current, normalizedY.current)
|
|
74
|
-
}
|
|
75
|
-
dragStartHandler(normalizedX.current, normalizedY.current)
|
|
76
|
-
},
|
|
77
|
-
onDrag: ({ clientX, clientY }) => {
|
|
78
|
-
if (update(clientX, clientY)) {
|
|
79
|
-
dragHandler(normalizedX.current, normalizedY.current)
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
onDragEnd: () => {
|
|
83
|
-
setDragging(false)
|
|
84
|
-
dragEndHandler(normalizedX.current, normalizedY.current)
|
|
85
|
-
},
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
return () => instance.destroy()
|
|
89
|
-
}, [
|
|
90
|
-
node,
|
|
91
|
-
update,
|
|
92
|
-
updateOnPointerDown,
|
|
93
|
-
cursor,
|
|
94
|
-
dragHandler,
|
|
95
|
-
dragStartHandler,
|
|
96
|
-
dragEndHandler,
|
|
97
|
-
])
|
|
98
|
-
|
|
99
|
-
return { refCallback: setNode, dragging }
|
|
100
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { useRef, useCallback, RefObject } from 'react'
|
|
2
|
-
|
|
3
|
-
import { normalizeValue } from '@tremolo-ui/functions'
|
|
4
|
-
|
|
5
|
-
import { useEventListener } from './useEventListener'
|
|
6
|
-
import { useRefCallbackEvent } from './useRefCallbackEvent'
|
|
7
|
-
|
|
8
|
-
interface UsePianoDrag<T extends Element> {
|
|
9
|
-
baseElementRef: RefObject<T | null>
|
|
10
|
-
onDrag: (normalizedX: number, normalizedY: number) => void
|
|
11
|
-
onDragStart?: (normalizedX: number, normalizedY: number) => void
|
|
12
|
-
onDragEnd?: (normalizedX: number, normalizedY: number) => void
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Internal
|
|
17
|
-
* @returns [refCallback, pointerDownHandler]
|
|
18
|
-
* @private
|
|
19
|
-
*/
|
|
20
|
-
export function usePianoDrag<T extends Element>({
|
|
21
|
-
baseElementRef,
|
|
22
|
-
onDrag,
|
|
23
|
-
onDragStart,
|
|
24
|
-
onDragEnd,
|
|
25
|
-
}: UsePianoDrag<T>) {
|
|
26
|
-
const dragged = useRef(false)
|
|
27
|
-
const normalizedX = useRef(0)
|
|
28
|
-
const normalizedY = useRef(0)
|
|
29
|
-
|
|
30
|
-
const handleDrag = useCallback(
|
|
31
|
-
(event: MouseEvent | React.PointerEvent<T> | TouchEvent) => {
|
|
32
|
-
if (!baseElementRef.current || !dragged.current) {
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
const isTouch = event instanceof TouchEvent
|
|
36
|
-
if (isTouch && event.cancelable) event.preventDefault()
|
|
37
|
-
const { left, top, right, bottom } =
|
|
38
|
-
baseElementRef.current.getBoundingClientRect()
|
|
39
|
-
const mouseX = isTouch ? event.touches[0].clientX : event.clientX
|
|
40
|
-
const mouseY = isTouch ? event.touches[0].clientY : event.clientY
|
|
41
|
-
const nx = normalizeValue(mouseX, left, right)
|
|
42
|
-
const ny = normalizeValue(mouseY, top, bottom)
|
|
43
|
-
normalizedX.current = nx
|
|
44
|
-
normalizedY.current = ny
|
|
45
|
-
onDrag(nx, ny)
|
|
46
|
-
},
|
|
47
|
-
[onDrag, baseElementRef],
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
const refHandler = useRefCallbackEvent(
|
|
51
|
-
'touchmove',
|
|
52
|
-
handleDrag,
|
|
53
|
-
{ passive: false },
|
|
54
|
-
[onDrag],
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
const pointerDownHandler = useCallback(
|
|
58
|
-
(event: React.PointerEvent<T>) => {
|
|
59
|
-
dragged.current = true
|
|
60
|
-
handleDrag(event)
|
|
61
|
-
onDragStart?.(normalizedX.current, normalizedY.current)
|
|
62
|
-
},
|
|
63
|
-
[handleDrag, onDragStart],
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
useEventListener(globalThis.window, 'pointermove', handleDrag)
|
|
67
|
-
|
|
68
|
-
useEventListener(globalThis.window, 'pointerup', () => {
|
|
69
|
-
if (!dragged.current) return
|
|
70
|
-
dragged.current = false
|
|
71
|
-
onDragEnd?.(normalizedX.current, normalizedY.current)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
return { refHandler, pointerDownHandler }
|
|
75
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { useMemo } from 'react'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Internal
|
|
5
|
-
* @private
|
|
6
|
-
*/
|
|
7
|
-
export function useRefCallbackEvent<K extends keyof DocumentEventMap>(
|
|
8
|
-
event: K,
|
|
9
|
-
handler: (event: DocumentEventMap[K]) => void,
|
|
10
|
-
options?: boolean | AddEventListenerOptions,
|
|
11
|
-
deps?: React.DependencyList,
|
|
12
|
-
): (div: EventTarget | null) => void
|
|
13
|
-
export function useRefCallbackEvent(
|
|
14
|
-
event: string,
|
|
15
|
-
handler: (event: Event) => void,
|
|
16
|
-
options?: boolean | AddEventListenerOptions,
|
|
17
|
-
deps: React.DependencyList = [],
|
|
18
|
-
): (div: EventTarget | null) => void {
|
|
19
|
-
return useMemo(() => {
|
|
20
|
-
let cleanup: (() => void) | undefined
|
|
21
|
-
return (node: EventTarget | null) => {
|
|
22
|
-
if (!node) {
|
|
23
|
-
cleanup?.()
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
node.addEventListener(event, handler, options)
|
|
27
|
-
|
|
28
|
-
cleanup = () => {
|
|
29
|
-
node.removeEventListener(event, handler, options)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
33
|
-
}, deps)
|
|
34
|
-
}
|
|
File without changes
|