@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.
Files changed (64) hide show
  1. package/dist/index.cjs +1162 -1554
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +58 -93
  4. package/dist/index.d.cts +592 -278
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.ts +592 -278
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1163 -1555
  9. package/dist/index.js.map +1 -1
  10. package/package.json +12 -13
  11. package/src/components/AnimationCanvas/index.tsx +84 -155
  12. package/src/components/Knob/ActiveLine.tsx +6 -6
  13. package/src/components/Knob/InactiveLine.tsx +9 -11
  14. package/src/components/Knob/SVGRoot.tsx +9 -18
  15. package/src/components/Knob/context.tsx +57 -89
  16. package/src/components/Knob/index.css +6 -2
  17. package/src/components/Knob/index.tsx +95 -83
  18. package/src/components/NumberInput/DecrementStepper.tsx +17 -50
  19. package/src/components/NumberInput/IncrementStepper.tsx +17 -50
  20. package/src/components/NumberInput/InputField.tsx +123 -0
  21. package/src/components/NumberInput/Stepper.tsx +90 -12
  22. package/src/components/NumberInput/context.tsx +74 -146
  23. package/src/components/NumberInput/index.css +38 -102
  24. package/src/components/NumberInput/index.tsx +252 -98
  25. package/src/components/NumberInput/stepperButton.tsx +90 -0
  26. package/src/components/Piano/index.tsx +272 -288
  27. package/src/components/Piano/keyboardShortcuts.ts +34 -4
  28. package/src/components/PointsEditor/Background.tsx +8 -2
  29. package/src/components/PointsEditor/Container.tsx +19 -9
  30. package/src/components/PointsEditor/Point.tsx +117 -52
  31. package/src/components/PointsEditor/context.tsx +28 -74
  32. package/src/components/PointsEditor/index.css +5 -0
  33. package/src/components/PointsEditor/index.tsx +77 -43
  34. package/src/components/Slider/{Scale.tsx → Marks.tsx} +8 -8
  35. package/src/components/Slider/{ScaleOption.tsx → MarksOption.tsx} +16 -18
  36. package/src/components/Slider/Thumb.tsx +55 -68
  37. package/src/components/Slider/Track.tsx +15 -15
  38. package/src/components/Slider/context.tsx +36 -51
  39. package/src/components/Slider/index.css +18 -5
  40. package/src/components/Slider/index.tsx +136 -170
  41. package/src/components/Slider/type.ts +4 -4
  42. package/src/components/XYPad/Area.tsx +16 -7
  43. package/src/components/XYPad/Thumb.tsx +62 -74
  44. package/src/components/XYPad/context.tsx +46 -0
  45. package/src/components/XYPad/index.css +12 -0
  46. package/src/components/XYPad/index.tsx +193 -250
  47. package/src/hooks/useDrag.ts +41 -76
  48. package/src/hooks/useDragValue.ts +149 -0
  49. package/src/hooks/useEventListener.ts +1 -1
  50. package/src/hooks/useWheel.ts +51 -0
  51. package/src/index.ts +15 -13
  52. package/src/components/AnimationCanvas/canvas.ts +0 -46
  53. package/src/components/DragObserver/index.tsx +0 -58
  54. package/src/components/NumberInput/InternalInput.tsx +0 -221
  55. package/src/components/NumberInput/type.ts +0 -58
  56. package/src/components/Piano/KeyLabel.tsx +0 -51
  57. package/src/components/Piano/context.tsx +0 -73
  58. package/src/components/Piano/key.tsx +0 -205
  59. package/src/components/WheelObserver/index.tsx +0 -48
  60. package/src/components/_util/type.ts +0 -1
  61. package/src/hooks/useDragWithElement.ts +0 -72
  62. package/src/hooks/usePianoDrag.ts +0 -75
  63. package/src/hooks/useRefCallbackEvent.ts +0 -34
  64. /package/src/hooks/{useCallbackRef.ts → _internal/useCallbackRef.ts} +0 -0
@@ -1,221 +0,0 @@
1
- import clsx from 'clsx'
2
- import {
3
- ComponentPropsWithoutRef,
4
- forwardRef,
5
- useCallback,
6
- useImperativeHandle,
7
- useMemo,
8
- useRef,
9
- useState,
10
- } from 'react'
11
-
12
- import {
13
- clamp,
14
- InputEventOption,
15
- normalizeValue,
16
- rawValue,
17
- stepValue,
18
- } from '@tremolo-ui/functions'
19
-
20
- import { useRefCallbackEvent } from '../../hooks/useRefCallbackEvent'
21
-
22
- import { safeClamp, useNumberInputContext } from './context'
23
- import { parseValue } from './type'
24
-
25
- import { NumberInputMethods } from '.'
26
-
27
- interface InternalProps {
28
- disabled: boolean
29
- readonly: boolean
30
- wheel: InputEventOption | null
31
- keyboard: InputEventOption | null
32
-
33
- selectWithFocus?: 'all' | 'number' | 'none'
34
- blurOnEnter?: boolean
35
- keepWithinRange?: boolean
36
- clampValueOnBlur?: boolean
37
-
38
- onChange?: (value: number, text: string) => void
39
- onFocus?: (
40
- value: number,
41
- text: string,
42
- event: React.FocusEvent<HTMLInputElement, Element>,
43
- ) => void
44
- onBlur?: (
45
- value: number,
46
- text: string,
47
- event: React.FocusEvent<HTMLInputElement, Element>,
48
- ) => void
49
- }
50
-
51
- type InternalPropsWithAttributes = InternalProps &
52
- Omit<ComponentPropsWithoutRef<'input'>, keyof InternalProps>
53
-
54
- export const InternalInput = forwardRef<
55
- NumberInputMethods,
56
- InternalPropsWithAttributes
57
- >(
58
- (
59
- {
60
- disabled,
61
- readonly,
62
- selectWithFocus,
63
- blurOnEnter,
64
- clampValueOnBlur,
65
- wheel,
66
- keyboard,
67
- className,
68
- onChange,
69
- onFocus,
70
- onBlur,
71
- onKeyDown,
72
- children: _children,
73
- ...props
74
- }: InternalPropsWithAttributes,
75
- ref,
76
- ) => {
77
- const elmRef = useRef<HTMLInputElement>(null)
78
- const [editing, setEditing] = useState(false)
79
- const value = useNumberInputContext((s) => s.value)
80
- const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber)
81
- const min = useNumberInputContext((s) => s.min)
82
- const max = useNumberInputContext((s) => s.max)
83
- const step = useNumberInputContext((s) => s.step)
84
- const units = useNumberInputContext((s) => s.units)
85
- const digit = useNumberInputContext((s) => s.digit)
86
- const change = useNumberInputContext((s) => s.change)
87
-
88
- const error = useMemo(() => {
89
- if (editing) return false
90
- const v = valueAsNumber
91
- return (min ?? -Infinity) > v || v > (max ?? Infinity)
92
- }, [editing, max, min, valueAsNumber])
93
-
94
- const updateValueByEvent = useCallback(
95
- (eventType: InputEventOption[0], x: number) => {
96
- let newValue
97
- if (eventType == 'normalized') {
98
- if (!min || !max) {
99
- throw new Error(
100
- '[NumberInput] "min" and "max" are required when InputEventOption[0] is set to "normalized".',
101
- )
102
- }
103
- const n = normalizeValue(valueAsNumber, min, max)
104
- newValue = rawValue(n + x, min, max)
105
- return clamp(stepValue(newValue, step), min, max)
106
- } else {
107
- newValue = valueAsNumber + x
108
- return stepValue(newValue, step)
109
- }
110
- },
111
- [max, min, step, valueAsNumber],
112
- )
113
-
114
- const handleKeyDown = useCallback(
115
- (event: React.KeyboardEvent<HTMLDivElement>) => {
116
- if (!keyboard || !onChange || readonly) return
117
- const key = event.key
118
- if (!['ArrowUp', 'ArrowDown'].includes(key)) return
119
- event.preventDefault()
120
- const x = key == 'ArrowUp' ? keyboard[1] : -keyboard[1]
121
- const parsed = parseValue(String(updateValueByEvent(keyboard[0], x)))
122
- // console.log('key control')
123
- onChange?.(parsed.rawValue, parsed.formatValue)
124
- },
125
- [keyboard, readonly, onChange, updateValueByEvent],
126
- )
127
-
128
- const wheelRefCallback = useRefCallbackEvent(
129
- 'wheel',
130
- (event) => {
131
- if (!wheel || readonly) return
132
- event.preventDefault()
133
- if (!onChange || event.deltaY == 0) return
134
- const x = Math.sign(event.deltaY) * -wheel[1]
135
- const parsed = parseValue(String(updateValueByEvent(wheel[0], x)))
136
- // console.log('wheel control')
137
- onChange?.(parsed.rawValue, parsed.formatValue)
138
- },
139
- { passive: false },
140
- [wheel, readonly, onChange, updateValueByEvent],
141
- )
142
-
143
- useImperativeHandle(ref, () => {
144
- return {
145
- focus() {
146
- elmRef.current?.focus()
147
- },
148
- blur() {
149
- elmRef.current?.blur()
150
- },
151
- }
152
- }, [])
153
-
154
- return (
155
- <input
156
- ref={(input) => {
157
- elmRef.current = input
158
- wheelRefCallback(input)
159
- }}
160
- className={clsx('tremolo-number-input', className)}
161
- value={value}
162
- min={min}
163
- max={max}
164
- step={step}
165
- type="text"
166
- aria-disabled={disabled}
167
- aria-readonly={readonly}
168
- tabIndex={-1}
169
- data-error={error}
170
- onChange={(event) => {
171
- if (readonly) return
172
- const v = event.currentTarget.value
173
- const cursorPos = event.target.selectionStart
174
- change(v)
175
- const valueAsNumber = parseValue(v, units, digit).rawValue
176
- onChange?.(valueAsNumber, v)
177
- if (cursorPos) {
178
- event.target.selectionStart = cursorPos
179
- event.target.selectionEnd = cursorPos
180
- }
181
- }}
182
- onFocus={(event) => {
183
- setEditing(true)
184
- const v = event.currentTarget.value
185
- const parsed = parseValue(v, units, digit)
186
- if (selectWithFocus == 'all') {
187
- event.currentTarget.setSelectionRange(0, v.length)
188
- } else if (selectWithFocus == 'number') {
189
- event.currentTarget.setSelectionRange(
190
- 0,
191
- parsed.formatValue.length - parsed.unit.length,
192
- )
193
- }
194
- onFocus?.(parsed.rawValue, parsed.formatValue, event)
195
- }}
196
- onBlur={(event) => {
197
- setEditing(false)
198
- // update value
199
- let v = parseValue(event.currentTarget.value, units, digit).rawValue
200
- if (clampValueOnBlur) {
201
- v = safeClamp(v, min, max)
202
- }
203
- const parsed = parseValue(String(v), units, digit)
204
- change(parsed.formatValue)
205
- if (clampValueOnBlur) {
206
- onChange?.(parsed.rawValue, parsed.formatValue)
207
- }
208
- onBlur?.(parsed.rawValue, parsed.formatValue, event)
209
- }}
210
- onKeyDown={(event) => {
211
- if (blurOnEnter && event.key == 'Enter') {
212
- event.currentTarget.blur()
213
- }
214
- handleKeyDown(event)
215
- onKeyDown?.(event)
216
- }}
217
- {...props}
218
- />
219
- )
220
- },
221
- )
@@ -1,58 +0,0 @@
1
- /**
2
- * [unit, scale][]
3
- * @example
4
- * [['Hz', 1], ['kHz', 1000]]
5
- */
6
- export type Units = [string, number][]
7
-
8
- export function selectUnit(units: Units, value: number): [string, number] {
9
- let i = 0
10
- for (; i < units.length; i++) {
11
- if (Math.abs(units[i][1]) > Math.abs(value)) break
12
- }
13
- return units[Math.max(0, i - 1)]
14
- }
15
-
16
- export function parseValue(
17
- inputString: string,
18
- units?: string | Units,
19
- digit?: number,
20
- ): {
21
- rawValue: number
22
- formatValue: string
23
- unit: string
24
- } {
25
- const str = inputString.trim()
26
- if (!units || typeof units == 'string') {
27
- const m = str.match(/-?\d+(\.\d+)?/)
28
- let v = Number(m?.[0] ?? '0')
29
- v = isNaN(v) ? 0 : v
30
- return {
31
- rawValue: v,
32
- formatValue: (digit != undefined ? v.toFixed(digit) : v) + (units ?? ''),
33
- unit: units ?? '',
34
- }
35
- }
36
-
37
- const unitList = units.map(([u, _s]) => u)
38
- const scaleList = units.map(([_u, s]) => s)
39
- let rawValue = 0
40
- let unit = unitList[0]
41
- let formatValue =
42
- (digit != undefined ? rawValue.toFixed(digit) : rawValue) + unit
43
- const m = str.match(/^(-?\d+(\.\d+)?)\s*(\w*)$/)
44
- if (m) {
45
- rawValue = Number(m[1]) || 0
46
- const uIndex = unitList.indexOf(m[3])
47
- rawValue *= uIndex != -1 ? scaleList[uIndex] : 1
48
- const [u, scale] = selectUnit(units, rawValue)
49
- const v = rawValue / scale
50
- formatValue = (digit != undefined ? v.toFixed(digit) : v) + u
51
- unit = u
52
- }
53
- return {
54
- rawValue: rawValue,
55
- formatValue: formatValue,
56
- unit: unit,
57
- }
58
- }
@@ -1,51 +0,0 @@
1
- import clsx from 'clsx'
2
- import { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react'
3
-
4
- import { usePianoContext } from './context'
5
-
6
- import { getNoteRangeArray } from '.'
7
-
8
- export interface KeyLabelProps {
9
- /**
10
- * override Piano.label
11
- */
12
- label?: (note: number, index: number) => ReactNode
13
-
14
- wrapperClassName?: string
15
- wrapperStyle?: CSSProperties
16
-
17
- /** @internal */
18
- __note?: number
19
- /** @internal */
20
- __label?: (note: number, index: number) => ReactNode
21
- }
22
-
23
- export function KeyLabel({
24
- label,
25
- className,
26
- wrapperClassName,
27
- wrapperStyle,
28
- __note,
29
- __label,
30
- ...props
31
- }: KeyLabelProps & Omit<ComponentPropsWithoutRef<'div'>, keyof KeyLabelProps>) {
32
- const noteRange = usePianoContext((s) => s.noteRange)
33
- const noteRangeArray = getNoteRangeArray(noteRange)
34
- const index = noteRangeArray.indexOf(__note!)
35
- const content = label
36
- ? label(__note!, index)
37
- : __label && __label(__note!, index)
38
-
39
- return (
40
- (content != undefined || content != null) && (
41
- <div
42
- className={clsx('tremolo-piano-key-label-wrapper', wrapperClassName)}
43
- style={wrapperStyle}
44
- >
45
- <div className={clsx('tremolo-piano-key-label', className)} {...props}>
46
- {content}
47
- </div>
48
- </div>
49
- )
50
- )
51
- }
@@ -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,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
- }
@@ -1 +0,0 @@
1
- export type Override<T, U> = T & Omit<U, keyof T>