@tremolo-ui/react 0.1.6 → 0.2.1

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.
Files changed (68) hide show
  1. package/dist/index.cjs +1329 -1152
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +238 -183
  4. package/dist/index.d.cts +367 -366
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.ts +367 -366
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1313 -1125
  9. package/dist/index.js.map +1 -1
  10. package/package.json +17 -19
  11. package/src/components/AnimationCanvas/index.tsx +0 -7
  12. package/src/components/DragObserver/index.tsx +9 -7
  13. package/src/components/Knob/ActiveLine.tsx +29 -0
  14. package/src/components/Knob/InactiveLine.tsx +53 -0
  15. package/src/components/Knob/SVGRoot.tsx +39 -0
  16. package/src/components/Knob/Thumb.tsx +64 -0
  17. package/src/components/Knob/context.tsx +120 -0
  18. package/src/components/Knob/index.css +25 -27
  19. package/src/components/Knob/index.tsx +110 -164
  20. package/src/components/NumberInput/DecrementStepper.tsx +0 -2
  21. package/src/components/NumberInput/IncrementStepper.tsx +0 -2
  22. package/src/components/NumberInput/InternalInput.tsx +3 -1
  23. package/src/components/NumberInput/Stepper.tsx +0 -2
  24. package/src/components/NumberInput/context.tsx +0 -1
  25. package/src/components/NumberInput/index.css +31 -15
  26. package/src/components/NumberInput/index.tsx +21 -14
  27. package/src/components/Piano/KeyLabel.tsx +0 -2
  28. package/src/components/Piano/context.tsx +0 -2
  29. package/src/components/Piano/index.css +7 -14
  30. package/src/components/Piano/index.tsx +14 -15
  31. package/src/components/Piano/key.tsx +0 -10
  32. package/src/components/Piano/keyboardShortcuts.ts +0 -2
  33. package/src/components/PointsEditor/Background.tsx +17 -0
  34. package/src/components/PointsEditor/Container.tsx +26 -0
  35. package/src/components/PointsEditor/Point.tsx +137 -0
  36. package/src/components/PointsEditor/context.tsx +91 -0
  37. package/src/components/PointsEditor/index.css +30 -0
  38. package/src/components/PointsEditor/index.tsx +129 -0
  39. package/src/components/Slider/Scale.tsx +0 -2
  40. package/src/components/Slider/ScaleOption.tsx +0 -2
  41. package/src/components/Slider/Thumb.tsx +1 -5
  42. package/src/components/Slider/Track.tsx +1 -3
  43. package/src/components/Slider/context.tsx +0 -1
  44. package/src/components/Slider/index.css +33 -8
  45. package/src/components/Slider/index.tsx +61 -33
  46. package/src/components/WheelObserver/index.tsx +17 -19
  47. package/src/components/XYPad/Area.tsx +4 -5
  48. package/src/components/XYPad/Thumb.tsx +8 -7
  49. package/src/components/XYPad/index.css +18 -4
  50. package/src/components/XYPad/index.tsx +71 -38
  51. package/src/components/_util/composeRefs.tsx +63 -0
  52. package/src/components/_util/index.ts +23 -6
  53. package/src/components/_util/type.ts +1 -0
  54. package/src/hooks/useAnimationFrame.ts +0 -3
  55. package/src/hooks/useCallbackRef.ts +2 -2
  56. package/src/hooks/useDrag.ts +12 -11
  57. package/src/hooks/useDragWithElement.ts +12 -17
  58. package/src/hooks/useEventListener.ts +6 -9
  59. package/src/hooks/useInterval.ts +0 -3
  60. package/src/hooks/useLongPress.ts +0 -3
  61. package/src/hooks/useMIDIAccess.ts +0 -1
  62. package/src/hooks/useMIDIInput.ts +0 -1
  63. package/src/hooks/useMIDIMessage.ts +0 -1
  64. package/src/hooks/usePianoDrag.ts +75 -0
  65. package/src/index.ts +21 -30
  66. package/src/styles/global.css +16 -4
  67. package/dist/index.css.map +0 -1
  68. package/src/components/AnimationKnob/index.tsx +0 -314
@@ -1,314 +0,0 @@
1
- import clsx from 'clsx'
2
- import React, {
3
- ComponentPropsWithoutRef,
4
- CSSProperties,
5
- forwardRef,
6
- useCallback,
7
- useImperativeHandle,
8
- useRef,
9
- } from 'react'
10
-
11
- import {
12
- clamp,
13
- normalizeValue,
14
- radian,
15
- rawValue,
16
- stepValue,
17
- InputEventOption,
18
- } from '@tremolo-ui/functions'
19
-
20
- import { useDrag } from '../../hooks/useDrag'
21
- import { useRefCallbackEvent } from '../../hooks/useRefCallbackEvent'
22
- import { addNoSelect, removeNoSelect } from '../_util'
23
- import { AnimationCanvas, DrawFunction } from '../AnimationCanvas'
24
-
25
- /** @category AnimationKnob */
26
- export type AnimationKnobProps = {
27
- // required
28
- value: number
29
- min: number
30
- max: number
31
-
32
- // optional
33
- step?: number
34
- skew?: number // | SkewFunction
35
- /**
36
- * value set when double-clicking
37
- * @see enableDoubleClickDefault
38
- */
39
- defaultValue?: number
40
-
41
- /**
42
- * Value to be used as the starting point of the line when drawing.
43
- * @default min
44
- */
45
- startValue?: number
46
-
47
- /** Priority over width or height */
48
- size?: number | `${number}%`
49
-
50
- width?: number | `${number}%`
51
- height?: number | `${number}%`
52
-
53
- /** active line color */
54
- activeColor?: string
55
- /** inactive line color */
56
- inactiveColor?: string
57
- /** background color */
58
- bg?: string
59
- /** thumb color */
60
- thumb?: string
61
- lineWeight?: number
62
- thumbWeight?: number
63
-
64
- draw?: DrawFunction
65
-
66
- /** Whether to apply `{use-select: none}` when dragging */
67
- bodyNoSelect?: boolean
68
- /**
69
- * wheel control option
70
- */
71
- wheel?: InputEventOption | null
72
- /**
73
- * keyboard control option
74
- */
75
- keyboard?: InputEventOption | null
76
- enableDoubleClickDefault?: boolean
77
- style?: CSSProperties
78
- onChange?: (value: number) => void
79
- }
80
-
81
- /**
82
- * @category AnimationKnob
83
- */
84
- export interface AnimationKnobMethods {
85
- focus: () => void
86
- blur: () => void
87
- }
88
-
89
- type Props = AnimationKnobProps &
90
- Omit<ComponentPropsWithoutRef<'div'>, keyof AnimationKnobProps>
91
-
92
- /**
93
- * Animatable rotary knob.
94
- * @category AnimationKnob
95
- */
96
- export const AnimationKnob = forwardRef<AnimationKnobMethods, Props>(
97
- (
98
- {
99
- value,
100
- min,
101
- max,
102
- defaultValue = min,
103
- startValue = min,
104
- step = 1,
105
- skew = 1,
106
- size,
107
- width = 50,
108
- height = 50,
109
- activeColor = '#7998ec',
110
- inactiveColor = '#eee',
111
- bg = '#ccc',
112
- thumb = '#4e76e6',
113
- lineWeight = 6,
114
- thumbWeight = 4,
115
- draw,
116
- bodyNoSelect = true,
117
- wheel = ['raw', 1],
118
- keyboard = ['raw', 1],
119
- enableDoubleClickDefault = true,
120
- style,
121
- className,
122
- onChange,
123
- ...props
124
- }: Props,
125
- ref,
126
- ) => {
127
- // -- state and ref ---
128
- const valueRef = useRef(0)
129
- const elmRef = useRef<HTMLDivElement>(null)
130
-
131
- // --- interpret props ---
132
- const isRelativeSize =
133
- typeof (size ?? width) == 'string' || typeof height == 'string'
134
-
135
- const onDrag = useCallback(
136
- (_x: number, y: number) => {
137
- if (!onChange) return
138
- const v = rawValue(valueRef.current - y / 100, min, max, skew)
139
- const v2 = clamp(stepValue(v, step), min, max)
140
- onChange(v2)
141
- },
142
- [max, min, onChange, skew, step],
143
- )
144
-
145
- const handleKeyDown = useCallback(
146
- (event: React.KeyboardEvent<HTMLDivElement>) => {
147
- if (!keyboard || !onChange) return
148
- const key = event.key
149
- if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
150
- event.preventDefault()
151
- const x =
152
- key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1]
153
- let v
154
- if (keyboard[0] == 'normalized') {
155
- const n = normalizeValue(value, min, max, skew)
156
- v = rawValue(n + x, min, max, skew)
157
- } else {
158
- v = value + x
159
- }
160
- onChange(clamp(stepValue(v, step), min, max))
161
- }
162
- },
163
- [value, min, max, step, skew, keyboard, onChange],
164
- )
165
-
166
- // --- hooks ---
167
- const [touchMoveRefCallback, pointerDownHandler] = useDrag<HTMLDivElement>({
168
- onDrag: onDrag,
169
- onDragStart: () => {
170
- valueRef.current = normalizeValue(value, min, max, skew)
171
- if (bodyNoSelect) addNoSelect()
172
- },
173
- onDragEnd: () => {
174
- if (bodyNoSelect) removeNoSelect()
175
- },
176
- })
177
-
178
- const wheelRefCallback = useRefCallbackEvent(
179
- 'wheel',
180
- (event) => {
181
- if (!wheel) return
182
- event.preventDefault()
183
- const x = event.deltaY > 0 ? -wheel[1] : wheel[1]
184
- let v
185
- if (wheel[0] == 'normalized') {
186
- const n = normalizeValue(value, min, max, skew)
187
- v = rawValue(n + x, min, max, skew)
188
- } else {
189
- v = value + x
190
- }
191
- if (onChange) onChange(clamp(stepValue(v, step), min, max))
192
- },
193
- { passive: false },
194
- [wheel, value, min, max, skew, step, onChange],
195
- )
196
-
197
- useImperativeHandle(ref, () => {
198
- return {
199
- focus() {
200
- elmRef.current?.focus()
201
- },
202
- blur() {
203
- elmRef.current?.blur()
204
- },
205
- }
206
- }, [])
207
-
208
- return (
209
- <div
210
- className={clsx('tremolo-animation-knob', className)}
211
- ref={(div) => {
212
- elmRef.current = div
213
- wheelRefCallback(div)
214
- touchMoveRefCallback(div)
215
- }}
216
- role="slider"
217
- tabIndex={0}
218
- aria-valuenow={value}
219
- aria-valuemax={max}
220
- aria-valuemin={min}
221
- style={{
222
- display: 'inline-block',
223
- width: size ?? width,
224
- height: size ?? height,
225
- cursor: 'pointer',
226
- WebkitTapHighlightColor: 'transparent',
227
- ...style,
228
- }}
229
- onPointerDown={pointerDownHandler}
230
- onDoubleClick={() => {
231
- if (enableDoubleClickDefault && onChange) {
232
- onChange(defaultValue)
233
- }
234
- }}
235
- onKeyDown={handleKeyDown}
236
- {...props}
237
- >
238
- <AnimationCanvas
239
- {...(isRelativeSize
240
- ? { relativeSize: true }
241
- : {
242
- width: size ?? width,
243
- height: size ?? height,
244
- })}
245
- draw={
246
- draw
247
- ? draw
248
- : (ctx, { width, height }) => {
249
- const p = normalizeValue(value, min, max, skew)
250
- const startP = normalizeValue(startValue, min, max, skew)
251
- const cx = width / 2
252
- const cy = height / 2
253
- const r = Math.min(cx, cy)
254
- if (r <= 0) return
255
-
256
- // reset
257
- ctx.clearRect(0, 0, width, height)
258
- ctx.lineCap = 'butt'
259
-
260
- // bg
261
- ctx.beginPath()
262
- ctx.fillStyle = bg
263
- ctx.arc(cx, cy, r - 0.5, 0, 2 * Math.PI)
264
- ctx.fill()
265
-
266
- ctx.lineWidth = lineWeight
267
-
268
- // inactive rotary
269
- ctx.beginPath()
270
- ctx.strokeStyle = inactiveColor
271
- ctx.arc(
272
- cx,
273
- cy,
274
- r - 0.5 - lineWeight / 2,
275
- radian(135),
276
- radian(45),
277
- )
278
- ctx.stroke()
279
-
280
- // active rotary
281
- ctx.beginPath()
282
- ctx.strokeStyle = activeColor
283
- ctx.arc(
284
- cx,
285
- cy,
286
- r - 0.5 - lineWeight / 2,
287
- radian(135 + 270 * startP),
288
- radian(135 + 270 * p),
289
- p < startP,
290
- )
291
- ctx.stroke()
292
-
293
- // thumb
294
- ctx.lineCap = 'round'
295
- ctx.beginPath()
296
- ctx.moveTo(cx, cy)
297
- ctx.lineTo(
298
- cx +
299
- Math.cos(radian(135 + 270 * p)) *
300
- (r - 0.5 - thumbWeight / 2),
301
- cy +
302
- Math.sin(radian(135 + 270 * p)) *
303
- (r - 0.5 - thumbWeight / 2),
304
- )
305
- ctx.strokeStyle = thumb
306
- ctx.lineWidth = thumbWeight
307
- ctx.stroke()
308
- }
309
- }
310
- />
311
- </div>
312
- )
313
- },
314
- )