@tremolo-ui/react 0.0.11 → 0.0.12

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 (130) hide show
  1. package/dist/cjs/index.js +428 -196
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/AnimationCanvas/index.d.ts +2 -2
  4. package/dist/cjs/types/components/AnimationCanvas/index.d.ts.map +1 -1
  5. package/dist/cjs/types/components/AnimationKnob/index.d.ts +10 -3
  6. package/dist/cjs/types/components/AnimationKnob/index.d.ts.map +1 -1
  7. package/dist/cjs/types/components/Knob/index.d.ts +13 -3
  8. package/dist/cjs/types/components/Knob/index.d.ts.map +1 -1
  9. package/dist/cjs/types/components/NumberInput/DecrementStepper.d.ts +2 -0
  10. package/dist/cjs/types/components/NumberInput/DecrementStepper.d.ts.map +1 -1
  11. package/dist/cjs/types/components/NumberInput/IncrementStepper.d.ts +2 -0
  12. package/dist/cjs/types/components/NumberInput/IncrementStepper.d.ts.map +1 -1
  13. package/dist/cjs/types/components/NumberInput/InternalInput.d.ts +18 -0
  14. package/dist/cjs/types/components/NumberInput/InternalInput.d.ts.map +1 -0
  15. package/dist/cjs/types/components/NumberInput/Stepper.d.ts +2 -0
  16. package/dist/cjs/types/components/NumberInput/Stepper.d.ts.map +1 -1
  17. package/dist/cjs/types/components/NumberInput/context.d.ts +3 -2
  18. package/dist/cjs/types/components/NumberInput/context.d.ts.map +1 -1
  19. package/dist/cjs/types/components/NumberInput/index.d.ts +12 -3
  20. package/dist/cjs/types/components/NumberInput/index.d.ts.map +1 -1
  21. package/dist/cjs/types/components/Piano/BlackKey.d.ts.map +1 -1
  22. package/dist/cjs/types/components/Piano/WhiteKey.d.ts.map +1 -1
  23. package/dist/cjs/types/components/Piano/index.d.ts +7 -4
  24. package/dist/cjs/types/components/Piano/index.d.ts.map +1 -1
  25. package/dist/cjs/types/components/Piano/key.d.ts +4 -0
  26. package/dist/cjs/types/components/Piano/key.d.ts.map +1 -1
  27. package/dist/cjs/types/components/Piano/keyboardShortcuts.d.ts +2 -2
  28. package/dist/cjs/types/components/Slider/Scale.d.ts +1 -3
  29. package/dist/cjs/types/components/Slider/Scale.d.ts.map +1 -1
  30. package/dist/cjs/types/components/Slider/Thumb.d.ts +1 -0
  31. package/dist/cjs/types/components/Slider/Thumb.d.ts.map +1 -1
  32. package/dist/cjs/types/components/Slider/Track.d.ts +2 -2
  33. package/dist/cjs/types/components/Slider/Track.d.ts.map +1 -1
  34. package/dist/cjs/types/components/Slider/index.d.ts +5 -1
  35. package/dist/cjs/types/components/Slider/index.d.ts.map +1 -1
  36. package/dist/cjs/types/components/WheelObserver/index.d.ts +3 -3
  37. package/dist/cjs/types/components/WheelObserver/index.d.ts.map +1 -1
  38. package/dist/cjs/types/components/XYPad/Area.d.ts +3 -3
  39. package/dist/cjs/types/components/XYPad/Area.d.ts.map +1 -1
  40. package/dist/cjs/types/components/XYPad/Thumb.d.ts +1 -0
  41. package/dist/cjs/types/components/XYPad/Thumb.d.ts.map +1 -1
  42. package/dist/cjs/types/components/XYPad/index.d.ts +6 -1
  43. package/dist/cjs/types/components/XYPad/index.d.ts.map +1 -1
  44. package/dist/cjs/types/hooks/useInterval.d.ts +2 -0
  45. package/dist/cjs/types/hooks/useInterval.d.ts.map +1 -0
  46. package/dist/cjs/types/hooks/useLongPress.d.ts +2 -0
  47. package/dist/cjs/types/hooks/useLongPress.d.ts.map +1 -0
  48. package/dist/cjs/types/index.d.ts +5 -0
  49. package/dist/cjs/types/index.d.ts.map +1 -1
  50. package/dist/esm/index.js +424 -197
  51. package/dist/esm/index.js.map +1 -1
  52. package/dist/esm/types/components/AnimationCanvas/index.d.ts +2 -2
  53. package/dist/esm/types/components/AnimationCanvas/index.d.ts.map +1 -1
  54. package/dist/esm/types/components/AnimationKnob/index.d.ts +10 -3
  55. package/dist/esm/types/components/AnimationKnob/index.d.ts.map +1 -1
  56. package/dist/esm/types/components/Knob/index.d.ts +13 -3
  57. package/dist/esm/types/components/Knob/index.d.ts.map +1 -1
  58. package/dist/esm/types/components/NumberInput/DecrementStepper.d.ts +2 -0
  59. package/dist/esm/types/components/NumberInput/DecrementStepper.d.ts.map +1 -1
  60. package/dist/esm/types/components/NumberInput/IncrementStepper.d.ts +2 -0
  61. package/dist/esm/types/components/NumberInput/IncrementStepper.d.ts.map +1 -1
  62. package/dist/esm/types/components/NumberInput/InternalInput.d.ts +18 -0
  63. package/dist/esm/types/components/NumberInput/InternalInput.d.ts.map +1 -0
  64. package/dist/esm/types/components/NumberInput/Stepper.d.ts +2 -0
  65. package/dist/esm/types/components/NumberInput/Stepper.d.ts.map +1 -1
  66. package/dist/esm/types/components/NumberInput/context.d.ts +3 -2
  67. package/dist/esm/types/components/NumberInput/context.d.ts.map +1 -1
  68. package/dist/esm/types/components/NumberInput/index.d.ts +12 -3
  69. package/dist/esm/types/components/NumberInput/index.d.ts.map +1 -1
  70. package/dist/esm/types/components/Piano/BlackKey.d.ts.map +1 -1
  71. package/dist/esm/types/components/Piano/WhiteKey.d.ts.map +1 -1
  72. package/dist/esm/types/components/Piano/index.d.ts +7 -4
  73. package/dist/esm/types/components/Piano/index.d.ts.map +1 -1
  74. package/dist/esm/types/components/Piano/key.d.ts +4 -0
  75. package/dist/esm/types/components/Piano/key.d.ts.map +1 -1
  76. package/dist/esm/types/components/Piano/keyboardShortcuts.d.ts +2 -2
  77. package/dist/esm/types/components/Slider/Scale.d.ts +1 -3
  78. package/dist/esm/types/components/Slider/Scale.d.ts.map +1 -1
  79. package/dist/esm/types/components/Slider/Thumb.d.ts +1 -0
  80. package/dist/esm/types/components/Slider/Thumb.d.ts.map +1 -1
  81. package/dist/esm/types/components/Slider/Track.d.ts +2 -2
  82. package/dist/esm/types/components/Slider/Track.d.ts.map +1 -1
  83. package/dist/esm/types/components/Slider/index.d.ts +5 -1
  84. package/dist/esm/types/components/Slider/index.d.ts.map +1 -1
  85. package/dist/esm/types/components/WheelObserver/index.d.ts +3 -3
  86. package/dist/esm/types/components/WheelObserver/index.d.ts.map +1 -1
  87. package/dist/esm/types/components/XYPad/Area.d.ts +3 -3
  88. package/dist/esm/types/components/XYPad/Area.d.ts.map +1 -1
  89. package/dist/esm/types/components/XYPad/Thumb.d.ts +1 -0
  90. package/dist/esm/types/components/XYPad/Thumb.d.ts.map +1 -1
  91. package/dist/esm/types/components/XYPad/index.d.ts +6 -1
  92. package/dist/esm/types/components/XYPad/index.d.ts.map +1 -1
  93. package/dist/esm/types/hooks/useInterval.d.ts +2 -0
  94. package/dist/esm/types/hooks/useInterval.d.ts.map +1 -0
  95. package/dist/esm/types/hooks/useLongPress.d.ts +2 -0
  96. package/dist/esm/types/hooks/useLongPress.d.ts.map +1 -0
  97. package/dist/esm/types/index.d.ts +5 -0
  98. package/dist/esm/types/index.d.ts.map +1 -1
  99. package/dist/styles/index.css +115 -33
  100. package/package.json +2 -2
  101. package/src/components/AnimationCanvas/index.tsx +14 -6
  102. package/src/components/AnimationKnob/index.tsx +216 -187
  103. package/src/components/Knob/index.tsx +230 -171
  104. package/src/components/NumberInput/DecrementStepper.tsx +9 -2
  105. package/src/components/NumberInput/IncrementStepper.tsx +9 -2
  106. package/src/components/NumberInput/InternalInput.tsx +219 -0
  107. package/src/components/NumberInput/Stepper.tsx +2 -0
  108. package/src/components/NumberInput/context.tsx +20 -8
  109. package/src/components/NumberInput/index.css +76 -22
  110. package/src/components/NumberInput/index.tsx +94 -162
  111. package/src/components/Piano/BlackKey.tsx +14 -0
  112. package/src/components/Piano/WhiteKey.tsx +14 -0
  113. package/src/components/Piano/index.css +22 -6
  114. package/src/components/Piano/index.tsx +19 -21
  115. package/src/components/Piano/key.ts +5 -0
  116. package/src/components/Piano/keyboardShortcuts.ts +2 -2
  117. package/src/components/Slider/Scale.tsx +1 -4
  118. package/src/components/Slider/Thumb.tsx +4 -0
  119. package/src/components/Slider/Track.tsx +5 -2
  120. package/src/components/Slider/index.css +5 -2
  121. package/src/components/Slider/index.tsx +51 -26
  122. package/src/components/WheelObserver/index.tsx +4 -2
  123. package/src/components/XYPad/Area.tsx +7 -6
  124. package/src/components/XYPad/Thumb.tsx +6 -0
  125. package/src/components/XYPad/index.css +12 -2
  126. package/src/components/XYPad/index.tsx +57 -41
  127. package/src/hooks/useInterval.ts +27 -0
  128. package/src/hooks/useLongPress.ts +31 -0
  129. package/src/index.ts +6 -0
  130. package/src/styles/index.css +0 -1
@@ -1,19 +1,16 @@
1
1
  import clsx from 'clsx'
2
- import { ComponentPropsWithoutRef, ReactNode, useMemo, useState } from 'react'
2
+ import { ComponentPropsWithoutRef, forwardRef, ReactNode } from 'react'
3
3
 
4
4
  import { InputEventOption } from '@tremolo-ui/functions'
5
- import { parseValue, Units } from '@tremolo-ui/functions/NumberInput'
5
+ import { Units } from '@tremolo-ui/functions/NumberInput'
6
6
 
7
- import {
8
- NumberInputProvider,
9
- safeClamp,
10
- useNumberInputContext,
11
- } from './context'
7
+ import { NumberInputProvider } from './context'
8
+ import { InternalInput } from './InternalInput'
12
9
 
13
10
  /*
14
11
  TODO
15
- - onBlurでは、parse + (clamp) + formatして返す
16
12
  - numberMode: focusが当たった時だけ、parse + type="number"にする
13
+ - skew
17
14
  */
18
15
 
19
16
  /** @category NumberInput */
@@ -29,7 +26,6 @@ export interface NumberInputProps {
29
26
  */
30
27
  max?: number
31
28
  step?: number
32
- defaultValue?: number | string
33
29
 
34
30
  /**
35
31
  * @example
@@ -50,6 +46,10 @@ export interface NumberInputProps {
50
46
  */
51
47
  digit?: number
52
48
 
49
+ readonly?: boolean
50
+
51
+ variant?: 'outline' | 'filled' | 'flushed' | 'unstyled'
52
+
53
53
  selectWithFocus?: 'all' | 'number' | 'none'
54
54
  blurOnEnter?: boolean
55
55
  keepWithinRange?: boolean
@@ -65,6 +65,8 @@ export interface NumberInputProps {
65
65
  */
66
66
  keyboard?: InputEventOption | null
67
67
 
68
+ activeColor?: string
69
+
68
70
  wrapperClassName?: string
69
71
 
70
72
  onChange?: (value: number, text: string) => void
@@ -82,166 +84,96 @@ export interface NumberInputProps {
82
84
  children?: ReactNode
83
85
  }
84
86
 
85
- function InternalInput({
86
- min,
87
- max,
88
- step,
89
- defaultValue,
90
- units,
91
- disabled,
92
- digit,
93
- selectWithFocus,
94
- blurOnEnter,
95
- keepWithinRange,
96
- clampValueOnBlur,
97
- wheel,
98
- keyboard,
99
- wrapperClassName,
100
- className,
101
- onChange,
102
- onFocus,
103
- onBlur,
104
- children: _children,
105
- ...props
106
- }: Omit<NumberInputProps, 'value'> &
107
- Omit<ComponentPropsWithoutRef<'input'>, keyof NumberInputProps | 'type'>) {
108
- const [editing, setEditing] = useState(false)
109
- const value = useNumberInputContext((s) => s.value)
110
- const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber)
111
- const change = useNumberInputContext((s) => s.change)
112
-
113
- const error = useMemo(() => {
114
- if (editing) return false
115
- const v = valueAsNumber
116
- return (min ?? -Infinity) > v || v > (max ?? Infinity)
117
- }, [editing, max, min, valueAsNumber])
118
-
119
- return (
120
- <input
121
- className={clsx('tremolo-number-input', className)}
122
- value={value}
123
- min={min}
124
- max={max}
125
- step={step}
126
- type="text"
127
- disabled={disabled}
128
- tabIndex={-1}
129
- data-error={error}
130
- onChange={(event) => {
131
- const v = event.currentTarget.value
132
- change(v)
133
- const valueAsNumber = parseValue(v, units, digit).rawValue
134
- onChange?.(valueAsNumber, v)
135
- }}
136
- onFocus={(event) => {
137
- setEditing(true)
138
- const v = event.currentTarget.value
139
- const parsed = parseValue(v, units, digit)
140
- if (selectWithFocus == 'all') {
141
- event.currentTarget.setSelectionRange(0, v.length)
142
- } else if (selectWithFocus == 'number') {
143
- event.currentTarget.setSelectionRange(
144
- 0,
145
- parsed.formatValue.length - parsed.unit.length,
146
- )
147
- }
148
- onFocus?.(parsed.rawValue, parsed.formatValue, event)
149
- }}
150
- onBlur={(event) => {
151
- setEditing(false)
152
- // update value
153
- let v = parseValue(event.currentTarget.value, units, digit).rawValue
154
- if (clampValueOnBlur) {
155
- v = safeClamp(v, min, max)
156
- }
157
- const parsed = parseValue(String(v), units, digit)
158
- change(parsed.formatValue)
159
- if (clampValueOnBlur) {
160
- onChange?.(parsed.rawValue, parsed.formatValue)
161
- }
162
- onBlur?.(parsed.rawValue, parsed.formatValue, event)
163
- }}
164
- onKeyDown={(event) => {
165
- if (blurOnEnter && event.key == 'Enter') {
166
- event.currentTarget.blur()
167
- }
168
- }}
169
- {...props}
170
- />
171
- )
87
+ /**
88
+ * @category NumberInput
89
+ */
90
+ export interface NumberInputMethods {
91
+ focus: () => void
92
+ blur: () => void
172
93
  }
173
94
 
95
+ type Props = NumberInputProps &
96
+ Omit<ComponentPropsWithoutRef<'input'>, keyof NumberInputProps | 'type'>
97
+
174
98
  /**
175
99
  * Input with some useful functions for entering numerical values.
176
100
  * @category NumberInput
177
101
  */
178
- export function NumberInput({
179
- value,
180
- min,
181
- max,
182
- step = 1,
183
- defaultValue,
184
- units,
185
- disabled = false,
186
- digit,
187
- selectWithFocus = 'none',
188
- blurOnEnter = true,
189
- keepWithinRange = true,
190
- clampValueOnBlur = true,
191
- wheel = ['raw', 1],
192
- keyboard = ['raw', 1],
193
- wrapperClassName,
194
- className,
195
- onChange,
196
- onFocus,
197
- onBlur,
198
- children,
199
- ...props
200
- }: NumberInputProps &
201
- Omit<ComponentPropsWithoutRef<'input'>, keyof NumberInputProps | 'type'>) {
202
- return (
203
- <NumberInputProvider
204
- value={String(value)}
205
- min={min}
206
- max={max}
207
- step={step}
208
- units={units}
209
- digit={digit}
210
- keepWithinRange={keepWithinRange}
211
- onChange={onChange}
212
- >
213
- <div
214
- className={clsx('tremolo-number-input-wrapper', wrapperClassName)}
215
- // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
216
- tabIndex={0}
217
- data-stepper={!!children}
102
+ export const NumberInput = forwardRef<NumberInputMethods, Props>(
103
+ (
104
+ {
105
+ value,
106
+ min,
107
+ max,
108
+ step = 1,
109
+ units,
110
+ readonly = false,
111
+ disabled = false,
112
+ digit,
113
+ variant = 'outline',
114
+ selectWithFocus = 'none',
115
+ blurOnEnter = true,
116
+ keepWithinRange = true,
117
+ clampValueOnBlur = true,
118
+ wheel = ['raw', 1],
119
+ keyboard = ['raw', 1],
120
+ activeColor,
121
+ wrapperClassName,
122
+ className,
123
+ onChange,
124
+ onFocus,
125
+ onBlur,
126
+ children,
127
+ ...props
128
+ }: Props,
129
+ ref,
130
+ ) => {
131
+ const colors: { [key: string]: string | undefined } = {
132
+ '--active-color': activeColor,
133
+ }
134
+ return (
135
+ <NumberInputProvider
136
+ value={String(value)}
137
+ min={min}
138
+ max={max}
139
+ step={step}
140
+ units={units}
141
+ digit={digit}
142
+ readonly={readonly}
143
+ keepWithinRange={keepWithinRange}
144
+ onChange={onChange}
218
145
  >
219
- <InternalInput
220
- min={min}
221
- max={max}
222
- step={step}
223
- defaultValue={defaultValue}
224
- units={units}
225
- disabled={disabled}
226
- digit={digit}
227
- selectWithFocus={selectWithFocus}
228
- blurOnEnter={blurOnEnter}
229
- keepWithinRange={keepWithinRange}
230
- clampValueOnBlur={clampValueOnBlur}
231
- wheel={wheel}
232
- keyboard={keyboard}
233
- wrapperClassName={wrapperClassName}
234
- className={className}
235
- onChange={onChange}
236
- onFocus={onFocus}
237
- onBlur={onBlur}
238
- {...props}
239
- />
240
- {children}
241
- </div>
242
- </NumberInputProvider>
243
- )
244
- }
146
+ <div
147
+ className={clsx('tremolo-number-input-wrapper', wrapperClassName)}
148
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
149
+ tabIndex={0}
150
+ style={{
151
+ ...colors,
152
+ }}
153
+ data-stepper={!!children}
154
+ data-variant={variant}
155
+ >
156
+ <InternalInput
157
+ ref={ref}
158
+ readonly={readonly}
159
+ disabled={disabled}
160
+ selectWithFocus={selectWithFocus}
161
+ blurOnEnter={blurOnEnter}
162
+ clampValueOnBlur={clampValueOnBlur}
163
+ wheel={wheel}
164
+ keyboard={keyboard}
165
+ className={className}
166
+ onChange={onChange}
167
+ onFocus={onFocus}
168
+ onBlur={onBlur}
169
+ {...props}
170
+ />
171
+ {children}
172
+ </div>
173
+ </NumberInputProvider>
174
+ )
175
+ },
176
+ )
245
177
 
246
178
  export * from './Stepper'
247
179
  export * from './IncrementStepper'
@@ -18,6 +18,10 @@ export const BlackKey = forwardRef<KeyMethods, Props>(
18
18
  {
19
19
  width = defaultWhiteKeyWidth * 0.65,
20
20
  height = '60%',
21
+ bg,
22
+ color,
23
+ activeBg,
24
+ activeColor,
21
25
  className,
22
26
  style,
23
27
  children,
@@ -31,12 +35,20 @@ export const BlackKey = forwardRef<KeyMethods, Props>(
31
35
  __onPlayNote,
32
36
  __onStopNote,
33
37
  __label,
38
+ onContextMenu = (e) => e.preventDefault(),
34
39
  ...props
35
40
  }: Props,
36
41
  ref,
37
42
  ) => {
38
43
  const [played, setPlayed] = useState(false)
39
44
 
45
+ const colors = {
46
+ '--bg': bg,
47
+ '--color': color,
48
+ '--active-bg': activeBg,
49
+ '--active-color': activeColor,
50
+ }
51
+
40
52
  useImperativeHandle(ref, () => {
41
53
  return {
42
54
  play() {
@@ -75,6 +87,7 @@ export const BlackKey = forwardRef<KeyMethods, Props>(
75
87
  aria-disabled={__disabled}
76
88
  data-active={played}
77
89
  style={{
90
+ ...colors,
78
91
  left: __position,
79
92
  width: __fill ? __width : (width ?? __width),
80
93
  height: height,
@@ -104,6 +117,7 @@ export const BlackKey = forwardRef<KeyMethods, Props>(
104
117
  if (__onStopNote) __onStopNote(__note!)
105
118
  }
106
119
  }}
120
+ onContextMenu={onContextMenu}
107
121
  {...props}
108
122
  >
109
123
  <KeyLabel
@@ -20,6 +20,10 @@ export const WhiteKey = forwardRef<KeyMethods, Props>(
20
20
  {
21
21
  width = defaultWhiteKeyWidth,
22
22
  height = '100%',
23
+ bg,
24
+ color,
25
+ activeBg,
26
+ activeColor,
23
27
  className,
24
28
  style,
25
29
  children,
@@ -33,12 +37,20 @@ export const WhiteKey = forwardRef<KeyMethods, Props>(
33
37
  __onPlayNote,
34
38
  __onStopNote,
35
39
  __label,
40
+ onContextMenu = (e) => e.preventDefault(),
36
41
  ...props
37
42
  }: Props,
38
43
  ref,
39
44
  ) => {
40
45
  const [played, setPlayed] = useState(false)
41
46
 
47
+ const colors = {
48
+ '--bg': bg,
49
+ '--color': color,
50
+ '--active-bg': activeBg,
51
+ '--active-color': activeColor,
52
+ }
53
+
42
54
  useImperativeHandle(ref, () => {
43
55
  return {
44
56
  play() {
@@ -77,6 +89,7 @@ export const WhiteKey = forwardRef<KeyMethods, Props>(
77
89
  aria-disabled={__disabled}
78
90
  data-active={played}
79
91
  style={{
92
+ ...colors,
80
93
  left: __position,
81
94
  width: __fill ? __width : (width ?? __width),
82
95
  height: height,
@@ -106,6 +119,7 @@ export const WhiteKey = forwardRef<KeyMethods, Props>(
106
119
  if (__onStopNote) __onStopNote(__note!)
107
120
  }
108
121
  }}
122
+ onContextMenu={onContextMenu}
109
123
  {...props}
110
124
  >
111
125
  <KeyLabel
@@ -2,20 +2,28 @@
2
2
  display: inline-block;
3
3
  box-sizing: border-box;
4
4
  user-select: none;
5
+ touch-action: none;
5
6
  position: relative;
6
7
  }
7
8
 
8
9
  .tremolo-piano-white-key {
10
+ --bg: white;
11
+ --color: black;
12
+ --active-bg: #ccc;
13
+ --active-color: black;
14
+
9
15
  position: absolute;
10
- background-color: white;
11
- color: black;
16
+ background-color: var(--bg);
17
+ -webkit-tap-highlight-color: transparent;
18
+ color: var(--color);
12
19
  border: 1px solid #555;
13
20
  border-radius: 0px 0px 8px 8px;
14
21
  cursor: pointer;
15
22
  z-index: 1;
16
23
 
17
24
  &[data-active='true'] {
18
- background-color: #ccc;
25
+ background-color: var(--active-bg);
26
+ color: var(--active-color);
19
27
  }
20
28
 
21
29
  &[aria-disabled='true'] {
@@ -24,16 +32,23 @@
24
32
  }
25
33
 
26
34
  .tremolo-piano-black-key {
35
+ --color: white;
36
+ --bg: #333;
37
+ --active-color: white;
38
+ --active-bg: #666;
39
+
27
40
  position: absolute;
28
- background-color: #333;
29
- color: white;
41
+ background-color: var(--bg);
42
+ -webkit-tap-highlight-color: transparent;
43
+ color: var(--color);
30
44
  border: 1px solid #555;
31
45
  border-radius: 0px 0px 8px 8px;
32
46
  cursor: pointer;
33
47
  z-index: 2;
34
48
 
35
49
  &[data-active='true'] {
36
- background-color: #666;
50
+ background-color: var(--active-bg);
51
+ color: var(--active-color);
37
52
  }
38
53
 
39
54
  &[aria-disabled='true'] {
@@ -49,6 +64,7 @@
49
64
  }
50
65
 
51
66
  .tremolo-piano-key-label {
67
+ box-sizing: content-box;
52
68
  display: flex;
53
69
  justify-content: center;
54
70
  align-items: center;
@@ -11,19 +11,13 @@ import React, {
11
11
  useState,
12
12
  } from 'react'
13
13
 
14
- import {
15
- isWhiteKey,
16
- NoteName,
17
- noteName,
18
- noteNames,
19
- parseNoteName,
20
- } from '@tremolo-ui/functions'
14
+ import { isWhiteKey, NoteKey, noteKeys, noteKey } from '@tremolo-ui/functions'
21
15
 
22
16
  import { useEventListener } from '../../hooks/useEventListener'
23
17
 
24
18
  import { BlackKey } from './BlackKey'
25
19
  import { KeyMethods, KeyProps } from './key'
26
- import { KeyBoardShortcuts } from './keyboardShortcuts'
20
+ import { KeyboardShortcuts } from './keyboardShortcuts'
27
21
  import { defaultWhiteKeyWidth, WhiteKey } from './WhiteKey'
28
22
 
29
23
  /**
@@ -43,7 +37,7 @@ export interface PianoProps {
43
37
 
44
38
  glissando?: boolean
45
39
  midiMax?: number
46
- keyboardShortcuts?: KeyBoardShortcuts
40
+ keyboardShortcuts?: KeyboardShortcuts
47
41
  fill?: boolean
48
42
 
49
43
  style?: CSSProperties
@@ -59,7 +53,10 @@ export interface PianoProps {
59
53
  children?: ReactElement | ReactElement[]
60
54
  }
61
55
 
62
- /** @category Piano */
56
+ /**
57
+ * Customizable piano component.
58
+ * @category Piano
59
+ */
63
60
  export function Piano({
64
61
  noteRange,
65
62
  glissando = true,
@@ -73,6 +70,8 @@ export function Piano({
73
70
  onStopNote,
74
71
  label,
75
72
  children,
73
+ onPointerUp,
74
+ onPointerDown,
76
75
  ...props
77
76
  }: PianoProps & Omit<ComponentPropsWithoutRef<'div'>, keyof PianoProps>) {
78
77
  // -- state and ref ---
@@ -111,7 +110,7 @@ export function Piano({
111
110
 
112
111
  // --- internal functions ---
113
112
  function notePosition(note: number) {
114
- const pitchPositions: Record<NoteName, number> = {
113
+ const pitchPositions: Record<NoteKey, number> = {
115
114
  C: 0,
116
115
  'C#': 0.55,
117
116
  D: 1,
@@ -126,16 +125,12 @@ export function Piano({
126
125
  B: 6,
127
126
  }
128
127
 
129
- const toNoteName = (n: number) => {
130
- const { letter, accidental } = parseNoteName(noteName(n))
131
- return (letter + accidental) as NoteName
132
- }
133
- const _noteName = toNoteName(note)
134
- const firstNoteName = toNoteName(noteRange.first)
135
- const pos = pitchPositions[_noteName] - pitchPositions[firstNoteName]
128
+ const targetNoteKey = noteKey(note)
129
+ const firstNoteKey = noteKey(noteRange.first)
130
+ const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey]
136
131
  const octave = Math.floor((note - noteRange.first) / 12)
137
132
  const octaveOffset =
138
- noteNames.indexOf(firstNoteName) > noteNames.indexOf(_noteName) ? 1 : 0
133
+ noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey) ? 1 : 0
139
134
  const length = whiteNoteWidth + 3
140
135
  return pos * length + (octave + octaveOffset) * 7 * length
141
136
  }
@@ -150,6 +145,9 @@ export function Piano({
150
145
  setWhiteNoteWidth(w / whiteNoteCount - 3)
151
146
  })
152
147
  resizeObserver.observe(parent)
148
+ return () => {
149
+ resizeObserver.unobserve(parent)
150
+ }
153
151
  } else {
154
152
  setWhiteNoteWidth(whiteKeyProps?.width || defaultWhiteKeyWidth)
155
153
  }
@@ -185,12 +183,12 @@ export function Piano({
185
183
  ...style,
186
184
  }}
187
185
  onPointerDown={(e) => {
188
- if (props.onPointerDown) props.onPointerDown(e)
189
186
  setPressed(true)
187
+ onPointerDown?.(e)
190
188
  }}
191
189
  onPointerUp={(e) => {
192
- if (props.onPointerUp) props.onPointerUp(e)
193
190
  setPressed(false)
191
+ onPointerUp?.(e)
194
192
  }}
195
193
  {...props}
196
194
  >
@@ -7,6 +7,11 @@ export interface KeyProps {
7
7
  width?: number
8
8
  height?: number | `${number}%`
9
9
 
10
+ bg?: string
11
+ color?: string
12
+ activeBg?: string
13
+ activeColor?: string
14
+
10
15
  style?: CSSProperties
11
16
 
12
17
  /**
@@ -1,5 +1,5 @@
1
1
  /** @category Piano */
2
- export type KeyBoardShortcuts = {
2
+ export type KeyboardShortcuts = {
3
3
  keys: string[]
4
4
  // TODO
5
5
  flags?: {
@@ -8,7 +8,7 @@ export type KeyBoardShortcuts = {
8
8
  }
9
9
 
10
10
  /** @category Piano */
11
- export const Shortcuts = {
11
+ export const SHORTCUTS = {
12
12
  HOME_ROW: {
13
13
  keys: [
14
14
  'a',
@@ -14,9 +14,6 @@ export interface ScaleProps {
14
14
  children?: ReactNode
15
15
  }
16
16
 
17
- type Props = ScaleProps &
18
- Omit<ComponentPropsWithoutRef<'div'>, keyof ScaleProps>
19
-
20
17
  /** @category Slider */
21
18
  export function Scale({
22
19
  gap = 6,
@@ -25,7 +22,7 @@ export function Scale({
25
22
  className,
26
23
  style,
27
24
  ...props
28
- }: Props) {
25
+ }: ScaleProps & Omit<ComponentPropsWithoutRef<'div'>, keyof ScaleProps>) {
29
26
  const min = useSliderContext((s) => s.min)
30
27
  const max = useSliderContext((s) => s.max)
31
28
  const step = useSliderContext((s) => s.step)
@@ -15,6 +15,8 @@ export interface SliderThumbProps {
15
15
  width?: number | string
16
16
  height?: number | string
17
17
 
18
+ color?: string
19
+
18
20
  className?: string
19
21
  style?: CSSProperties
20
22
  children?: ReactNode
@@ -39,6 +41,7 @@ export const SliderThumb = forwardRef<SliderThumbMethods, SliderThumbProps>(
39
41
  size,
40
42
  width = defaultThumbSize,
41
43
  height = defaultThumbSize,
44
+ color,
42
45
  children,
43
46
  className,
44
47
  style,
@@ -82,6 +85,7 @@ export const SliderThumb = forwardRef<SliderThumbMethods, SliderThumbProps>(
82
85
  aria-disabled={disabled}
83
86
  aria-readonly={readonly}
84
87
  style={{
88
+ ...{ '--color': color },
85
89
  width: size ?? width,
86
90
  height: size ?? height,
87
91
  ...style,
@@ -1,5 +1,5 @@
1
1
  import clsx from 'clsx'
2
- import { CSSProperties, ReactElement } from 'react'
2
+ import { ComponentPropsWithoutRef, CSSProperties, ReactElement } from 'react'
3
3
 
4
4
  import { styleHelper, xor } from '@tremolo-ui/functions'
5
5
 
@@ -40,7 +40,9 @@ export function SliderTrack({
40
40
  defaultStyle = true,
41
41
  __thumb,
42
42
  __percent = 0,
43
- }: SliderTrackProps) {
43
+ ...props
44
+ }: SliderTrackProps &
45
+ Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>) {
44
46
  const vertical = useSliderContext((s) => s.vertical)
45
47
  const reverse = useSliderContext((s) => s.reverse)
46
48
  const disabled = useSliderContext((s) => s.disabled)
@@ -70,6 +72,7 @@ export function SliderTrack({
70
72
  ...style,
71
73
  }
72
74
  }
75
+ {...props}
73
76
  >
74
77
  {children}
75
78
  {__thumb}
@@ -16,7 +16,9 @@
16
16
  }
17
17
 
18
18
  .tremolo-slider-thumb {
19
- background: #4e76e6;
19
+ --color: var(--tremolo-theme-color);
20
+
21
+ background: var(--color);
20
22
  border-radius: 50%;
21
23
  outline: none;
22
24
 
@@ -25,7 +27,8 @@
25
27
  }
26
28
 
27
29
  &[aria-readonly='false']:focus {
28
- box-shadow: 0px 0px 0px 3px rgba(var(--tremolo-theme-color-rgb), 0.2);
30
+ box-shadow: 0px 0px 0px 3px
31
+ color-mix(in srgb, var(--color) 20%, transparent);
29
32
  }
30
33
  }
31
34