@tremolo-ui/react 0.0.7 → 0.0.8
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/cjs/index.js +674 -253
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/AnimationCanvas/index.d.ts +16 -9
- package/dist/cjs/types/components/AnimationCanvas/index.d.ts.map +1 -1
- package/dist/cjs/types/components/AnimationKnob/index.d.ts +53 -0
- package/dist/cjs/types/components/AnimationKnob/index.d.ts.map +1 -0
- package/dist/cjs/types/components/DragObserver/index.d.ts +19 -0
- package/dist/cjs/types/components/DragObserver/index.d.ts.map +1 -0
- package/dist/cjs/types/components/Knob/index.d.ts +26 -26
- package/dist/cjs/types/components/Knob/index.d.ts.map +1 -1
- package/dist/cjs/types/components/NumberInput/index.d.ts +2 -5
- package/dist/cjs/types/components/NumberInput/index.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/BlackKey.d.ts +3 -3
- package/dist/cjs/types/components/Piano/BlackKey.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/KeyLabel.d.ts +5 -8
- package/dist/cjs/types/components/Piano/KeyLabel.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/WhiteKey.d.ts +3 -3
- package/dist/cjs/types/components/Piano/WhiteKey.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/index.d.ts +3 -4
- package/dist/cjs/types/components/Piano/index.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/key.d.ts +4 -7
- package/dist/cjs/types/components/Piano/key.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/keyboardShortcuts.d.ts.map +1 -1
- package/dist/cjs/types/components/Slider/Scale.d.ts +11 -0
- package/dist/cjs/types/components/Slider/Scale.d.ts.map +1 -0
- package/dist/cjs/types/components/Slider/ScaleOption.d.ts +25 -0
- package/dist/cjs/types/components/Slider/ScaleOption.d.ts.map +1 -0
- package/dist/cjs/types/components/Slider/Thumb.d.ts +5 -13
- package/dist/cjs/types/components/Slider/Thumb.d.ts.map +1 -1
- package/dist/cjs/types/components/Slider/Track.d.ts +10 -21
- package/dist/cjs/types/components/Slider/Track.d.ts.map +1 -1
- package/dist/cjs/types/components/Slider/context.d.ts +21 -0
- package/dist/cjs/types/components/Slider/context.d.ts.map +1 -0
- package/dist/cjs/types/components/Slider/index.d.ts +18 -12
- package/dist/cjs/types/components/Slider/index.d.ts.map +1 -1
- package/dist/cjs/types/components/WheelObserver/index.d.ts +24 -0
- package/dist/cjs/types/components/WheelObserver/index.d.ts.map +1 -0
- package/dist/cjs/types/components/XYPad/Area.d.ts +13 -0
- package/dist/cjs/types/components/XYPad/Area.d.ts.map +1 -0
- package/dist/cjs/types/components/XYPad/Thumb.d.ts +19 -0
- package/dist/cjs/types/components/XYPad/Thumb.d.ts.map +1 -0
- package/dist/cjs/types/components/XYPad/index.d.ts +39 -0
- package/dist/cjs/types/components/XYPad/index.d.ts.map +1 -0
- package/dist/cjs/types/hooks/useCallbackRef.d.ts.map +1 -1
- package/dist/cjs/types/index.d.ts +10 -0
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/esm/index.js +657 -254
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/AnimationCanvas/index.d.ts +16 -9
- package/dist/esm/types/components/AnimationCanvas/index.d.ts.map +1 -1
- package/dist/esm/types/components/AnimationKnob/index.d.ts +53 -0
- package/dist/esm/types/components/AnimationKnob/index.d.ts.map +1 -0
- package/dist/esm/types/components/DragObserver/index.d.ts +19 -0
- package/dist/esm/types/components/DragObserver/index.d.ts.map +1 -0
- package/dist/esm/types/components/Knob/index.d.ts +26 -26
- package/dist/esm/types/components/Knob/index.d.ts.map +1 -1
- package/dist/esm/types/components/NumberInput/index.d.ts +2 -5
- package/dist/esm/types/components/NumberInput/index.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/BlackKey.d.ts +3 -3
- package/dist/esm/types/components/Piano/BlackKey.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/KeyLabel.d.ts +5 -8
- package/dist/esm/types/components/Piano/KeyLabel.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/WhiteKey.d.ts +3 -3
- package/dist/esm/types/components/Piano/WhiteKey.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/index.d.ts +3 -4
- package/dist/esm/types/components/Piano/index.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/key.d.ts +4 -7
- package/dist/esm/types/components/Piano/key.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/keyboardShortcuts.d.ts.map +1 -1
- package/dist/esm/types/components/Slider/Scale.d.ts +11 -0
- package/dist/esm/types/components/Slider/Scale.d.ts.map +1 -0
- package/dist/esm/types/components/Slider/ScaleOption.d.ts +25 -0
- package/dist/esm/types/components/Slider/ScaleOption.d.ts.map +1 -0
- package/dist/esm/types/components/Slider/Thumb.d.ts +5 -13
- package/dist/esm/types/components/Slider/Thumb.d.ts.map +1 -1
- package/dist/esm/types/components/Slider/Track.d.ts +10 -21
- package/dist/esm/types/components/Slider/Track.d.ts.map +1 -1
- package/dist/esm/types/components/Slider/context.d.ts +21 -0
- package/dist/esm/types/components/Slider/context.d.ts.map +1 -0
- package/dist/esm/types/components/Slider/index.d.ts +18 -12
- package/dist/esm/types/components/Slider/index.d.ts.map +1 -1
- package/dist/esm/types/components/WheelObserver/index.d.ts +24 -0
- package/dist/esm/types/components/WheelObserver/index.d.ts.map +1 -0
- package/dist/esm/types/components/XYPad/Area.d.ts +13 -0
- package/dist/esm/types/components/XYPad/Area.d.ts.map +1 -0
- package/dist/esm/types/components/XYPad/Thumb.d.ts +19 -0
- package/dist/esm/types/components/XYPad/Thumb.d.ts.map +1 -0
- package/dist/esm/types/components/XYPad/index.d.ts +39 -0
- package/dist/esm/types/components/XYPad/index.d.ts.map +1 -0
- package/dist/esm/types/hooks/useCallbackRef.d.ts.map +1 -1
- package/dist/esm/types/index.d.ts +10 -0
- package/dist/esm/types/index.d.ts.map +1 -1
- package/dist/styles/index.css +241 -0
- package/package.json +2 -5
- package/src/components/AnimationCanvas/index.tsx +87 -57
- package/src/components/AnimationKnob/index.tsx +305 -0
- package/src/components/DragObserver/index.tsx +94 -0
- package/src/components/Knob/index.css +48 -0
- package/src/components/Knob/index.tsx +152 -165
- package/src/components/NumberInput/index.css +25 -0
- package/src/components/NumberInput/index.tsx +8 -41
- package/src/components/Piano/BlackKey.tsx +104 -98
- package/src/components/Piano/KeyLabel.tsx +18 -40
- package/src/components/Piano/WhiteKey.tsx +92 -87
- package/src/components/Piano/index.css +63 -0
- package/src/components/Piano/index.tsx +76 -59
- package/src/components/Piano/key.ts +4 -7
- package/src/components/Piano/keyboardShortcuts.ts +23 -4
- package/src/components/Slider/Scale.tsx +55 -0
- package/src/components/Slider/ScaleOption.tsx +103 -0
- package/src/components/Slider/Thumb.tsx +69 -76
- package/src/components/Slider/Track.tsx +37 -48
- package/src/components/Slider/context.tsx +66 -0
- package/src/components/Slider/index.css +76 -0
- package/src/components/Slider/index.tsx +256 -298
- package/src/components/WheelObserver/index.tsx +47 -0
- package/src/components/XYPad/Area.tsx +40 -0
- package/src/components/XYPad/Thumb.tsx +92 -0
- package/src/components/XYPad/index.css +24 -0
- package/src/components/XYPad/index.tsx +317 -0
- package/src/hooks/useCallbackRef.ts +1 -0
- package/src/hooks/useRefCallbackEvent.ts +1 -0
- package/src/index.ts +11 -0
- package/dist/cjs/types/components/Slider/type.d.ts +0 -7
- package/dist/cjs/types/components/Slider/type.d.ts.map +0 -1
- package/dist/cjs/types/system/pseudo.d.ts +0 -14
- package/dist/cjs/types/system/pseudo.d.ts.map +0 -1
- package/dist/esm/types/components/Slider/type.d.ts +0 -7
- package/dist/esm/types/components/Slider/type.d.ts.map +0 -1
- package/dist/esm/types/system/pseudo.d.ts +0 -14
- package/dist/esm/types/system/pseudo.d.ts.map +0 -1
- package/src/components/Slider/type.ts +0 -16
- package/src/system/pseudo.ts +0 -55
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { css, CSSObject, Global } from '@emotion/react'
|
|
2
|
-
import { clamp, normalizeValue, radian, rawValue, stepValue, InputEventOption } from '@tremolo-ui/functions'
|
|
3
|
-
import React, { ComponentPropsWithRef, useRef } from 'react'
|
|
4
1
|
import clsx from 'clsx'
|
|
2
|
+
import { ComponentPropsWithoutRef, useCallback, useRef } from 'react'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
clamp,
|
|
6
|
+
InputEventOption,
|
|
7
|
+
normalizeValue,
|
|
8
|
+
radian,
|
|
9
|
+
rawValue,
|
|
10
|
+
stepValue,
|
|
11
|
+
} from '@tremolo-ui/functions'
|
|
5
12
|
|
|
6
13
|
import { useEventListener } from '../../hooks/useEventListener'
|
|
7
14
|
import { useRefCallbackEvent } from '../../hooks/useRefCallbackEvent'
|
|
8
|
-
import { UserActionPseudoProps } from '../../system/pseudo'
|
|
9
|
-
import { AnimationCanvas, DrawFunction } from '../AnimationCanvas'
|
|
10
15
|
import { addNoSelect, removeNoSelect } from '../_util'
|
|
11
16
|
|
|
12
17
|
export interface KnobProps {
|
|
@@ -16,8 +21,11 @@ export interface KnobProps {
|
|
|
16
21
|
max: number
|
|
17
22
|
|
|
18
23
|
// optional
|
|
24
|
+
step?: number
|
|
25
|
+
skew?: number // | SkewFunction
|
|
19
26
|
/**
|
|
20
27
|
* value set when double-clicking
|
|
28
|
+
* @default min
|
|
21
29
|
* @see enableDoubleClickDefault
|
|
22
30
|
*/
|
|
23
31
|
defaultValue?: number
|
|
@@ -28,111 +36,118 @@ export interface KnobProps {
|
|
|
28
36
|
*/
|
|
29
37
|
startValue?: number
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
// TODO: relative size (Lower priority)
|
|
40
|
+
size?: number
|
|
33
41
|
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
/** Whether to apply `{use-select: none}` when dragging */
|
|
43
|
+
bodyNoSelect?: boolean
|
|
44
|
+
/**
|
|
45
|
+
* wheel control option
|
|
46
|
+
*/
|
|
47
|
+
wheel?: InputEventOption | null
|
|
48
|
+
/**
|
|
49
|
+
* keyboard control option
|
|
50
|
+
*/
|
|
51
|
+
keyboard?: InputEventOption | null
|
|
52
|
+
enableDoubleClickDefault?: boolean
|
|
39
53
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
active?: string // active line color
|
|
43
|
-
inactive?: string // inactive line color
|
|
44
|
-
bg?: string // background color
|
|
45
|
-
thumb?: string // thumb color
|
|
46
|
-
lineWeight?: number // line color
|
|
47
|
-
thumbWeight?: number // thumb weight
|
|
48
|
-
}
|
|
54
|
+
disabled?: boolean
|
|
55
|
+
readonly?: boolean
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
activeLine?: string
|
|
58
|
+
inactiveLine?: string
|
|
59
|
+
thumb?: string
|
|
60
|
+
thumbLine?: string
|
|
51
61
|
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
classes?: {
|
|
63
|
+
activeLine?: string
|
|
64
|
+
inactiveLine?: string
|
|
65
|
+
thumb?: string
|
|
66
|
+
thumbLine?: string
|
|
67
|
+
}
|
|
54
68
|
|
|
55
|
-
wheel?: InputEventOption
|
|
56
|
-
enableDoubleClickDefault?: boolean
|
|
57
|
-
style?: CSSObject
|
|
58
69
|
onChange?: (value: number) => void
|
|
59
70
|
}
|
|
60
71
|
|
|
61
|
-
const defaultOptions = {
|
|
62
|
-
active: '#7998ec',
|
|
63
|
-
inactive: '#eee',
|
|
64
|
-
bg: '#ccc',
|
|
65
|
-
thumb: '#4e76e6',
|
|
66
|
-
lineWeight: 6,
|
|
67
|
-
thumbWeight: 4,
|
|
68
|
-
} as const
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* simple rotary Knob.
|
|
72
|
-
*/
|
|
73
72
|
export function Knob({
|
|
74
73
|
value,
|
|
75
74
|
min,
|
|
76
75
|
max,
|
|
77
|
-
defaultValue = min,
|
|
78
|
-
startValue = min,
|
|
79
76
|
step = 1,
|
|
80
77
|
skew = 1,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
options = defaultOptions,
|
|
85
|
-
draw,
|
|
78
|
+
defaultValue = min,
|
|
79
|
+
startValue = min,
|
|
80
|
+
size = 50,
|
|
86
81
|
bodyNoSelect = true,
|
|
87
|
-
wheel,
|
|
82
|
+
wheel = ['raw', 1],
|
|
83
|
+
keyboard = ['raw', 1],
|
|
88
84
|
enableDoubleClickDefault = true,
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
disabled = false,
|
|
86
|
+
readonly = false,
|
|
87
|
+
activeLine,
|
|
88
|
+
inactiveLine,
|
|
89
|
+
thumb,
|
|
90
|
+
thumbLine,
|
|
91
91
|
onChange,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
_hover,
|
|
92
|
+
className,
|
|
93
|
+
classes,
|
|
95
94
|
...props
|
|
96
|
-
}: KnobProps &
|
|
97
|
-
UserActionPseudoProps &
|
|
98
|
-
Omit<ComponentPropsWithRef<'div'>, keyof KnobProps>) {
|
|
95
|
+
}: KnobProps & Omit<ComponentPropsWithoutRef<'svg'>, keyof KnobProps>) {
|
|
99
96
|
// -- state and ref ---
|
|
100
|
-
// const [privateValue, setPrivateValue] = useState(value);
|
|
101
97
|
const dragOffsetY = useRef<number | undefined>(undefined)
|
|
102
98
|
|
|
103
99
|
// --- interpret props ---
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
return Number(size.slice(0, -1))
|
|
111
|
-
} else {
|
|
112
|
-
return size
|
|
113
|
-
}
|
|
114
|
-
}
|
|
100
|
+
const padding = 8 // %
|
|
101
|
+
const thumbLineWeight = 6 // %
|
|
102
|
+
const thumbLength = 35 // %
|
|
103
|
+
const lineWeight = 3 // px
|
|
104
|
+
const p = normalizeValue(value, min, max, skew)
|
|
105
|
+
const s = normalizeValue(startValue, min, max, skew)
|
|
115
106
|
|
|
107
|
+
// --- internal functions ---
|
|
116
108
|
const handleEvent = (
|
|
117
109
|
event:
|
|
118
110
|
| MouseEvent
|
|
119
|
-
| React.MouseEvent<
|
|
111
|
+
| React.MouseEvent<HTMLOrSVGElement, MouseEvent>
|
|
120
112
|
| TouchEvent,
|
|
121
113
|
) => {
|
|
114
|
+
if (!onChange || readonly) return
|
|
122
115
|
const isTouch = event instanceof TouchEvent
|
|
123
116
|
if (isTouch && event.cancelable) event.preventDefault()
|
|
124
117
|
if (dragOffsetY.current) {
|
|
125
118
|
if (bodyNoSelect) addNoSelect()
|
|
126
119
|
const screenY = isTouch ? event.touches[0].screenY : event.screenY
|
|
127
120
|
const delta = dragOffsetY.current - screenY
|
|
121
|
+
if (Math.abs(delta) < 1) return
|
|
128
122
|
dragOffsetY.current = screenY
|
|
129
123
|
const n = normalizeValue(value, min, max, skew)
|
|
130
124
|
const v = rawValue(n + delta / 100, min, max, skew)
|
|
131
125
|
const v2 = clamp(stepValue(v, step), min, max)
|
|
132
|
-
|
|
126
|
+
onChange(v2)
|
|
133
127
|
}
|
|
134
128
|
}
|
|
135
129
|
|
|
130
|
+
const handleKeyDown = useCallback(
|
|
131
|
+
(event: React.KeyboardEvent<HTMLOrSVGElement>) => {
|
|
132
|
+
if (!keyboard || !onChange || readonly) return
|
|
133
|
+
const key = event.key
|
|
134
|
+
if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
|
|
135
|
+
event.preventDefault()
|
|
136
|
+
const x =
|
|
137
|
+
key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1]
|
|
138
|
+
let v
|
|
139
|
+
if (keyboard[0] == 'normalized') {
|
|
140
|
+
const n = normalizeValue(value, min, max, skew)
|
|
141
|
+
v = rawValue(n + x, min, max, skew)
|
|
142
|
+
} else {
|
|
143
|
+
v = value + x
|
|
144
|
+
}
|
|
145
|
+
onChange(clamp(stepValue(v, step), min, max))
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
[value, min, max, step, skew, keyboard, readonly, onChange],
|
|
149
|
+
)
|
|
150
|
+
|
|
136
151
|
// --- hooks ---
|
|
137
152
|
const wheelRefCallback = useRefCallbackEvent(
|
|
138
153
|
'wheel',
|
|
@@ -167,34 +182,35 @@ export function Knob({
|
|
|
167
182
|
if (bodyNoSelect) removeNoSelect()
|
|
168
183
|
})
|
|
169
184
|
|
|
185
|
+
const center = size / 2
|
|
186
|
+
const r1 = -135
|
|
187
|
+
const r2 = -135 + Math.min(p, s) * 270
|
|
188
|
+
const r3 = -135 + Math.max(p, s) * 270
|
|
189
|
+
const r4 = 135
|
|
190
|
+
const x1 = center + center * Math.cos(radian(r1 - 90))
|
|
191
|
+
const y1 = center + center * Math.sin(radian(r1 - 90))
|
|
192
|
+
const x2 = center + center * Math.cos(radian(r2 - 90))
|
|
193
|
+
const y2 = center + center * Math.sin(radian(r2 - 90))
|
|
194
|
+
const x3 = center + center * Math.cos(radian(r3 - 90))
|
|
195
|
+
const y3 = center + center * Math.sin(radian(r3 - 90))
|
|
196
|
+
const x4 = center + center * Math.cos(radian(r4 - 90))
|
|
197
|
+
const y4 = center + center * Math.sin(radian(r4 - 90))
|
|
198
|
+
|
|
170
199
|
return (
|
|
171
|
-
<
|
|
200
|
+
<svg
|
|
172
201
|
className={clsx('tremolo-knob', className)}
|
|
173
202
|
ref={(div) => {
|
|
174
203
|
wheelRefCallback(div)
|
|
175
204
|
touchMoveRefCallback(div)
|
|
176
205
|
}}
|
|
177
|
-
|
|
206
|
+
width={size}
|
|
207
|
+
height={size}
|
|
178
208
|
tabIndex={0}
|
|
179
209
|
aria-valuenow={value}
|
|
180
|
-
aria-valuemax={max}
|
|
181
210
|
aria-valuemin={min}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
height: size ?? height,
|
|
186
|
-
cursor: 'pointer',
|
|
187
|
-
...style,
|
|
188
|
-
':active': {
|
|
189
|
-
..._active,
|
|
190
|
-
},
|
|
191
|
-
':focus': {
|
|
192
|
-
..._focus,
|
|
193
|
-
},
|
|
194
|
-
':hover': {
|
|
195
|
-
..._hover,
|
|
196
|
-
},
|
|
197
|
-
})}
|
|
211
|
+
aria-valuemax={max}
|
|
212
|
+
aria-disabled={disabled}
|
|
213
|
+
aria-readonly={readonly}
|
|
198
214
|
onPointerDown={(event) => {
|
|
199
215
|
dragOffsetY.current = event.screenY
|
|
200
216
|
handleEvent(event)
|
|
@@ -204,85 +220,56 @@ export function Knob({
|
|
|
204
220
|
onChange(defaultValue)
|
|
205
221
|
}
|
|
206
222
|
}}
|
|
223
|
+
onKeyDown={handleKeyDown}
|
|
224
|
+
onContextMenu={(e) => e.preventDefault()}
|
|
207
225
|
{...props}
|
|
208
226
|
>
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const w = width.current,
|
|
226
|
-
cx = w / 2
|
|
227
|
-
const h = height.current,
|
|
228
|
-
cy = h / 2
|
|
229
|
-
const r = Math.min(cx, cy)
|
|
230
|
-
if (r <= 0) return
|
|
231
|
-
|
|
232
|
-
// reset
|
|
233
|
-
ctx.clearRect(0, 0, w, h)
|
|
234
|
-
ctx.lineCap = 'butt'
|
|
235
|
-
|
|
236
|
-
// bg
|
|
237
|
-
ctx.beginPath()
|
|
238
|
-
ctx.fillStyle = opts.bg
|
|
239
|
-
ctx.arc(cx, cy, r - 0.5, 0, 2 * Math.PI)
|
|
240
|
-
ctx.fill()
|
|
241
|
-
|
|
242
|
-
ctx.lineCap = 'round'
|
|
243
|
-
ctx.lineWidth = opts.lineWeight
|
|
244
|
-
|
|
245
|
-
// inactive rotary
|
|
246
|
-
ctx.beginPath()
|
|
247
|
-
ctx.strokeStyle = opts.inactive
|
|
248
|
-
ctx.arc(
|
|
249
|
-
cx,
|
|
250
|
-
cy,
|
|
251
|
-
r - 0.5 - opts.lineWeight / 2,
|
|
252
|
-
radian(135),
|
|
253
|
-
radian(45),
|
|
254
|
-
)
|
|
255
|
-
ctx.stroke()
|
|
256
|
-
|
|
257
|
-
// active rotary
|
|
258
|
-
ctx.beginPath()
|
|
259
|
-
ctx.strokeStyle = opts.active
|
|
260
|
-
ctx.arc(
|
|
261
|
-
cx,
|
|
262
|
-
cy,
|
|
263
|
-
r - 0.5 - opts.lineWeight / 2,
|
|
264
|
-
radian(135 + 270 * startP),
|
|
265
|
-
radian(135 + 270 * p),
|
|
266
|
-
p < startP,
|
|
267
|
-
)
|
|
268
|
-
ctx.stroke()
|
|
269
|
-
|
|
270
|
-
// thumb
|
|
271
|
-
ctx.beginPath()
|
|
272
|
-
ctx.moveTo(cx, cy)
|
|
273
|
-
ctx.lineTo(
|
|
274
|
-
cx +
|
|
275
|
-
Math.cos(radian(135 + 270 * p)) *
|
|
276
|
-
(r - 0.5 - opts.thumbWeight / 2),
|
|
277
|
-
cy +
|
|
278
|
-
Math.sin(radian(135 + 270 * p)) *
|
|
279
|
-
(r - 0.5 - opts.thumbWeight / 2),
|
|
280
|
-
)
|
|
281
|
-
ctx.strokeStyle = opts.thumb
|
|
282
|
-
ctx.lineWidth = opts.thumbWeight
|
|
283
|
-
ctx.stroke()
|
|
284
|
-
}}
|
|
227
|
+
{/* rotary meter */}
|
|
228
|
+
{startValue > min && (
|
|
229
|
+
<path
|
|
230
|
+
className={clsx('tremolo-knob-inactive-line', classes?.inactiveLine)}
|
|
231
|
+
d={`M ${x1} ${y1} A ${center} ${center} -135 ${r2 - r1 > 180 ? 1 : 0} 1 ${x2}, ${y2}`}
|
|
232
|
+
fill="none"
|
|
233
|
+
stroke={inactiveLine || 'currentColor'}
|
|
234
|
+
strokeWidth={lineWeight}
|
|
235
|
+
/>
|
|
236
|
+
)}
|
|
237
|
+
<path
|
|
238
|
+
className={clsx('tremolo-knob-active-line', classes?.activeLine)}
|
|
239
|
+
d={`M ${x2} ${y2} A ${center} ${center} -135 ${r3 - r2 > 180 ? 1 : 0} 1 ${x3}, ${y3}`}
|
|
240
|
+
fill="none"
|
|
241
|
+
stroke={activeLine || 'currentColor'}
|
|
242
|
+
strokeWidth={lineWeight}
|
|
285
243
|
/>
|
|
286
|
-
|
|
244
|
+
{startValue < max && (
|
|
245
|
+
<path
|
|
246
|
+
className={clsx('tremolo-knob-inactive-line', classes?.inactiveLine)}
|
|
247
|
+
d={`M ${x3} ${y3} A ${center} ${center} -135 ${r4 - r3 > 180 ? 1 : 0} 1 ${x4}, ${y4}`}
|
|
248
|
+
fill="none"
|
|
249
|
+
stroke={inactiveLine || 'currentColor'}
|
|
250
|
+
strokeWidth={lineWeight}
|
|
251
|
+
/>
|
|
252
|
+
)}
|
|
253
|
+
{/* thumb */}
|
|
254
|
+
<svg className={clsx('tremolo-knob-thumb', classes?.thumb)}>
|
|
255
|
+
<circle
|
|
256
|
+
cx="50%"
|
|
257
|
+
cy="50%"
|
|
258
|
+
r={`${50 - padding}%`}
|
|
259
|
+
fill={thumb || 'currentColor'}
|
|
260
|
+
/>
|
|
261
|
+
<line
|
|
262
|
+
className={clsx('tremolo-knob-thumb-line', classes?.thumbLine)}
|
|
263
|
+
x1="50%"
|
|
264
|
+
y1={`${padding}%`}
|
|
265
|
+
x2="50%"
|
|
266
|
+
y2={`${thumbLength}%`}
|
|
267
|
+
stroke={thumbLine || 'currentColor'}
|
|
268
|
+
strokeWidth={`${thumbLineWeight}%`}
|
|
269
|
+
transform={`rotate(${-135 + p * 270})`}
|
|
270
|
+
transform-origin="50% 50%"
|
|
271
|
+
/>
|
|
272
|
+
</svg>
|
|
273
|
+
</svg>
|
|
287
274
|
)
|
|
288
275
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.tremolo-number-input {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
font: inherit;
|
|
4
|
+
margin: 0px;
|
|
5
|
+
padding-top: 6px;
|
|
6
|
+
padding-bottom: 6px;
|
|
7
|
+
padding-left: 10px;
|
|
8
|
+
padding-right: 10px;
|
|
9
|
+
border-style: solid;
|
|
10
|
+
border-width: 1px;
|
|
11
|
+
border-color: #bbb;
|
|
12
|
+
border-radius: 4px;
|
|
13
|
+
outline: 0px;
|
|
14
|
+
transition: all 0.1s linear;
|
|
15
|
+
appearance: textfield;
|
|
16
|
+
|
|
17
|
+
&:hover {
|
|
18
|
+
border-color: var(--tremolo-theme-color);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&:focus {
|
|
22
|
+
border-color: var(--tremolo-theme-color);
|
|
23
|
+
box-shadow: 0px 0px 0px 2px rgba(var(--tremolo-theme-color-rgb), 0.1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { css, CSSObject } from '@emotion/react'
|
|
2
|
-
import { clamp } from '@tremolo-ui/functions'
|
|
3
|
-
import { parseValue, Units } from '@tremolo-ui/functions/NumberInput'
|
|
4
|
-
import { ChangeEvent, ComponentPropsWithoutRef, useState } from 'react'
|
|
5
1
|
import clsx from 'clsx'
|
|
2
|
+
import { ChangeEvent, ComponentPropsWithoutRef, useState } from 'react'
|
|
6
3
|
|
|
7
|
-
import {
|
|
4
|
+
import { clamp } from '@tremolo-ui/functions'
|
|
5
|
+
import { parseValue, Units } from '@tremolo-ui/functions/NumberInput'
|
|
8
6
|
|
|
9
7
|
export interface NumberInputProps {
|
|
10
8
|
value: number | string
|
|
@@ -31,7 +29,7 @@ export interface NumberInputProps {
|
|
|
31
29
|
/**
|
|
32
30
|
* NOTE: If units is specified, it is forced to `false`
|
|
33
31
|
*/
|
|
34
|
-
typeNumber?: boolean
|
|
32
|
+
typeNumber?: boolean
|
|
35
33
|
|
|
36
34
|
/**
|
|
37
35
|
* Digits for rounding numbers.
|
|
@@ -46,7 +44,6 @@ export interface NumberInputProps {
|
|
|
46
44
|
selectWithFocus?: 'all' | 'number' | 'none'
|
|
47
45
|
blurWithEnter?: boolean
|
|
48
46
|
className?: string
|
|
49
|
-
style?: CSSObject
|
|
50
47
|
onChange?: (value: number, event: ChangeEvent<HTMLInputElement>) => void
|
|
51
48
|
onFocus?: (
|
|
52
49
|
value: number,
|
|
@@ -77,19 +74,16 @@ export function NumberInput({
|
|
|
77
74
|
selectWithFocus = 'none',
|
|
78
75
|
blurWithEnter = true,
|
|
79
76
|
className,
|
|
80
|
-
style,
|
|
81
77
|
onChange,
|
|
82
78
|
onFocus,
|
|
83
79
|
onBlur,
|
|
84
80
|
...props
|
|
85
|
-
}: NumberInputProps &
|
|
86
|
-
Omit<ComponentPropsWithoutRef<'input'>, keyof NumberInputProps>
|
|
87
|
-
) {
|
|
81
|
+
}: NumberInputProps &
|
|
82
|
+
Omit<ComponentPropsWithoutRef<'input'>, keyof NumberInputProps | 'type'>) {
|
|
88
83
|
const [showValue, setShowValue] = useState(
|
|
89
84
|
parseValue(String(value), units, digit, defaultValue).formatValue,
|
|
90
85
|
)
|
|
91
86
|
const calculatedTypeNumber = units ? false : typeNumber
|
|
92
|
-
const { _active, _focus, _hover, ...rest } = props
|
|
93
87
|
|
|
94
88
|
return (
|
|
95
89
|
<input
|
|
@@ -104,40 +98,13 @@ export function NumberInput({
|
|
|
104
98
|
placeholder={placeholder}
|
|
105
99
|
readOnly={readOnly}
|
|
106
100
|
tabIndex={tabIndex}
|
|
107
|
-
css={css({
|
|
108
|
-
display: 'inline-block',
|
|
109
|
-
font: 'inherit',
|
|
110
|
-
margin: 0,
|
|
111
|
-
paddingTop: 6,
|
|
112
|
-
paddingBottom: 6,
|
|
113
|
-
paddingLeft: 10,
|
|
114
|
-
paddingRight: 10,
|
|
115
|
-
borderStyle: 'solid',
|
|
116
|
-
borderWidth: 1,
|
|
117
|
-
borderColor: '#bbb',
|
|
118
|
-
borderRadius: 4,
|
|
119
|
-
outline: 0,
|
|
120
|
-
transition: 'all 0.1s linear',
|
|
121
|
-
appearance: 'textfield',
|
|
122
|
-
'&:hover': {
|
|
123
|
-
borderColor: 'var(--tremolo-theme-color)',
|
|
124
|
-
..._hover,
|
|
125
|
-
},
|
|
126
|
-
'&:focus': {
|
|
127
|
-
borderColor: 'var(--tremolo-theme-color)',
|
|
128
|
-
boxShadow: '0px 0px 0px 2px rgba(var(--tremolo-theme-color-rgb), 0.1)',
|
|
129
|
-
..._focus,
|
|
130
|
-
},
|
|
131
|
-
...(_active && { '&:active': { ..._active } }),
|
|
132
|
-
...style,
|
|
133
|
-
})}
|
|
134
101
|
onChange={(event) => {
|
|
135
102
|
const v = event.currentTarget.value
|
|
136
103
|
setShowValue(v)
|
|
137
104
|
const newValue = clamp(
|
|
138
105
|
parseValue(v, units, digit, defaultValue).rawValue,
|
|
139
106
|
min ?? -Infinity,
|
|
140
|
-
max ?? Infinity
|
|
107
|
+
max ?? Infinity,
|
|
141
108
|
)
|
|
142
109
|
if (onChange) onChange(newValue, event)
|
|
143
110
|
}}
|
|
@@ -166,7 +133,7 @@ export function NumberInput({
|
|
|
166
133
|
event.currentTarget.blur()
|
|
167
134
|
}
|
|
168
135
|
}}
|
|
169
|
-
{...
|
|
136
|
+
{...props}
|
|
170
137
|
/>
|
|
171
138
|
)
|
|
172
139
|
}
|