@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,6 +1,16 @@
|
|
|
1
1
|
.tremolo-xy-pad {
|
|
2
|
+
/* Half the thumb size. The thumb overhangs the edges of the area, so the
|
|
3
|
+
pad reserves room for it. Override this when using a different size. */
|
|
4
|
+
--thumb-size: 22px;
|
|
5
|
+
|
|
2
6
|
display: inline-block;
|
|
7
|
+
margin: calc(var(--thumb-size) / 2);
|
|
3
8
|
cursor: pointer;
|
|
9
|
+
-webkit-tap-highlight-color: transparent;
|
|
10
|
+
|
|
11
|
+
&[aria-readonly='true'] {
|
|
12
|
+
cursor: not-allowed;
|
|
13
|
+
}
|
|
4
14
|
}
|
|
5
15
|
|
|
6
16
|
.tremolo-xy-pad-area {
|
|
@@ -19,6 +29,8 @@
|
|
|
19
29
|
.tremolo-xy-pad-thumb {
|
|
20
30
|
--color: oklch(59.5% 0.17548 266.373);
|
|
21
31
|
|
|
32
|
+
width: var(--thumb-size);
|
|
33
|
+
height: var(--thumb-size);
|
|
22
34
|
border-radius: 50%;
|
|
23
35
|
outline: none;
|
|
24
36
|
background-color: var(--color);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import clsx from 'clsx'
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
3
|
ComponentPropsWithoutRef,
|
|
4
4
|
forwardRef,
|
|
5
|
-
|
|
5
|
+
ReactNode,
|
|
6
6
|
Ref,
|
|
7
7
|
useCallback,
|
|
8
8
|
useImperativeHandle,
|
|
@@ -10,31 +10,23 @@ import React, {
|
|
|
10
10
|
useRef,
|
|
11
11
|
} from 'react'
|
|
12
12
|
|
|
13
|
+
import type { AxisOptions } from '@tremolo-ui/dom'
|
|
13
14
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
rawValue,
|
|
17
|
-
stepValue,
|
|
15
|
+
applyDelta,
|
|
16
|
+
linearScale,
|
|
18
17
|
toFixed,
|
|
19
18
|
InputEventOption,
|
|
20
|
-
|
|
19
|
+
type Scale,
|
|
21
20
|
} from '@tremolo-ui/functions'
|
|
22
21
|
|
|
23
|
-
import {
|
|
22
|
+
import { useDragValue } from '../../hooks/useDragValue'
|
|
24
23
|
import { useWheel } from '../../hooks/useWheel'
|
|
25
24
|
import { addUserSelectNone, Cursor, removeUserSelectNone } from '../_util'
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
import { Area, XYPadAreaProps } from './Area'
|
|
29
|
-
import { Thumb, XYPadThumbMethods, XYPadThumbProps } from './Thumb'
|
|
25
|
+
import { useComposedRefs } from '../_util/composeRefs'
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
reverse: false,
|
|
35
|
-
wheel: ['raw', 1] as InputEventOption,
|
|
36
|
-
keyboard: ['raw', 1] as InputEventOption,
|
|
37
|
-
}
|
|
27
|
+
import { Area } from './Area'
|
|
28
|
+
import { toXY, XY, XYInput, XYPadProvider } from './context'
|
|
29
|
+
import { Thumb, XYPadThumbMethods } from './Thumb'
|
|
38
30
|
|
|
39
31
|
const defaultExternalStyles: XYPadProps['externalStyles'] = {
|
|
40
32
|
userSelectNone: true,
|
|
@@ -43,30 +35,38 @@ const defaultExternalStyles: XYPadProps['externalStyles'] = {
|
|
|
43
35
|
|
|
44
36
|
/**
|
|
45
37
|
* Two-dimensional slider component.
|
|
38
|
+
*
|
|
39
|
+
* The per-axis settings mirror `Slider`, given as `[x, y]` tuples. A plain
|
|
40
|
+
* value applies to both axes.
|
|
46
41
|
*/
|
|
47
|
-
export interface
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
export interface XYPadProps {
|
|
43
|
+
value: XY<number>
|
|
44
|
+
min: XYInput<number>
|
|
45
|
+
max: XYInput<number>
|
|
46
|
+
|
|
47
|
+
step?: XYInput<number>
|
|
48
|
+
/**
|
|
49
|
+
* How the value of each axis is distributed across the travel.
|
|
50
|
+
*
|
|
51
|
+
* Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,
|
|
52
|
+
* `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or
|
|
53
|
+
* `skewScale(n)` for a value that has to match a JUCE parameter.
|
|
54
|
+
*
|
|
55
|
+
* @default linearScale
|
|
56
|
+
*/
|
|
57
|
+
scale?: XYInput<Scale>
|
|
58
|
+
reverse?: XYInput<boolean>
|
|
52
59
|
|
|
53
|
-
// optional
|
|
54
|
-
step?: number
|
|
55
|
-
skew?: number
|
|
56
|
-
reverse?: boolean
|
|
57
60
|
/**
|
|
58
|
-
* wheel control option
|
|
61
|
+
* wheel control option. Shift selects the x axis.
|
|
62
|
+
* If null, no event will be triggered
|
|
59
63
|
*/
|
|
60
64
|
wheel?: InputEventOption | null
|
|
61
65
|
/**
|
|
62
66
|
* keyboard control option
|
|
67
|
+
* If null, no event will be triggered
|
|
63
68
|
*/
|
|
64
69
|
keyboard?: InputEventOption | null
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export interface XYPadProps {
|
|
68
|
-
x: ValueOptions
|
|
69
|
-
y: ValueOptions
|
|
70
70
|
|
|
71
71
|
externalStyles?: {
|
|
72
72
|
userSelectNone?: boolean
|
|
@@ -76,11 +76,22 @@ export interface XYPadProps {
|
|
|
76
76
|
disabled?: boolean
|
|
77
77
|
readonly?: boolean
|
|
78
78
|
|
|
79
|
-
onChange?: (
|
|
80
|
-
onDragStart?: (
|
|
81
|
-
onDragEnd?: (
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
onChange?: (value: XY<number>) => void
|
|
80
|
+
onDragStart?: (value: XY<number>) => void
|
|
81
|
+
onDragEnd?: (value: XY<number>) => void
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The pad renders exactly what you compose here; there is no default
|
|
85
|
+
* markup to fall back to.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* <XYPad.Root value={[x, y]} min={0} max={100} onChange={setValue}>
|
|
89
|
+
* <XYPad.Area>
|
|
90
|
+
* <XYPad.Thumb />
|
|
91
|
+
* </XYPad.Area>
|
|
92
|
+
* </XYPad.Root>
|
|
93
|
+
*/
|
|
94
|
+
children: ReactNode
|
|
84
95
|
}
|
|
85
96
|
|
|
86
97
|
export interface XYPadMethods {
|
|
@@ -92,11 +103,23 @@ export interface XYPadMethods {
|
|
|
92
103
|
type Props = XYPadProps &
|
|
93
104
|
Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadProps>
|
|
94
105
|
|
|
106
|
+
/**
|
|
107
|
+
* The wheel only acts while the focus is inside, so that scrolling a page past
|
|
108
|
+
* the control does not change its value.
|
|
109
|
+
*/
|
|
110
|
+
const WHEEL_OPTIONS = { requireFocus: true }
|
|
111
|
+
|
|
95
112
|
export const Root = forwardRef<XYPadMethods, Props>(
|
|
96
113
|
(
|
|
97
114
|
{
|
|
98
|
-
|
|
99
|
-
|
|
115
|
+
value,
|
|
116
|
+
min: _min,
|
|
117
|
+
max: _max,
|
|
118
|
+
step: _step = 1,
|
|
119
|
+
scale: _scale = linearScale,
|
|
120
|
+
reverse: _reverse = false,
|
|
121
|
+
wheel = ['raw', 1],
|
|
122
|
+
keyboard = ['raw', 1],
|
|
100
123
|
className,
|
|
101
124
|
style,
|
|
102
125
|
externalStyles: _externalStyles,
|
|
@@ -114,178 +137,133 @@ export const Root = forwardRef<XYPadMethods, Props>(
|
|
|
114
137
|
}: Props,
|
|
115
138
|
forwardedRef,
|
|
116
139
|
) => {
|
|
117
|
-
const x = useMemo(() => {
|
|
118
|
-
return { ...defaultValueOptions, ..._x }
|
|
119
|
-
}, [_x])
|
|
120
|
-
const y = useMemo(() => {
|
|
121
|
-
return { ...defaultValueOptions, ..._y }
|
|
122
|
-
}, [_y])
|
|
123
|
-
|
|
124
140
|
// -- state and ref ---
|
|
125
141
|
const rootRef = useRef<HTMLDivElement>(null)
|
|
126
|
-
const
|
|
142
|
+
const areaRef = useRef<HTMLDivElement>(null)
|
|
127
143
|
const thumbRef = useRef<XYPadThumbMethods>(null)
|
|
128
144
|
|
|
129
145
|
// --- interpret props ---
|
|
130
146
|
const externalStyles = { ...defaultExternalStyles, ..._externalStyles }
|
|
131
147
|
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
} else {
|
|
147
|
-
throw new Error('only <XYPadThumb> or <XYPadArea>')
|
|
148
|
-
}
|
|
149
|
-
} else {
|
|
150
|
-
throw new Error('children is an invalid element.')
|
|
151
|
-
}
|
|
152
|
-
})
|
|
153
|
-
}
|
|
148
|
+
const min = useMemo(() => toXY(_min), [_min])
|
|
149
|
+
const max = useMemo(() => toXY(_max), [_max])
|
|
150
|
+
const step = useMemo(() => toXY(_step), [_step])
|
|
151
|
+
const scale = useMemo(() => toXY(_scale), [_scale])
|
|
152
|
+
const reverse = useMemo(() => toXY(_reverse), [_reverse])
|
|
153
|
+
|
|
154
|
+
const percent = useMemo((): XY<number> => {
|
|
155
|
+
const normalized = [0, 1].map((i) =>
|
|
156
|
+
scale[i].normalize(value[i], min[i], max[i]),
|
|
157
|
+
)
|
|
158
|
+
return [0, 1].map((i) =>
|
|
159
|
+
toFixed((reverse[i] ? 1 - normalized[i] : normalized[i]) * 100),
|
|
160
|
+
) as XY<number>
|
|
161
|
+
}, [value, min, max, scale, reverse])
|
|
154
162
|
|
|
155
163
|
// --- internal functions ---
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
x.reverse,
|
|
171
|
-
x.skew,
|
|
172
|
-
x.step,
|
|
173
|
-
y.max,
|
|
174
|
-
y.min,
|
|
175
|
-
y.reverse,
|
|
176
|
-
y.skew,
|
|
177
|
-
y.step,
|
|
178
|
-
],
|
|
164
|
+
// `AxisOptions` extends `ValueRange`, so the same pair also describes the
|
|
165
|
+
// scaling for `applyDelta`. Its `reverse` only concerns the drag, where
|
|
166
|
+
// positions follow the screen; the key and wheel handlers below flip the
|
|
167
|
+
// direction themselves.
|
|
168
|
+
const axis = useMemo(
|
|
169
|
+
(): XY<AxisOptions> =>
|
|
170
|
+
[0, 1].map((i) => ({
|
|
171
|
+
min: min[i],
|
|
172
|
+
max: max[i],
|
|
173
|
+
step: step[i],
|
|
174
|
+
scale: scale[i],
|
|
175
|
+
reverse: reverse[i],
|
|
176
|
+
})) as XY<AxisOptions>,
|
|
177
|
+
[min, max, step, scale, reverse],
|
|
179
178
|
)
|
|
180
179
|
|
|
181
|
-
const
|
|
182
|
-
(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
target.max,
|
|
193
|
-
target.skew,
|
|
194
|
-
)
|
|
195
|
-
v = rawValue(n + x, target.min, target.max, target.skew)
|
|
196
|
-
} else {
|
|
197
|
-
v = target.value + x
|
|
198
|
-
}
|
|
199
|
-
return clamp(stepValue(v, target.step), target.min, target.max)
|
|
200
|
-
},
|
|
201
|
-
[],
|
|
180
|
+
const withAxis = useCallback(
|
|
181
|
+
(axis: 0 | 1, next: number): XY<number> =>
|
|
182
|
+
axis == 0 ? [next, value[1]] : [value[0], next],
|
|
183
|
+
[value],
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
/** @param i 0 = x, 1 = y */
|
|
187
|
+
const nudge = useCallback(
|
|
188
|
+
(i: 0 | 1, direction: number, option: InputEventOption): XY<number> =>
|
|
189
|
+
withAxis(i, applyDelta(value[i], direction, option, axis[i])),
|
|
190
|
+
[value, axis, withAxis],
|
|
202
191
|
)
|
|
203
192
|
|
|
204
193
|
const handleKeyDown = useCallback(
|
|
205
194
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
206
|
-
if (!onChange || readonly) return
|
|
195
|
+
if (!onChange || readonly || !keyboard) return
|
|
207
196
|
const key = event.key
|
|
208
197
|
if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
|
|
209
|
-
const
|
|
210
|
-
const target = isHorizontal ? x : y
|
|
211
|
-
if (!target.keyboard) return
|
|
198
|
+
const i: 0 | 1 = key == 'ArrowRight' || key == 'ArrowLeft' ? 0 : 1
|
|
212
199
|
event.preventDefault()
|
|
213
|
-
let
|
|
214
|
-
if (key == 'ArrowLeft' || key == 'ArrowUp')
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
if (isHorizontal) {
|
|
218
|
-
onChange(newValue, y.value)
|
|
219
|
-
} else {
|
|
220
|
-
onChange(x.value, newValue)
|
|
221
|
-
}
|
|
200
|
+
let direction = 1
|
|
201
|
+
if (key == 'ArrowLeft' || key == 'ArrowUp') direction *= -1
|
|
202
|
+
if (reverse[i]) direction *= -1
|
|
203
|
+
onChange(nudge(i, direction, keyboard))
|
|
222
204
|
}
|
|
223
205
|
},
|
|
224
|
-
[onChange, readonly,
|
|
206
|
+
[onChange, readonly, keyboard, reverse, nudge],
|
|
225
207
|
)
|
|
226
208
|
|
|
227
209
|
// --- hooks ---
|
|
228
|
-
const { refCallback: dragRefCallback } = useDragWithElement<HTMLDivElement>(
|
|
229
|
-
{
|
|
230
|
-
baseElementRef: areaElementRef,
|
|
231
|
-
cursor: readonly ? undefined : externalStyles.cursor,
|
|
232
|
-
updateOnPointerDown: true,
|
|
233
|
-
onDrag: onDrag,
|
|
234
|
-
onDragStart: (nx, ny) => {
|
|
235
|
-
if (readonly) return
|
|
236
|
-
|
|
237
|
-
if (externalStyles.userSelectNone) addUserSelectNone()
|
|
238
|
-
|
|
239
|
-
thumbRef.current?.focus()
|
|
240
|
-
|
|
241
|
-
const valueX = clamp(
|
|
242
|
-
stepValue(rawValue(nx, x.min, x.max, x.skew), x.step),
|
|
243
|
-
x.min,
|
|
244
|
-
x.max,
|
|
245
|
-
)
|
|
246
|
-
const valueY = clamp(
|
|
247
|
-
stepValue(rawValue(ny, y.min, y.max, y.skew), y.step),
|
|
248
|
-
y.min,
|
|
249
|
-
y.max,
|
|
250
|
-
)
|
|
251
210
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
stepValue(rawValue(nx, x.min, x.max, x.skew), x.step),
|
|
261
|
-
x.min,
|
|
262
|
-
x.max,
|
|
263
|
-
)
|
|
264
|
-
const valueY = clamp(
|
|
265
|
-
stepValue(rawValue(ny, y.min, y.max, y.skew), y.step),
|
|
266
|
-
y.min,
|
|
267
|
-
y.max,
|
|
268
|
-
)
|
|
269
|
-
onDragEnd?.(valueX, valueY)
|
|
270
|
-
},
|
|
211
|
+
const { refCallback: dragRefCallback } = useDragValue<HTMLDivElement>({
|
|
212
|
+
axis,
|
|
213
|
+
baseElementRef: areaRef,
|
|
214
|
+
updateOnPointerDown: true,
|
|
215
|
+
cursor: readonly ? undefined : externalStyles.cursor,
|
|
216
|
+
onChange: (v) => {
|
|
217
|
+
if (readonly) return
|
|
218
|
+
onChange?.(v)
|
|
271
219
|
},
|
|
272
|
-
|
|
220
|
+
onDragStart: (v) => {
|
|
221
|
+
if (readonly) return
|
|
222
|
+
if (externalStyles.userSelectNone) addUserSelectNone()
|
|
223
|
+
thumbRef.current?.focus()
|
|
224
|
+
onDragStart?.(v)
|
|
225
|
+
},
|
|
226
|
+
onDragEnd: (v) => {
|
|
227
|
+
if (readonly) return
|
|
228
|
+
if (externalStyles.userSelectNone) removeUserSelectNone()
|
|
229
|
+
onDragEnd?.(v)
|
|
230
|
+
},
|
|
231
|
+
})
|
|
273
232
|
|
|
274
233
|
const wheelRefCallback = useWheel<HTMLDivElement>((event) => {
|
|
275
|
-
if (!onChange || readonly) return
|
|
276
|
-
const
|
|
277
|
-
if (!target.wheel) return
|
|
234
|
+
if (!onChange || readonly || !wheel) return
|
|
235
|
+
const i: 0 | 1 = event.shiftKey ? 0 : 1
|
|
278
236
|
event.preventDefault()
|
|
279
|
-
let
|
|
280
|
-
if (event.deltaY < 0)
|
|
281
|
-
if (
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
237
|
+
let direction = 1
|
|
238
|
+
if (event.deltaY < 0) direction *= -1
|
|
239
|
+
if (reverse[i]) direction *= -1
|
|
240
|
+
onChange(nudge(i, direction, wheel))
|
|
241
|
+
}, WHEEL_OPTIONS)
|
|
242
|
+
|
|
243
|
+
// Composed once, so React attaches the refs a single time instead of
|
|
244
|
+
// detaching and re-attaching on every render.
|
|
245
|
+
const rootRefCallback = useComposedRefs<HTMLDivElement>(
|
|
246
|
+
rootRef,
|
|
247
|
+
dragRefCallback,
|
|
248
|
+
wheelRefCallback,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
const context = useMemo(
|
|
252
|
+
() => ({
|
|
253
|
+
value,
|
|
254
|
+
min,
|
|
255
|
+
max,
|
|
256
|
+
step,
|
|
257
|
+
scale,
|
|
258
|
+
reverse,
|
|
259
|
+
disabled,
|
|
260
|
+
readonly,
|
|
261
|
+
percent,
|
|
262
|
+
areaRef,
|
|
263
|
+
thumbRef,
|
|
264
|
+
}),
|
|
265
|
+
[value, min, max, step, scale, reverse, disabled, readonly, percent],
|
|
266
|
+
)
|
|
289
267
|
|
|
290
268
|
useImperativeHandle(forwardedRef, () => {
|
|
291
269
|
return {
|
|
@@ -300,52 +278,33 @@ export const Root = forwardRef<XYPadMethods, Props>(
|
|
|
300
278
|
}, [])
|
|
301
279
|
|
|
302
280
|
return (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}}
|
|
329
|
-
{...props}
|
|
330
|
-
>
|
|
331
|
-
<div className="tremolo-xy-pad-area-wrapper" ref={areaElementRef}>
|
|
332
|
-
<Area
|
|
333
|
-
__thumb={
|
|
334
|
-
<Thumb
|
|
335
|
-
ref={thumbRef}
|
|
336
|
-
__disabled={disabled}
|
|
337
|
-
__readonly={readonly}
|
|
338
|
-
__css={{
|
|
339
|
-
top: `${percentY}%`,
|
|
340
|
-
left: `${percentX}%`,
|
|
341
|
-
}}
|
|
342
|
-
{...thumbProps}
|
|
343
|
-
/>
|
|
344
|
-
}
|
|
345
|
-
{...areaProps}
|
|
346
|
-
/>
|
|
281
|
+
<XYPadProvider value={context}>
|
|
282
|
+
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
|
283
|
+
<div
|
|
284
|
+
className={clsx('tremolo-xy-pad', className)}
|
|
285
|
+
ref={rootRefCallback}
|
|
286
|
+
tabIndex={-1}
|
|
287
|
+
aria-disabled={disabled}
|
|
288
|
+
aria-readonly={readonly}
|
|
289
|
+
style={style}
|
|
290
|
+
onPointerDown={onPointerDown}
|
|
291
|
+
onKeyDown={(event) => {
|
|
292
|
+
handleKeyDown(event)
|
|
293
|
+
onKeyDown?.(event)
|
|
294
|
+
}}
|
|
295
|
+
onFocus={(event) => {
|
|
296
|
+
thumbRef.current?.focus()
|
|
297
|
+
onFocus?.(event)
|
|
298
|
+
}}
|
|
299
|
+
onBlur={(event) => {
|
|
300
|
+
thumbRef.current?.blur()
|
|
301
|
+
onBlur?.(event)
|
|
302
|
+
}}
|
|
303
|
+
{...props}
|
|
304
|
+
>
|
|
305
|
+
{children}
|
|
347
306
|
</div>
|
|
348
|
-
</
|
|
307
|
+
</XYPadProvider>
|
|
349
308
|
)
|
|
350
309
|
},
|
|
351
310
|
)
|
|
@@ -361,3 +320,4 @@ export const XYPad = {
|
|
|
361
320
|
|
|
362
321
|
export { type XYPadThumbProps, type XYPadThumbMethods } from './Thumb'
|
|
363
322
|
export { type XYPadAreaProps } from './Area'
|
|
323
|
+
export { useXYPadContext, type XY, type XYInput } from './context'
|
package/src/hooks/useDrag.ts
CHANGED