@tremolo-ui/react 0.3.0 → 0.4.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 +152 -232
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -52
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +48 -52
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +152 -231
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/components/Knob/index.tsx +13 -30
- package/src/components/NumberInput/InternalInput.tsx +10 -15
- package/src/components/PointsEditor/Point.tsx +25 -40
- package/src/components/Slider/index.tsx +22 -37
- package/src/components/XYPad/index.tsx +24 -41
- package/src/hooks/useDrag.ts +41 -76
- package/src/hooks/useDragWithElement.ts +71 -43
- package/src/hooks/useWheel.ts +31 -0
- package/src/index.ts +1 -5
- package/src/components/DragObserver/index.tsx +0 -58
- package/src/components/WheelObserver/index.tsx +0 -48
|
@@ -1,19 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RefObject, useCallback, useEffect, useRef, useState } from 'react'
|
|
2
2
|
|
|
3
|
+
import { createDrag } from '@tremolo-ui/dom'
|
|
3
4
|
import { normalizeValue } from '@tremolo-ui/functions'
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
-
import { useRefCallbackEvent } from './useRefCallbackEvent'
|
|
6
|
+
import { useCallbackRef } from './useCallbackRef'
|
|
7
7
|
|
|
8
8
|
interface UseDragWithElement<T extends Element> {
|
|
9
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
|
|
10
22
|
onDrag: (normalizedX: number, normalizedY: number) => void
|
|
11
23
|
onDragStart?: (normalizedX: number, normalizedY: number) => void
|
|
12
24
|
onDragEnd?: (normalizedX: number, normalizedY: number) => void
|
|
13
25
|
}
|
|
14
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
|
+
*/
|
|
15
33
|
export function useDragWithElement<T extends Element>({
|
|
16
34
|
baseElementRef,
|
|
35
|
+
updateOnPointerDown = false,
|
|
36
|
+
cursor,
|
|
17
37
|
onDrag,
|
|
18
38
|
onDragStart,
|
|
19
39
|
onDragEnd,
|
|
@@ -22,51 +42,59 @@ export function useDragWithElement<T extends Element>({
|
|
|
22
42
|
const normalizedX = useRef(0)
|
|
23
43
|
const normalizedY = useRef(0)
|
|
24
44
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const ny = normalizeValue(mouseY, top, bottom)
|
|
38
|
-
normalizedX.current = nx
|
|
39
|
-
normalizedY.current = ny
|
|
40
|
-
onDrag(nx, ny)
|
|
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
|
|
41
57
|
},
|
|
42
|
-
[
|
|
58
|
+
[baseElementRef],
|
|
43
59
|
)
|
|
44
60
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
handleDrag,
|
|
48
|
-
{ passive: false },
|
|
49
|
-
[onDrag],
|
|
50
|
-
)
|
|
61
|
+
// See useDrag for why the node is held in state.
|
|
62
|
+
const [node, setNode] = useState<Element | null>(null)
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
(
|
|
54
|
-
// dragging.current = true
|
|
55
|
-
setDragging(true)
|
|
56
|
-
handleDrag(event)
|
|
57
|
-
onDragStart?.(normalizedX.current, normalizedY.current)
|
|
58
|
-
},
|
|
59
|
-
[handleDrag, onDragStart],
|
|
60
|
-
)
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (!node) return
|
|
61
66
|
|
|
62
|
-
|
|
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
|
+
})
|
|
63
87
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
88
|
+
return () => instance.destroy()
|
|
89
|
+
}, [
|
|
90
|
+
node,
|
|
91
|
+
update,
|
|
92
|
+
updateOnPointerDown,
|
|
93
|
+
cursor,
|
|
94
|
+
dragHandler,
|
|
95
|
+
dragStartHandler,
|
|
96
|
+
dragEndHandler,
|
|
97
|
+
])
|
|
70
98
|
|
|
71
|
-
return {
|
|
99
|
+
return { refCallback: setNode, dragging }
|
|
72
100
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { createWheel } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
import { useCallbackRef } from './useCallbackRef'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Listen to wheel events on an element.
|
|
9
|
+
*
|
|
10
|
+
* The listener is not passive, so the handler may call `preventDefault()`.
|
|
11
|
+
*
|
|
12
|
+
* @returns a ref callback to attach to the element
|
|
13
|
+
*/
|
|
14
|
+
export function useWheel<T extends Element>(
|
|
15
|
+
onWheel: (event: WheelEvent) => void,
|
|
16
|
+
): (node: T | null) => void {
|
|
17
|
+
const wheelHandler = useCallbackRef(onWheel)
|
|
18
|
+
|
|
19
|
+
// See useDrag for why the node is held in state.
|
|
20
|
+
const [node, setNode] = useState<T | null>(null)
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!node) return
|
|
24
|
+
|
|
25
|
+
const instance = createWheel(node, (event) => wheelHandler(event))
|
|
26
|
+
|
|
27
|
+
return () => instance.destroy()
|
|
28
|
+
}, [node, wheelHandler])
|
|
29
|
+
|
|
30
|
+
return setNode
|
|
31
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -16,7 +16,6 @@ export {
|
|
|
16
16
|
type InitFunction,
|
|
17
17
|
type RelativeSizingProps,
|
|
18
18
|
} from './components/AnimationCanvas'
|
|
19
|
-
export { DragObserver, type DragObserverProps } from './components/DragObserver'
|
|
20
19
|
export { Knob, type KnobProps, type KnobMethods } from './components/Knob'
|
|
21
20
|
export {
|
|
22
21
|
NumberInput,
|
|
@@ -55,10 +54,6 @@ export {
|
|
|
55
54
|
type SliderThumbProps,
|
|
56
55
|
type SliderTrackProps,
|
|
57
56
|
} from './components/Slider'
|
|
58
|
-
export {
|
|
59
|
-
WheelObserver,
|
|
60
|
-
type WheelObserverProps,
|
|
61
|
-
} from './components/WheelObserver'
|
|
62
57
|
export {
|
|
63
58
|
XYPad,
|
|
64
59
|
type XYPadProps,
|
|
@@ -84,3 +79,4 @@ export {
|
|
|
84
79
|
} from './hooks/useMIDIAccess'
|
|
85
80
|
export { useMIDIInput } from './hooks/useMIDIInput'
|
|
86
81
|
export { useMIDIMessage } from './hooks/useMIDIMessage'
|
|
82
|
+
export { useWheel } from './hooks/useWheel'
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react'
|
|
2
|
-
|
|
3
|
-
import { useDrag } from '../../hooks/useDrag'
|
|
4
|
-
import { Override } from '../_util/type'
|
|
5
|
-
|
|
6
|
-
export interface DragObserverProps<T extends ElementType> {
|
|
7
|
-
/**
|
|
8
|
-
* React.ElementType
|
|
9
|
-
* @default div
|
|
10
|
-
*/
|
|
11
|
-
as?: T
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Threshold at which the onDrag event fires.
|
|
15
|
-
* Prevents onDrag events from firing, for example, when double-clicking.
|
|
16
|
-
*/
|
|
17
|
-
threshold?: number
|
|
18
|
-
|
|
19
|
-
children?: ReactNode
|
|
20
|
-
onDrag?: (x: number, y: number, deltaX: number, deltaY: number) => void
|
|
21
|
-
onDragStart?: () => void
|
|
22
|
-
onDragEnd?: () => void
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function DragObserver<T extends ElementType = 'div'>(
|
|
26
|
-
props: Override<DragObserverProps<T>, ComponentPropsWithoutRef<T>>,
|
|
27
|
-
) {
|
|
28
|
-
const {
|
|
29
|
-
as: Component = 'div',
|
|
30
|
-
threshold = 1,
|
|
31
|
-
onDrag,
|
|
32
|
-
onDragStart,
|
|
33
|
-
onDragEnd,
|
|
34
|
-
children,
|
|
35
|
-
onPointerDown,
|
|
36
|
-
...attributes
|
|
37
|
-
} = props
|
|
38
|
-
|
|
39
|
-
const [refCallback, pointerDownHandler] = useDrag({
|
|
40
|
-
threshold: threshold,
|
|
41
|
-
onDrag: onDrag,
|
|
42
|
-
onDragStart: onDragStart,
|
|
43
|
-
onDragEnd: onDragEnd,
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<Component
|
|
48
|
-
ref={refCallback}
|
|
49
|
-
onPointerDown={(e) => {
|
|
50
|
-
pointerDownHandler(e)
|
|
51
|
-
onPointerDown?.(e)
|
|
52
|
-
}}
|
|
53
|
-
{...attributes}
|
|
54
|
-
>
|
|
55
|
-
{children}
|
|
56
|
-
</Component>
|
|
57
|
-
)
|
|
58
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { ComponentProps, ElementType, ReactNode } from 'react'
|
|
2
|
-
|
|
3
|
-
import { useRefCallbackEvent } from '../../hooks/useRefCallbackEvent'
|
|
4
|
-
import { composeRefs } from '../_util/composeRefs'
|
|
5
|
-
import { Override } from '../_util/type'
|
|
6
|
-
|
|
7
|
-
export interface WheelObserverProps<T extends ElementType> {
|
|
8
|
-
/**
|
|
9
|
-
* React.ElementType
|
|
10
|
-
* @default div
|
|
11
|
-
*/
|
|
12
|
-
as?: T
|
|
13
|
-
|
|
14
|
-
children?: ReactNode
|
|
15
|
-
onWheel?: (event: WheelEvent) => void
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @example
|
|
20
|
-
* <WheelObserver
|
|
21
|
-
* onWheel={(event) => {
|
|
22
|
-
* event.preventDefault()
|
|
23
|
-
* console.log(event.deltaY)
|
|
24
|
-
* }
|
|
25
|
-
* >
|
|
26
|
-
* <div>Wheel here</div>
|
|
27
|
-
* </WheelObserver>
|
|
28
|
-
*/
|
|
29
|
-
export function WheelObserver<T extends ElementType = 'div'>(
|
|
30
|
-
props: Override<WheelObserverProps<T>, ComponentProps<T>>,
|
|
31
|
-
) {
|
|
32
|
-
const { as: Component = 'div', children, onWheel, ref, ...attributes } = props
|
|
33
|
-
|
|
34
|
-
const wheelRefCallback = useRefCallbackEvent(
|
|
35
|
-
'wheel',
|
|
36
|
-
(event) => {
|
|
37
|
-
if (onWheel) onWheel(event)
|
|
38
|
-
},
|
|
39
|
-
{ passive: false },
|
|
40
|
-
[onWheel],
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<Component ref={composeRefs(ref, wheelRefCallback)} {...attributes}>
|
|
45
|
-
{children}
|
|
46
|
-
</Component>
|
|
47
|
-
)
|
|
48
|
-
}
|