@tremolo-ui/react 0.3.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 +1162 -1554
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +58 -93
- package/dist/index.d.cts +592 -278
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +592 -278
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1163 -1555
- package/dist/index.js.map +1 -1
- package/package.json +12 -13
- 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 +95 -83
- 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 +117 -52
- 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 +136 -170
- 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 +193 -250
- package/src/hooks/useDrag.ts +41 -76
- package/src/hooks/useDragValue.ts +149 -0
- package/src/hooks/useEventListener.ts +1 -1
- package/src/hooks/useWheel.ts +51 -0
- package/src/index.ts +15 -13
- package/src/components/AnimationCanvas/canvas.ts +0 -46
- package/src/components/DragObserver/index.tsx +0 -58
- package/src/components/NumberInput/InternalInput.tsx +0 -221
- 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/WheelObserver/index.tsx +0 -48
- package/src/components/_util/type.ts +0 -1
- package/src/hooks/useDragWithElement.ts +0 -72
- package/src/hooks/usePianoDrag.ts +0 -75
- package/src/hooks/useRefCallbackEvent.ts +0 -34
- /package/src/hooks/{useCallbackRef.ts → _internal/useCallbackRef.ts} +0 -0
package/src/hooks/useDrag.ts
CHANGED
|
@@ -1,93 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { createDrag } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
import { useCallbackRef } from './_internal/useCallbackRef'
|
|
5
6
|
|
|
6
7
|
interface UseDragProps {
|
|
8
|
+
/**
|
|
9
|
+
* Threshold at which the onDrag event fires.
|
|
10
|
+
* Prevents onDrag events from firing, for example, when double-clicking.
|
|
11
|
+
*/
|
|
7
12
|
threshold?: number
|
|
8
13
|
|
|
14
|
+
/** CSS cursor to show while dragging. Applied to the element itself. */
|
|
15
|
+
cursor?: string
|
|
16
|
+
|
|
9
17
|
onDrag?: (x: number, y: number, deltaX: number, deltaY: number) => void
|
|
10
18
|
onDragStart?: () => void
|
|
11
19
|
onDragEnd?: () => void
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
/**
|
|
15
|
-
*
|
|
23
|
+
* Track a pointer drag on an element.
|
|
24
|
+
*
|
|
25
|
+
* @returns a ref callback to attach to the element being dragged
|
|
16
26
|
*/
|
|
17
27
|
export function useDrag<T extends Element>({
|
|
18
|
-
threshold
|
|
28
|
+
threshold = 1,
|
|
29
|
+
cursor,
|
|
19
30
|
onDrag,
|
|
20
31
|
onDragStart,
|
|
21
32
|
onDragEnd,
|
|
22
|
-
}: UseDragProps):
|
|
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
|
-
if (dragOffsetX.current) {
|
|
49
|
-
deltaX = screenX - dragOffsetX.current
|
|
50
|
-
dragOffsetX.current = screenX
|
|
51
|
-
}
|
|
52
|
-
if (dragOffsetY.current) {
|
|
53
|
-
deltaY = screenY - dragOffsetY.current
|
|
54
|
-
dragOffsetY.current = screenY
|
|
55
|
-
}
|
|
56
|
-
if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold) return
|
|
57
|
-
onDrag?.(
|
|
58
|
-
screenX - dragStartX.current,
|
|
59
|
-
screenY - dragStartY.current,
|
|
60
|
-
deltaX,
|
|
61
|
-
deltaY,
|
|
62
|
-
)
|
|
63
|
-
},
|
|
64
|
-
[threshold, onDrag],
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
const pointerDownHandler = useCallback(
|
|
68
|
-
(event: React.PointerEvent<T>) => {
|
|
69
|
-
dragOffsetX.current = event.screenX
|
|
70
|
-
dragOffsetY.current = event.screenY
|
|
71
|
-
handleDrag(event, true)
|
|
72
|
-
onDragStart?.()
|
|
73
|
-
},
|
|
74
|
-
[handleDrag, onDragStart],
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
const refHandler = useRefCallbackEvent(
|
|
78
|
-
'touchmove',
|
|
79
|
-
handleDrag,
|
|
80
|
-
{ passive: false },
|
|
81
|
-
[onDrag],
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
useEventListener(globalThis.window, 'mousemove', handleDrag)
|
|
85
|
-
|
|
86
|
-
useEventListener(globalThis.window, 'pointerup', () => {
|
|
87
|
-
dragOffsetX.current = undefined
|
|
88
|
-
dragOffsetY.current = undefined
|
|
89
|
-
onDragEnd?.()
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
return [refHandler, pointerDownHandler]
|
|
33
|
+
}: UseDragProps): (node: T | null) => void {
|
|
34
|
+
const dragHandler = useCallbackRef(onDrag)
|
|
35
|
+
const dragStartHandler = useCallbackRef(onDragStart)
|
|
36
|
+
const dragEndHandler = useCallbackRef(onDragEnd)
|
|
37
|
+
|
|
38
|
+
// The node is held in state rather than bound in the ref callback itself, so
|
|
39
|
+
// that re-attaching the ref with the same node does not restart the drag.
|
|
40
|
+
// React re-attaches on every render when the caller passes an inline ref.
|
|
41
|
+
const [node, setNode] = useState<T | null>(null)
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!node) return
|
|
45
|
+
|
|
46
|
+
const instance = createDrag(node, {
|
|
47
|
+
threshold,
|
|
48
|
+
cursor,
|
|
49
|
+
onDragStart: () => dragStartHandler(),
|
|
50
|
+
onDrag: ({ x, y, deltaX, deltaY }) => dragHandler(x, y, deltaX, deltaY),
|
|
51
|
+
onDragEnd: () => dragEndHandler(),
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return () => instance.destroy()
|
|
55
|
+
}, [node, threshold, cursor, dragHandler, dragStartHandler, dragEndHandler])
|
|
56
|
+
|
|
57
|
+
return setNode
|
|
93
58
|
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { RefObject, useEffect, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createDragValue,
|
|
5
|
+
elementMapping,
|
|
6
|
+
relativeMapping,
|
|
7
|
+
type AxisOptions,
|
|
8
|
+
type DragValueInstance,
|
|
9
|
+
type XY,
|
|
10
|
+
type XYInput,
|
|
11
|
+
} from '@tremolo-ui/dom'
|
|
12
|
+
|
|
13
|
+
import { useCallbackRef } from './_internal/useCallbackRef'
|
|
14
|
+
|
|
15
|
+
export interface UseDragValueOptions {
|
|
16
|
+
/** Scaling of each axis; a single value applies to both. */
|
|
17
|
+
axis: XYInput<AxisOptions>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Normalize the pointer against the bounding rect of this element, so that
|
|
21
|
+
* the value *is* the position pointed at.
|
|
22
|
+
*
|
|
23
|
+
* Give either this or `getValue`.
|
|
24
|
+
*/
|
|
25
|
+
baseElementRef?: RefObject<Element | null>
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Move the value away from where it stood when the drag started, by the
|
|
29
|
+
* distance dragged. The pointer position itself carries no meaning.
|
|
30
|
+
*
|
|
31
|
+
* Give either this or `baseElementRef`.
|
|
32
|
+
*/
|
|
33
|
+
getValue?: () => XY<number>
|
|
34
|
+
/**
|
|
35
|
+
* Pixels of movement that span the whole range, with `getValue`.
|
|
36
|
+
* @default 100
|
|
37
|
+
*/
|
|
38
|
+
pixelRange?: XYInput<number>
|
|
39
|
+
|
|
40
|
+
/** @see DragValueOptions.updateOnPointerDown */
|
|
41
|
+
updateOnPointerDown?: boolean
|
|
42
|
+
/** @default 0 */
|
|
43
|
+
threshold?: number
|
|
44
|
+
/** CSS cursor to show while dragging. Applied to the element itself. */
|
|
45
|
+
cursor?: string
|
|
46
|
+
|
|
47
|
+
onChange?: (value: XY<number>) => void
|
|
48
|
+
onDragStart?: (value: XY<number>) => void
|
|
49
|
+
onDragEnd?: (value: XY<number>) => void
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Drive a value with a pointer drag.
|
|
54
|
+
*
|
|
55
|
+
* @returns a ref callback for the element that starts the drag, and whether a drag is in progress
|
|
56
|
+
*/
|
|
57
|
+
export function useDragValue<T extends Element>(
|
|
58
|
+
options: UseDragValueOptions,
|
|
59
|
+
): { refCallback: (node: T | null) => void; dragging: boolean } {
|
|
60
|
+
const {
|
|
61
|
+
axis,
|
|
62
|
+
baseElementRef,
|
|
63
|
+
pixelRange,
|
|
64
|
+
updateOnPointerDown,
|
|
65
|
+
threshold,
|
|
66
|
+
cursor,
|
|
67
|
+
} = options
|
|
68
|
+
|
|
69
|
+
if (!baseElementRef && !options.getValue) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
'useDragValue: give either baseElementRef or getValue, so that the drag has something to move',
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const [dragging, setDragging] = useState(false)
|
|
76
|
+
|
|
77
|
+
const valueGetter = useCallbackRef(options.getValue)
|
|
78
|
+
const changeHandler = useCallbackRef(options.onChange)
|
|
79
|
+
const dragStartHandler = useCallbackRef(options.onDragStart)
|
|
80
|
+
const dragEndHandler = useCallbackRef(options.onDragEnd)
|
|
81
|
+
|
|
82
|
+
// Read when the instance is created. The effect below keeps it current, and
|
|
83
|
+
// runs right after, so a stale setting is replaced within the same commit.
|
|
84
|
+
const latest = useRef(options)
|
|
85
|
+
|
|
86
|
+
const instanceRef = useRef<DragValueInstance | null>(null)
|
|
87
|
+
|
|
88
|
+
// See useDrag for why the node is held in state.
|
|
89
|
+
const [node, setNode] = useState<T | null>(null)
|
|
90
|
+
|
|
91
|
+
const [pixelRangeX, pixelRangeY] = Array.isArray(pixelRange)
|
|
92
|
+
? pixelRange
|
|
93
|
+
: [pixelRange, pixelRange]
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!node) return
|
|
97
|
+
|
|
98
|
+
const instance = createDragValue(node, {
|
|
99
|
+
axis: latest.current.axis,
|
|
100
|
+
mapping: baseElementRef
|
|
101
|
+
? elementMapping(() => baseElementRef.current)
|
|
102
|
+
: relativeMapping({
|
|
103
|
+
pixelRange: [pixelRangeX ?? 100, pixelRangeY ?? 100],
|
|
104
|
+
}),
|
|
105
|
+
getValue: () => valueGetter() ?? [0, 0],
|
|
106
|
+
updateOnPointerDown: latest.current.updateOnPointerDown,
|
|
107
|
+
threshold: latest.current.threshold,
|
|
108
|
+
cursor: latest.current.cursor,
|
|
109
|
+
onChange: (value) => changeHandler(value),
|
|
110
|
+
onDragStart: (value) => {
|
|
111
|
+
setDragging(true)
|
|
112
|
+
dragStartHandler(value)
|
|
113
|
+
},
|
|
114
|
+
onDragEnd: (value) => {
|
|
115
|
+
setDragging(false)
|
|
116
|
+
dragEndHandler(value)
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
instanceRef.current = instance
|
|
120
|
+
|
|
121
|
+
return () => {
|
|
122
|
+
instanceRef.current = null
|
|
123
|
+
instance.destroy()
|
|
124
|
+
}
|
|
125
|
+
}, [
|
|
126
|
+
node,
|
|
127
|
+
baseElementRef,
|
|
128
|
+
pixelRangeX,
|
|
129
|
+
pixelRangeY,
|
|
130
|
+
valueGetter,
|
|
131
|
+
changeHandler,
|
|
132
|
+
dragStartHandler,
|
|
133
|
+
dragEndHandler,
|
|
134
|
+
])
|
|
135
|
+
|
|
136
|
+
// Runs after every render: the settings come from props and are cheap to
|
|
137
|
+
// push, and updating in place leaves a drag in progress untouched.
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
latest.current = options
|
|
140
|
+
instanceRef.current?.update({
|
|
141
|
+
axis,
|
|
142
|
+
updateOnPointerDown,
|
|
143
|
+
threshold,
|
|
144
|
+
cursor,
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return { refCallback: setNode, dragging }
|
|
149
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { RefObject, useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { createWheel, type WheelOptions } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
5
|
+
import { useCallbackRef } from './_internal/useCallbackRef'
|
|
6
|
+
|
|
7
|
+
export interface UseWheelOptions extends WheelOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Listen on this element rather than on the one the returned ref callback is
|
|
10
|
+
* attached to, and ignore that callback.
|
|
11
|
+
*
|
|
12
|
+
* For a control made of several movable parts: a wheel event only reaches
|
|
13
|
+
* what the cursor is over, so a listener per part responds only while the
|
|
14
|
+
* cursor is over that same part. Listening on the element they share lets
|
|
15
|
+
* every part see the event, and each one decide whether it is the one to act.
|
|
16
|
+
*
|
|
17
|
+
* The element is read when the listener is attached, so a ref filled in by a
|
|
18
|
+
* parent is fine as long as the parent is above in the tree.
|
|
19
|
+
*/
|
|
20
|
+
target?: RefObject<Element | null>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Listen to wheel events on an element.
|
|
25
|
+
*
|
|
26
|
+
* The listener is not passive, so the handler may call `preventDefault()`.
|
|
27
|
+
*
|
|
28
|
+
* @returns a ref callback to attach to the element, unused when `target` is given
|
|
29
|
+
*/
|
|
30
|
+
export function useWheel<T extends Element>(
|
|
31
|
+
onWheel: (event: WheelEvent) => void,
|
|
32
|
+
{ requireFocus = false, target }: UseWheelOptions = {},
|
|
33
|
+
): (node: T | null) => void {
|
|
34
|
+
const wheelHandler = useCallbackRef(onWheel)
|
|
35
|
+
|
|
36
|
+
// See useDrag for why the node is held in state.
|
|
37
|
+
const [node, setNode] = useState<T | null>(null)
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const element = target ? target.current : node
|
|
41
|
+
if (!element) return
|
|
42
|
+
|
|
43
|
+
const instance = createWheel(element, (event) => wheelHandler(event), {
|
|
44
|
+
requireFocus,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
return () => instance.destroy()
|
|
48
|
+
}, [node, target, wheelHandler, requireFocus])
|
|
49
|
+
|
|
50
|
+
return setNode
|
|
51
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -16,49 +16,49 @@ 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,
|
|
22
|
+
useNumberInputContext,
|
|
23
23
|
type NumberInputProps,
|
|
24
24
|
type NumberInputMethods,
|
|
25
|
+
type NumberInputFieldProps,
|
|
25
26
|
type StepperProps,
|
|
26
27
|
type IncrementStepperProps,
|
|
27
28
|
type DecrementStepperProps,
|
|
28
29
|
} from './components/NumberInput'
|
|
29
30
|
export {
|
|
30
31
|
Piano,
|
|
31
|
-
getNoteRangeArray,
|
|
32
32
|
SHORTCUTS,
|
|
33
33
|
type PianoProps,
|
|
34
34
|
type PianoMethods,
|
|
35
|
-
type
|
|
36
|
-
type
|
|
37
|
-
type
|
|
35
|
+
type KeyState,
|
|
36
|
+
type KeyAttributes,
|
|
37
|
+
type CSSVariables,
|
|
38
38
|
type KeyboardShortcuts,
|
|
39
39
|
} from './components/Piano'
|
|
40
40
|
export {
|
|
41
41
|
PointsEditor,
|
|
42
|
+
usePointsEditorContext,
|
|
42
43
|
clampPoint,
|
|
43
44
|
type PointsEditorProps,
|
|
45
|
+
type PointsEditorContextValue,
|
|
46
|
+
type PointsEditorBackgroundProps,
|
|
47
|
+
type PointsEditorContainerProps,
|
|
44
48
|
type PointProps,
|
|
45
49
|
type PointBaseType,
|
|
46
50
|
} from './components/PointsEditor'
|
|
47
51
|
export {
|
|
48
52
|
Slider,
|
|
49
53
|
useSliderContext,
|
|
50
|
-
type
|
|
51
|
-
type
|
|
54
|
+
type MarksOptionProps,
|
|
55
|
+
type MarksProps,
|
|
52
56
|
type SliderMethods,
|
|
53
57
|
type SliderProps,
|
|
54
58
|
type SliderThumbMethods,
|
|
55
59
|
type SliderThumbProps,
|
|
56
60
|
type SliderTrackProps,
|
|
57
61
|
} from './components/Slider'
|
|
58
|
-
export {
|
|
59
|
-
WheelObserver,
|
|
60
|
-
type WheelObserverProps,
|
|
61
|
-
} from './components/WheelObserver'
|
|
62
62
|
export {
|
|
63
63
|
XYPad,
|
|
64
64
|
type XYPadProps,
|
|
@@ -66,13 +66,14 @@ export {
|
|
|
66
66
|
type XYPadAreaProps,
|
|
67
67
|
type XYPadThumbProps,
|
|
68
68
|
type XYPadThumbMethods,
|
|
69
|
-
type
|
|
69
|
+
type XY,
|
|
70
|
+
type XYInput,
|
|
70
71
|
} from './components/XYPad'
|
|
71
72
|
|
|
72
73
|
// hooks
|
|
73
74
|
export { useAnimationFrame } from './hooks/useAnimationFrame'
|
|
74
75
|
export { useDrag } from './hooks/useDrag'
|
|
75
|
-
export {
|
|
76
|
+
export { useDragValue, type UseDragValueOptions } from './hooks/useDragValue'
|
|
76
77
|
export { useEventListener } from './hooks/useEventListener'
|
|
77
78
|
export { useInterval } from './hooks/useInterval'
|
|
78
79
|
export { useLongPress } from './hooks/useLongPress'
|
|
@@ -84,3 +85,4 @@ export {
|
|
|
84
85
|
} from './hooks/useMIDIAccess'
|
|
85
86
|
export { useMIDIInput } from './hooks/useMIDIInput'
|
|
86
87
|
export { useMIDIMessage } from './hooks/useMIDIMessage'
|
|
88
|
+
export { useWheel, type UseWheelOptions } from './hooks/useWheel'
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
|
|
2
|
-
export const drawingState = [
|
|
3
|
-
'strokeStyle',
|
|
4
|
-
'fillStyle',
|
|
5
|
-
'globalAlpha',
|
|
6
|
-
'lineWidth',
|
|
7
|
-
'lineCap',
|
|
8
|
-
'lineJoin',
|
|
9
|
-
'miterLimit',
|
|
10
|
-
'lineDashOffset',
|
|
11
|
-
'shadowOffsetX',
|
|
12
|
-
'shadowOffsetY',
|
|
13
|
-
'shadowBlur',
|
|
14
|
-
'shadowColor',
|
|
15
|
-
'globalCompositeOperation',
|
|
16
|
-
'font',
|
|
17
|
-
'textAlign',
|
|
18
|
-
'textBaseline',
|
|
19
|
-
'direction',
|
|
20
|
-
'imageSmoothingEnabled',
|
|
21
|
-
] as const
|
|
22
|
-
|
|
23
|
-
export type DrawingState = (typeof drawingState)[number]
|
|
24
|
-
export type DrawingStateValue = CanvasRenderingContext2D[DrawingState]
|
|
25
|
-
export type DrawingContext = Pick<CanvasRenderingContext2D, DrawingState>
|
|
26
|
-
|
|
27
|
-
export function isDrawingState(obj: unknown): obj is DrawingState {
|
|
28
|
-
const d: string[] = [...drawingState]
|
|
29
|
-
return typeof obj == 'string' && d.includes(obj)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function setDprConfig(
|
|
33
|
-
canvas: HTMLCanvasElement,
|
|
34
|
-
context: CanvasRenderingContext2D,
|
|
35
|
-
width: number,
|
|
36
|
-
height: number,
|
|
37
|
-
dpr: number,
|
|
38
|
-
) {
|
|
39
|
-
canvas.width = width * dpr
|
|
40
|
-
canvas.height = height * dpr
|
|
41
|
-
// Reset current transformation matrix to the identity matrix
|
|
42
|
-
context.setTransform(1, 0, 0, 1, 0, 0)
|
|
43
|
-
context.scale(dpr, dpr)
|
|
44
|
-
canvas.style.width = `${width}px`
|
|
45
|
-
canvas.style.height = `${height}px`
|
|
46
|
-
}
|
|
@@ -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
|
-
}
|