@tremolo-ui/react 0.1.5 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tremolo-ui/react",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "React component library for audio app",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -46,7 +46,7 @@
46
46
  "react-dom": "^18 || ^19"
47
47
  },
48
48
  "dependencies": {
49
- "@tremolo-ui/functions": "^0.1.5",
49
+ "@tremolo-ui/functions": "^0.1.6",
50
50
  "clsx": "^2.1.1",
51
51
  "zustand": "^5.0.3"
52
52
  },
@@ -4,45 +4,76 @@
4
4
  overflow: visible;
5
5
  outline: 0;
6
6
 
7
- &:focus,
8
- &:hover {
9
- > .tremolo-knob-active-line {
10
- color: #4e76e6;
11
- }
7
+ &[aria-readonly='true'] {
8
+ cursor: not-allowed;
9
+ }
10
+ }
12
11
 
13
- > .tremolo-knob-inactive-line {
14
- color: #ddd;
15
- }
12
+ .tremolo-knob-active-line {
13
+ overflow: visible;
14
+ color: oklch(69.2% 0.17548 266.373);
16
15
 
17
- > .tremolo-knob-thumb {
18
- color: #ccc;
19
- }
16
+ :where(.tremolo-knob:focus, .tremolo-knob:hover) & {
17
+ color: var(--tremolo-theme-color);
20
18
  }
21
19
 
22
- &[aria-readonly='true'] {
23
- cursor: not-allowed;
20
+ :where(.tremolo-knob[aria-disabled='true']) & {
21
+ color: oklch(50.5% 0.03307 270.021);
24
22
  }
23
+ }
25
24
 
26
- &[aria-disabled='true'] {
27
- > .tremolo-knob-active-line {
28
- color: #5d6478;
25
+ :where(.dark, [data-theme='dark']) {
26
+ .tremolo-knob-active-line {
27
+ color: var(--tremolo-theme-color);
28
+
29
+ :where(.tremolo-knob:focus, .tremolo-knob:hover) & {
30
+ color: oklch(65.2% 0.17548 266.373);
29
31
  }
30
32
  }
31
33
  }
32
34
 
33
- .tremolo-knob-active-line {
34
- overflow: visible;
35
- color: #7998ec;
35
+ .tremolo-knob-inactive-line {
36
+ color: oklch(95% 0 0);
37
+
38
+ :where(.tremolo-knob:focus, .tremolo-knob:hover) & {
39
+ color: oklch(90% 0 0);
40
+ }
36
41
  }
37
42
 
38
- .tremolo-knob-inactive-line {
39
- color: #eee;
43
+ :where(.dark, [data-theme='dark']) {
44
+ .tremolo-knob-inactive-line {
45
+ color: oklch(41.7% 0 0);
46
+
47
+ :where(.tremolo-knob:focus, .tremolo-knob:hover) & {
48
+ color: oklch(44.59% 0 0);
49
+ }
50
+ }
40
51
  }
41
52
 
42
53
  .tremolo-knob-thumb {
43
- color: #d0d0d0;
54
+ color: oklch(85.7% 0 0);
55
+
56
+ :where(.tremolo-knob:focus, .tremolo-knob:hover) & {
57
+ color: oklch(84.5% 0 0);
58
+ }
59
+ }
60
+
61
+ :where(.dark, [data-theme='dark']) {
62
+ .tremolo-knob-thumb {
63
+ color: oklch(48.6% 0 0);
64
+
65
+ :where(.tremolo-knob:focus, .tremolo-knob:hover) & {
66
+ color: oklch(51.7% 0 0);
67
+ }
68
+ }
44
69
  }
45
70
 
46
71
  .tremolo-knob-thumb-line {
47
- color: #eee;
72
+ color: oklch(94.2% 0 0);
73
+ }
74
+
75
+ :where(.dark, [data-theme='dark']) {
76
+ .tremolo-knob-thumb-line {
77
+ color: oklch(84.2% 0 0);
78
+ }
48
79
  }
@@ -43,8 +43,8 @@ export interface KnobProps {
43
43
  */
44
44
  startValue?: number
45
45
 
46
- // TODO: relative size (Lower priority)
47
- size?: number
46
+ /** width and height */
47
+ size?: number | string
48
48
 
49
49
  /** Whether to apply `{use-select: none}` when dragging */
50
50
  bodyNoSelect?: boolean
@@ -69,15 +69,16 @@ export interface KnobProps {
69
69
  thumb?: string
70
70
  /** color */
71
71
  thumbLine?: string
72
-
73
72
  /** percent (0-100) */
74
73
  thumbSize?: number
75
74
  /** percent (0-100) */
76
75
  thumbLineWeight?: number
77
76
  /** percent (0-100) */
78
77
  thumbLineLength?: number
79
- /** percent (0-100) */
78
+ /** line weight [px] */
80
79
  lineWeight?: number
80
+ /** angle range [degree] */
81
+ angleRange?: number
81
82
 
82
83
  classes?: {
83
84
  activeLine?: string
@@ -125,6 +126,11 @@ export const Knob = forwardRef<KnobMethods, Props>(
125
126
  inactiveLine,
126
127
  thumb,
127
128
  thumbLine,
129
+ thumbSize = 84,
130
+ thumbLineWeight = 6,
131
+ thumbLineLength = 35,
132
+ lineWeight = 6,
133
+ angleRange = 270,
128
134
  onChange,
129
135
  onKeyDown,
130
136
  onPointerDown,
@@ -133,26 +139,18 @@ export const Knob = forwardRef<KnobMethods, Props>(
133
139
  classes,
134
140
  ...props
135
141
  }: Props,
136
- ref,
142
+ forwardedRef,
137
143
  ) => {
138
144
  const valueRef = useRef(0)
139
145
  const elmRef = useRef<HTMLOrSVGElement>(null)
140
146
 
141
- // --- interpret props ---
142
- const padding = 8 // %
143
- const thumbLineWeight = 6 // %
144
- const thumbLineLength = 35 // %
145
- const lineWeight = size * 0.06 // px
146
- const p = normalizeValue(value, min, max, skew)
147
- const s = normalizeValue(startValue, min, max, skew)
148
-
149
147
  // --- internal functions ---
150
148
  const onDrag = useCallback(
151
149
  (_x: number, y: number) => {
152
150
  if (!onChange || readonly) return
153
151
  const v = rawValue(valueRef.current - y / 100, min, max, skew)
154
- const v2 = clamp(stepValue(v, step), min, max)
155
- onChange(v2)
152
+ const clamped = clamp(stepValue(v, step), min, max)
153
+ onChange(clamped)
156
154
  },
157
155
  [max, min, onChange, readonly, skew, step],
158
156
  )
@@ -210,7 +208,7 @@ export const Knob = forwardRef<KnobMethods, Props>(
210
208
  [wheel, onChange, readonly, updateValueByEvent],
211
209
  )
212
210
 
213
- useImperativeHandle(ref, () => {
211
+ useImperativeHandle(forwardedRef, () => {
214
212
  return {
215
213
  focus() {
216
214
  elmRef.current?.focus()
@@ -221,11 +219,17 @@ export const Knob = forwardRef<KnobMethods, Props>(
221
219
  }
222
220
  }, [])
223
221
 
224
- const center = size / 2
225
- const r1 = -135
226
- const r2 = -135 + Math.min(p, s) * 270
227
- const r3 = -135 + Math.max(p, s) * 270
228
- const r4 = 135
222
+ const viewBoxSize = 100
223
+ const center = viewBoxSize / 2
224
+ const padding = (viewBoxSize - clamp(thumbSize, 0, 100)) / 2 // %
225
+ const p = normalizeValue(value, min, max, skew)
226
+ const s = normalizeValue(startValue, min, max, skew)
227
+
228
+ // 時計回りで描画していく
229
+ const r1 = -angleRange / 2 // ロータリー開始位置
230
+ const r2 = r1 + Math.min(p, s) * angleRange // activeLineの開始位置
231
+ const r3 = r1 + Math.max(p, s) * angleRange // activeLineの終了位置
232
+ const r4 = angleRange / 2 // ロータリー終了位置
229
233
  const x1 = center + center * Math.cos(radian(r1 - 90))
230
234
  const y1 = center + center * Math.sin(radian(r1 - 90))
231
235
  const x2 = center + center * Math.cos(radian(r2 - 90))
@@ -237,12 +241,13 @@ export const Knob = forwardRef<KnobMethods, Props>(
237
241
 
238
242
  return (
239
243
  <svg
240
- className={clsx('tremolo-knob', className)}
241
244
  ref={(div) => {
242
245
  elmRef.current = div
243
246
  wheelRefCallback(div)
244
247
  touchMoveRefCallback(div)
245
248
  }}
249
+ className={clsx('tremolo-knob', className)}
250
+ viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}
246
251
  width={size}
247
252
  height={size}
248
253
  tabIndex={0}
@@ -268,7 +273,7 @@ export const Knob = forwardRef<KnobMethods, Props>(
268
273
  }}
269
274
  {...props}
270
275
  >
271
- {/* rotary meter */}
276
+ {/* lines */}
272
277
  {startValue > min && (
273
278
  <path
274
279
  className={clsx(
@@ -305,7 +310,7 @@ export const Knob = forwardRef<KnobMethods, Props>(
305
310
  <circle
306
311
  cx="50%"
307
312
  cy="50%"
308
- r={`${50 - padding}%`}
313
+ r={`${thumbSize / 2}%`}
309
314
  fill={thumb || 'currentColor'}
310
315
  />
311
316
  <line
@@ -320,7 +325,7 @@ export const Knob = forwardRef<KnobMethods, Props>(
320
325
  // https://bugs.webkit.org/show_bug.cgi?id=201854
321
326
  // https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/transform-origin#browser_compatibility
322
327
  style={{
323
- transform: `rotate(${-135 + p * 270}deg)`,
328
+ transform: `rotate(${r1 + p * angleRange}deg)`,
324
329
  transformOrigin: '50% 50%',
325
330
  }}
326
331
  />
@@ -55,7 +55,6 @@ export interface NumberInputProps {
55
55
  keepWithinRange?: boolean
56
56
  clampValueOnBlur?: boolean
57
57
 
58
- // TODO
59
58
  /**
60
59
  * wheel control option
61
60
  */
@@ -126,9 +125,9 @@ export const NumberInput = forwardRef<NumberInputMethods, Props>(
126
125
  children,
127
126
  ...props
128
127
  }: Props,
129
- ref,
128
+ forwardedRef,
130
129
  ) => {
131
- const colors: { [key: string]: string | undefined } = {
130
+ const colors: Record<string, string | undefined> = {
132
131
  '--active-color': activeColor,
133
132
  }
134
133
  return (
@@ -154,7 +153,7 @@ export const NumberInput = forwardRef<NumberInputMethods, Props>(
154
153
  data-variant={variant}
155
154
  >
156
155
  <InternalInput
157
- ref={ref}
156
+ ref={forwardedRef}
158
157
  readonly={readonly}
159
158
  disabled={disabled}
160
159
  selectWithFocus={selectWithFocus}
@@ -12,7 +12,7 @@ type State = {
12
12
  glissando: boolean
13
13
  midiMax: number
14
14
  fill: boolean
15
- onPlayNote: (noteNumber: number) => void
15
+ onPlayNote: (noteNumber: number, velocity?: number) => void
16
16
  onStopNote: (noteNumber: number) => void
17
17
  label: (note: number, index: number) => ReactNode
18
18
  }
@@ -7,10 +7,10 @@
7
7
  }
8
8
 
9
9
  .tremolo-piano-white-key {
10
- --bg: white;
11
- --color: black;
12
- --active-bg: #ccc;
13
- --active-color: black;
10
+ --bg: oklch(100% 0 0);
11
+ --color: oklch(0% 0 0);
12
+ --active-bg: oklch(90% 0 0);
13
+ --active-color: oklch(0% 0 0);
14
14
 
15
15
  box-sizing: border-box;
16
16
  position: absolute;
@@ -33,10 +33,10 @@
33
33
  }
34
34
 
35
35
  .tremolo-piano-black-key {
36
- --color: white;
37
- --bg: #333;
38
- --active-color: white;
39
- --active-bg: #666;
36
+ --color: oklch(100% 0 0);
37
+ --bg: oklch(32.109% 0.00004 271.152);
38
+ --active-color: oklch(100% 0 0);
39
+ --active-bg: oklch(47.836% 0.00005 271.152);
40
40
 
41
41
  box-sizing: border-box;
42
42
  position: absolute;
@@ -79,3 +79,19 @@
79
79
  aspect-ratio: 1;
80
80
  max-width: calc(100% - 16px);
81
81
  }
82
+
83
+ :where(.dark, [data-theme='dark']) {
84
+ .tremolo-piano-white-key {
85
+ --bg: oklch(48.193% 0.00005 271.152);
86
+ --color: oklch(100% 0 0);
87
+ --active-bg: oklch(52.78% 0.00006 271.152);
88
+ --active-color: oklch(100% 0 0);
89
+ }
90
+
91
+ .tremolo-piano-black-key {
92
+ --color: oklch(100% 0 0);
93
+ --bg: oklch(32.109% 0 0);
94
+ --active-color: oklch(100% 0 0);
95
+ --active-bg: oklch(38.666% 0.00004 271.152);
96
+ }
97
+ }