@tremolo-ui/react 0.4.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 (62) hide show
  1. package/dist/index.cjs +1125 -1437
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +58 -93
  4. package/dist/index.d.cts +561 -243
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.ts +561 -243
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1125 -1438
  9. package/dist/index.js.map +1 -1
  10. package/package.json +9 -10
  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 +90 -61
  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 +104 -24
  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 +127 -146
  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 +190 -230
  47. package/src/hooks/useDrag.ts +1 -1
  48. package/src/hooks/useDragValue.ts +149 -0
  49. package/src/hooks/useEventListener.ts +1 -1
  50. package/src/hooks/useWheel.ts +29 -9
  51. package/src/index.ts +15 -9
  52. package/src/components/AnimationCanvas/canvas.ts +0 -46
  53. package/src/components/NumberInput/InternalInput.tsx +0 -216
  54. package/src/components/NumberInput/type.ts +0 -58
  55. package/src/components/Piano/KeyLabel.tsx +0 -51
  56. package/src/components/Piano/context.tsx +0 -73
  57. package/src/components/Piano/key.tsx +0 -205
  58. package/src/components/_util/type.ts +0 -1
  59. package/src/hooks/useDragWithElement.ts +0 -100
  60. package/src/hooks/usePianoDrag.ts +0 -75
  61. package/src/hooks/useRefCallbackEvent.ts +0 -34
  62. /package/src/hooks/{useCallbackRef.ts → _internal/useCallbackRef.ts} +0 -0
package/dist/index.d.cts CHANGED
@@ -1,27 +1,23 @@
1
1
  import * as _$react from "react";
2
- import React$1, { CSSProperties, ComponentPropsWithoutRef, DependencyList, ReactElement, ReactNode, Ref, RefObject, SVGProps } from "react";
3
- import { InputEventOption } from "@tremolo-ui/functions";
2
+ import React, { CSSProperties, ComponentPropsWithoutRef, DependencyList, ReactElement, ReactNode, Ref, RefObject, SVGProps } from "react";
4
3
  import * as _$_tremolo_ui_dom0 from "@tremolo-ui/dom";
5
- import { MIDIAccessError, NOT_SUPPORTED, PERMISSION_DENIED } from "@tremolo-ui/dom";
4
+ import { AnimationFrame, AxisOptions, MIDIAccessError, NOT_SUPPORTED, PERMISSION_DENIED, WheelOptions, XY, XY as XY$1, XYInput, XYInput as XYInput$1 } from "@tremolo-ui/dom";
5
+ import { InputEventOption, NoteRange, Scale, Units, ValueRange } from "@tremolo-ui/functions";
6
6
 
7
7
  //#region src/components/AnimationCanvas/index.d.ts
8
8
  type InitFunction = (context: CanvasRenderingContext2D, option: {
9
9
  /** current canvas width */width: number; /** current canvas height */
10
10
  height: number;
11
11
  }) => void;
12
- type DrawFunction = (context: CanvasRenderingContext2D, option: {
13
- /** current canvas width */width: number; /** current canvas height */
14
- height: number; /** frame count */
15
- count: number; /** delta time (ms) */
16
- deltaTime: number; /** elapsed time (ms) */
17
- elapsedTime: number; /** frame per second */
18
- fps: number;
19
- }) => void;
12
+ type DrawFunction = (context: CanvasRenderingContext2D, option: AnimationFrame) => void;
20
13
  interface CommonProps {
21
14
  draw: DrawFunction;
22
15
  init?: InitFunction;
23
16
  animate?: boolean;
24
17
  /**
18
+ * Read once, when the 2D context is created, so changing it later has no
19
+ * effect. Passing a fresh object on every render is therefore harmless.
20
+ *
25
21
  * @see https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement/getContext#contextattributes
26
22
  */
27
23
  options?: CanvasRenderingContext2DSettings;
@@ -67,22 +63,22 @@ declare function InactiveLine({
67
63
  ...props
68
64
  }: Omit<SVGProps<SVGPathElement>, 'd'>): _$react.JSX.Element;
69
65
  //#endregion
70
- //#region src/components/_util/type.d.ts
71
- type Override<T, U> = T & Omit<U, keyof T>;
72
- //#endregion
73
66
  //#region src/components/Knob/SVGRoot.d.ts
74
- interface SVGRoot {
75
- block?: boolean;
76
- overflowVisible?: boolean;
67
+ interface SVGRootProps {
68
+ /**
69
+ * `<Knob.InactiveLine />`, `<Knob.ActiveLine />` and `<Knob.Thumb />` go
70
+ * here, in the order you want them painted.
71
+ */
72
+ children: ReactNode;
77
73
  }
78
74
  declare function SVGRoot({
79
75
  children,
80
76
  style,
81
77
  ...props
82
- }: Override<SVGRoot, SVGProps<SVGSVGElement>>): _$react.JSX.Element;
78
+ }: SVGRootProps & Omit<SVGProps<SVGSVGElement>, keyof SVGRootProps>): _$react.JSX.Element;
83
79
  //#endregion
84
80
  //#region src/components/Knob/Thumb.d.ts
85
- interface Props {
81
+ interface Props$1 {
86
82
  /** color */
87
83
  thumb?: string;
88
84
  /** color */
@@ -98,7 +94,7 @@ interface Props {
98
94
  thumbLine?: string;
99
95
  };
100
96
  }
101
- declare function Thumb({
97
+ declare function Thumb$2({
102
98
  className,
103
99
  thumb,
104
100
  thumbLine,
@@ -107,7 +103,7 @@ declare function Thumb({
107
103
  thumbLineLength,
108
104
  classes,
109
105
  ...props
110
- }: Props & Omit<SVGProps<SVGSVGElement>, 'd' | keyof Props>): _$react.JSX.Element;
106
+ }: Props$1 & Omit<SVGProps<SVGSVGElement>, 'd' | keyof Props$1>): _$react.JSX.Element;
111
107
  //#endregion
112
108
  //#region src/components/Knob/index.d.ts
113
109
  interface KnobProps {
@@ -115,7 +111,16 @@ interface KnobProps {
115
111
  min: number;
116
112
  max: number;
117
113
  step?: number;
118
- skew?: number;
114
+ /**
115
+ * How the value is distributed across the travel.
116
+ *
117
+ * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,
118
+ * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or
119
+ * `skewScale(n)` for a value that has to match a JUCE parameter.
120
+ *
121
+ * @default linearScale
122
+ */
123
+ scale?: Scale;
119
124
  /**
120
125
  * value set when double-clicking
121
126
  * restriction: enableDoubleClickDefault = true
@@ -129,6 +134,10 @@ interface KnobProps {
129
134
  */
130
135
  startValue?: number;
131
136
  /** width and height */
137
+ /**
138
+ * Width and height of the knob.
139
+ * Defaults to the `--knob-size` CSS variable (50px).
140
+ */
132
141
  size?: number | string;
133
142
  /**
134
143
  * Global style to apply when dragged
@@ -154,6 +163,20 @@ interface KnobProps {
154
163
  /** angle range [degree] */
155
164
  angleRange?: number;
156
165
  onChange?: (value: number) => void;
166
+ /**
167
+ * The knob renders exactly what you compose here; there is no default
168
+ * markup to fall back to.
169
+ *
170
+ * @example
171
+ * <Knob.Root value={value} min={0} max={100} onChange={setValue}>
172
+ * <Knob.SVGRoot>
173
+ * <Knob.InactiveLine />
174
+ * <Knob.ActiveLine />
175
+ * <Knob.Thumb />
176
+ * </Knob.SVGRoot>
177
+ * </Knob.Root>
178
+ */
179
+ children: ReactNode;
157
180
  }
158
181
  interface KnobMethods {
159
182
  focus: () => void;
@@ -167,67 +190,148 @@ declare const Knob: {
167
190
  SVGRoot: typeof SVGRoot;
168
191
  InactiveLine: typeof InactiveLine;
169
192
  ActiveLine: typeof ActiveLine;
170
- Thumb: typeof Thumb;
193
+ Thumb: typeof Thumb$2;
171
194
  };
172
195
  //#endregion
173
- //#region src/components/NumberInput/DecrementStepper.d.ts
174
- interface DecrementStepperProps {
175
- size?: number;
196
+ //#region src/components/NumberInput/stepperButton.d.ts
197
+ interface StepperButtonProps {
198
+ className?: string;
199
+ style?: CSSProperties;
200
+ /** Replaces the default arrow. Size it with the `--stepper-icon-size` variable. */
176
201
  children?: ReactNode;
177
202
  }
178
- declare function DecrementStepper({
179
- size,
180
- children,
181
- className,
182
- ...props
183
- }: DecrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof DecrementStepperProps>): _$react.JSX.Element;
203
+ //#endregion
204
+ //#region src/components/NumberInput/DecrementStepper.d.ts
205
+ type DecrementStepperProps = StepperButtonProps;
206
+ /** Lowers the value by one `step`, repeating while held. */
207
+ declare function DecrementStepper(props: DecrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof DecrementStepperProps>): _$react.JSX.Element;
184
208
  //#endregion
185
209
  //#region src/components/NumberInput/IncrementStepper.d.ts
186
- interface IncrementStepperProps {
187
- size?: number;
188
- children?: ReactNode;
210
+ type IncrementStepperProps = StepperButtonProps;
211
+ /** Raises the value by one `step`, repeating while held. */
212
+ declare function IncrementStepper(props: IncrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof IncrementStepperProps>): _$react.JSX.Element;
213
+ //#endregion
214
+ //#region src/components/NumberInput/InputField.d.ts
215
+ interface NumberInputFieldProps {
216
+ /**
217
+ * Select the text when the input takes focus. `'number'` selects the leading
218
+ * number, leaving whatever the format appended to it.
219
+ * @default 'none'
220
+ */
221
+ selectOnFocus?: 'all' | 'number' | 'none';
222
+ /**
223
+ * Commit and leave the input when Enter is pressed. Enter commits either way.
224
+ * @default true
225
+ */
226
+ blurOnEnter?: boolean;
227
+ className?: string;
228
+ style?: CSSProperties;
229
+ ref?: Ref<HTMLInputElement>;
189
230
  }
190
- declare function IncrementStepper({
191
- size,
192
- children,
231
+ /**
232
+ * The text field of a `NumberInput`, and the only place the value can be typed.
233
+ *
234
+ * While the user types, their own text stands rather than `format(value)`, so
235
+ * that a half-finished entry is not rewritten under the caret.
236
+ */
237
+ declare function InputField({
238
+ selectOnFocus,
239
+ blurOnEnter,
193
240
  className,
241
+ style,
242
+ ref,
243
+ onFocus,
244
+ onBlur,
245
+ onKeyDown,
194
246
  ...props
195
- }: IncrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof IncrementStepperProps>): _$react.JSX.Element;
247
+ }: NumberInputFieldProps & Omit<ComponentPropsWithoutRef<'input'>, keyof NumberInputFieldProps | 'type' | 'value' | 'defaultValue'>): _$react.JSX.Element;
196
248
  //#endregion
197
249
  //#region src/components/NumberInput/Stepper.d.ts
198
250
  interface StepperProps {
199
- /** Display only when hovering. */
200
- dynamic?: boolean;
251
+ className?: string;
252
+ style?: CSSProperties;
253
+ /** `<NumberInput.IncrementStepper />` and `<NumberInput.DecrementStepper />` go here. */
201
254
  children?: ReactNode;
255
+ ref?: Ref<HTMLDivElement>;
202
256
  }
257
+ /**
258
+ * The area the steppers sit in, and a drag handle in its own right: dragging it
259
+ * up and down moves the value one `step` every `drag` pixels.
260
+ *
261
+ * The drag lives here rather than on `InputField` because `createDrag` turns
262
+ * off text selection on whatever element it is attached to.
263
+ */
203
264
  declare function Stepper({
204
- dynamic,
205
- children,
206
265
  className,
266
+ style,
267
+ children,
268
+ ref,
207
269
  ...props
208
270
  }: StepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof StepperProps>): _$react.JSX.Element;
209
271
  //#endregion
210
- //#region src/components/NumberInput/type.d.ts
272
+ //#region src/components/NumberInput/context.d.ts
273
+ type NumberInputContextValue = {
274
+ value: number;
275
+ /**
276
+ * The range the caller asked for. Left undefined when unbounded, which is
277
+ * what `aria-valuemin` / `aria-valuemax` and the steppers need to tell apart
278
+ * from a range that happens to sit at the edge of the safe integers.
279
+ */
280
+ min?: number;
281
+ max?: number;
282
+ step: number; /** How the value is distributed across the travel. */
283
+ scale: Scale;
284
+ disabled: boolean;
285
+ readonly: boolean;
286
+ clampValue: boolean;
287
+ /**
288
+ * The effective scaling for `applyDelta` and for clamping on commit, with the
289
+ * unbounded ends filled in. Ignores `min` / `max` when `clampValue` is off.
290
+ */
291
+ range: ValueRange;
292
+ keyboard: InputEventOption | null; /** Pixels of vertical drag on `Stepper` that move the value by one `step`. */
293
+ drag: number | null; /** What the input shows: the draft while editing, the formatted value otherwise. */
294
+ text: string;
295
+ editing: boolean; /** Whether the value sits outside `min` / `max`. Not judged while editing. */
296
+ outOfRange: boolean; /** Whether a stepper would have no effect, for `aria-disabled`. */
297
+ atMin: boolean;
298
+ atMax: boolean;
299
+ format: (value: number) => string;
300
+ parse: (text: string) => number; /** Typing. Replaces the draft and reports the parsed value, unclamped. */
301
+ setDraft: (text: string) => void; /** Blur or Enter. Clamps the draft, reports it, and drops the draft. */
302
+ commitDraft: () => void; /** Drops the draft and replaces the value: steppers, wheel, keyboard, drag. */
303
+ changeValue: (next: number) => void; /** Moves the value by one `option` in `direction`, normally +1 or -1. */
304
+ nudge: (direction: number, option: InputEventOption) => void; /** `InputField` registers itself here; `Root` focuses it through its methods. */
305
+ inputRef: RefObject<HTMLInputElement | null>;
306
+ };
211
307
  /**
212
- * [unit, scale][]
213
- * @example
214
- * [['Hz', 1], ['kHz', 1000]]
308
+ * The only state behind this is the editing draft; `value` comes from the props
309
+ * of `Root` and everything else is derived during render.
215
310
  */
216
- type Units = [string, number][];
311
+ declare function useNumberInputContext(): NumberInputContextValue;
312
+ declare function useNumberInputContext<T>(selector: (state: NumberInputContextValue) => T): T;
217
313
  //#endregion
218
314
  //#region src/components/NumberInput/index.d.ts
219
315
  interface NumberInputProps {
220
- value: number | string;
221
316
  /**
222
- * Number.MIN_SAFE_INTEGER
317
+ * The value. What the input shows is `format(value)`, except while the user
318
+ * is typing, when their own text stands until it is committed.
223
319
  */
320
+ value: number;
224
321
  min?: number;
225
- /**
226
- * Number.MAX_SAFE_INTEGER
227
- */
228
322
  max?: number;
229
323
  step?: number;
230
324
  /**
325
+ * How the value is distributed across the travel of a drag or a
326
+ * `'normalized'` wheel / keyboard nudge.
327
+ *
328
+ * @default linearScale
329
+ */
330
+ scale?: Scale;
331
+ /**
332
+ * Unit to display the value in, or a list to pick from by magnitude.
333
+ * Builds the default `format` and `parse`.
334
+ *
231
335
  * @example
232
336
  * units='Hz'
233
337
  * units={[['Hz', 1], ['kHz', 1000]]}
@@ -235,23 +339,26 @@ interface NumberInputProps {
235
339
  */
236
340
  units?: string | Units;
237
341
  /**
238
- * Digits for rounding numbers.
342
+ * Digits after the decimal point, for the default `format`.
343
+ *
239
344
  * @example
240
- * with:
241
- * units={[['Hz', 1], ['kHz', 1000]]}
242
- * digit={3}
243
- * results:
244
- * value=100 -> 100Hz, value=1600 -> 1.60Hz
345
+ * // units={[['Hz', 1], ['kHz', 1000]]} digit={2}
346
+ * // 100 -> 100.00Hz, 1600 -> 1.60kHz
245
347
  */
246
348
  digit?: number;
247
- readonly?: boolean;
248
- variant?: 'outline' | 'filled' | 'flushed' | 'unstyled';
249
- selectWithFocus?: 'all' | 'number' | 'none';
250
- blurOnEnter?: boolean;
251
- keepWithinRange?: boolean;
252
- clampValueOnBlur?: boolean;
349
+ /** Render the value as text. Takes precedence over `units` / `digit`. */
350
+ format?: (value: number) => string;
351
+ /** Read a value back out of the text. Takes precedence over `units`. */
352
+ parse?: (text: string) => number;
253
353
  /**
254
- * wheel control option
354
+ * Keep the value within `min` and `max` when it is committed or stepped.
355
+ * Typing is never clamped, so that a value can be entered digit by digit.
356
+ * @default true
357
+ */
358
+ clampValue?: boolean;
359
+ /**
360
+ * Wheel control option. Only applies while the focus is inside, so that
361
+ * scrolling past the input does not change it.
255
362
  * If null, no event will be triggered
256
363
  */
257
364
  wheel?: InputEventOption | null;
@@ -260,12 +367,40 @@ interface NumberInputProps {
260
367
  * If null, no event will be triggered
261
368
  */
262
369
  keyboard?: InputEventOption | null;
263
- activeColor?: string;
264
- wrapperClassName?: string;
265
- onChange?: (value: number, text: string) => void;
266
- onFocus?: (value: number, text: string, event: React.FocusEvent<HTMLInputElement, Element>) => void;
267
- onBlur?: (value: number, text: string, event: React.FocusEvent<HTMLInputElement, Element>) => void;
268
- children?: ReactNode;
370
+ /**
371
+ * Pixels of vertical drag on `Stepper` that move the value by one `step`.
372
+ * If null, no event will be triggered
373
+ * @default 1
374
+ */
375
+ drag?: number | null;
376
+ /**
377
+ * Only the appearance will change.
378
+ * Please consider using with readonly.
379
+ * aria-disabled property is also applied.
380
+ */
381
+ disabled?: boolean;
382
+ /**
383
+ * Make the value unchangeable.
384
+ * aria-readonly property is also applied.
385
+ */
386
+ readonly?: boolean;
387
+ className?: string;
388
+ style?: CSSProperties;
389
+ onChange?: (value: number) => void;
390
+ /**
391
+ * The input renders exactly what you compose here; there is no default
392
+ * markup to fall back to.
393
+ *
394
+ * @example
395
+ * <NumberInput.Root value={value} min={0} max={100} onChange={setValue}>
396
+ * <NumberInput.InputField />
397
+ * <NumberInput.Stepper>
398
+ * <NumberInput.IncrementStepper />
399
+ * <NumberInput.DecrementStepper />
400
+ * </NumberInput.Stepper>
401
+ * </NumberInput.Root>
402
+ */
403
+ children: ReactNode;
269
404
  }
270
405
  interface NumberInputMethods {
271
406
  focus: () => void;
@@ -275,99 +410,127 @@ interface NumberInputMethods {
275
410
  * Input with some useful functions for entering numerical values.
276
411
  */
277
412
  declare const NumberInput: {
278
- Root: _$react.ForwardRefExoticComponent<NumberInputProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "type" | keyof NumberInputProps> & _$react.RefAttributes<NumberInputMethods>>;
413
+ Root: _$react.ForwardRefExoticComponent<NumberInputProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof NumberInputProps> & _$react.RefAttributes<NumberInputMethods>>;
414
+ InputField: typeof InputField;
279
415
  Stepper: typeof Stepper;
280
416
  IncrementStepper: typeof IncrementStepper;
281
417
  DecrementStepper: typeof DecrementStepper;
282
418
  };
283
419
  //#endregion
284
- //#region src/components/Piano/key.d.ts
285
- interface KeyProps {
286
- noteNumber: number;
287
- width?: number;
288
- height?: number | `${number}%`;
289
- bg?: string;
290
- color?: string;
291
- activeBg?: string;
292
- activeColor?: string;
293
- style?: CSSProperties;
294
- /**
295
- * \<KeyLabel />
296
- */
297
- children?: ReactElement;
298
- /** @internal */
299
- __width?: number;
300
- }
301
- interface KeyMethods {
302
- play: (velocity?: number) => void;
303
- stop: () => void;
304
- played: () => boolean;
305
- }
306
- //#endregion
307
- //#region src/components/Piano/context.d.ts
308
- type NoteRange = {
309
- first: number;
310
- last: number;
311
- };
312
- //#endregion
313
420
  //#region src/components/Piano/keyboardShortcuts.d.ts
314
421
  type KeyboardShortcuts = {
422
+ /**
423
+ * Keys laid out from `noteRange.first`, one entry per semitone.
424
+ *
425
+ * An empty string leaves that note without a shortcut: `KeyboardEvent.key` is
426
+ * never empty, so the entry can never match. Use it to skip the black keys
427
+ * (see {@link SHORTCUTS.HOME_ROW_NATURAL}) and keep the remaining entries
428
+ * lined up with the notes.
429
+ */
315
430
  keys: string[];
316
- flags?: {
317
- naturalOnly?: boolean;
318
- };
319
431
  };
432
+ /**
433
+ * Ready-made keyboard layouts. Both assume `noteRange.first` is a C.
434
+ */
320
435
  declare const SHORTCUTS: {
321
- HOME_ROW: {
436
+ /** Every semitone from C, over the two rows of a QWERTY keyboard. */HOME_ROW: {
437
+ keys: string[];
438
+ }; /** The white keys only, on the home row. Black keys have no shortcut. */
439
+ HOME_ROW_NATURAL: {
322
440
  keys: string[];
323
441
  };
324
442
  };
325
443
  //#endregion
326
- //#region src/components/Piano/KeyLabel.d.ts
327
- interface KeyLabelProps {
328
- /**
329
- * override Piano.label
330
- */
331
- label?: (note: number, index: number) => ReactNode;
332
- wrapperClassName?: string;
333
- wrapperStyle?: CSSProperties;
334
- /** @internal */
335
- __note?: number;
336
- /** @internal */
337
- __label?: (note: number, index: number) => ReactNode;
338
- }
339
- declare function KeyLabel({
340
- label,
341
- className,
342
- wrapperClassName,
343
- wrapperStyle,
344
- __note,
345
- __label,
346
- ...props
347
- }: KeyLabelProps & Omit<ComponentPropsWithoutRef<'div'>, keyof KeyLabelProps>): false | _$react.JSX.Element;
348
- //#endregion
349
444
  //#region src/components/Piano/index.d.ts
350
445
  /**
351
- * [noteRange.first, noteRange.first + 1 ..., noteRange.last]
446
+ * `style` that also takes CSS custom properties, which is how a key's colours
447
+ * are set: see `index.css` for the ones each key type reads.
448
+ */
449
+ type CSSVariables = Record<`--${string}`, string | number | undefined>;
450
+ /**
451
+ * What {@link PianoProps.keyProps} may return for one key.
452
+ *
453
+ * `data-*` attributes are spelled out because TypeScript only allows them on
454
+ * JSX syntax, not on an object type, and selecting on one is the usual way to
455
+ * mark a key out.
352
456
  */
353
- declare function getNoteRangeArray(noteRange: NoteRange): number[];
457
+ type KeyAttributes = Omit<ComponentPropsWithoutRef<'div'>, 'style'> & {
458
+ style?: CSSProperties & CSSVariables;
459
+ } & Record<`data-${string}`, string | number | boolean | undefined>;
460
+ /** What a key is, when {@link PianoProps.label} or `keyProps` is asked about it. */
461
+ interface KeyState {
462
+ /** Position in the note range, counting from `noteRange.first`. */
463
+ index: number;
464
+ keyType: 'white' | 'black';
465
+ /** Whether the note is currently sounding. */
466
+ active: boolean;
467
+ /** Whether the note is above {@link PianoProps.midiMax} and cannot sound. */
468
+ disabled: boolean;
469
+ }
354
470
  interface PianoProps {
355
471
  noteRange: NoteRange;
472
+ /**
473
+ * Let a pointer slide from one key to the next while it is down.
474
+ *
475
+ * @default true
476
+ */
356
477
  glissando?: boolean;
478
+ /**
479
+ * Highest note that can sound. Keys above it are drawn `aria-disabled`.
480
+ *
481
+ * @default 127
482
+ */
357
483
  midiMax?: number;
358
484
  keyboardShortcuts?: KeyboardShortcuts;
485
+ /**
486
+ * Fill the parent element, deriving the width of a white key from it.
487
+ * {@link PianoProps.whiteKeyWidth} is ignored.
488
+ *
489
+ * @default false
490
+ */
359
491
  fill?: boolean;
360
- whiteNoteWidth?: number;
361
- blackNoteWidth?: number;
492
+ /** @default 40 */
493
+ whiteKeyWidth?: number;
494
+ /**
495
+ * Space between two white keys.
496
+ * @default 1
497
+ */
498
+ keyGap?: number;
499
+ /**
500
+ * Width of a black key, as a fraction of {@link PianoProps.whiteKeyWidth}.
501
+ * @default 0.65
502
+ */
503
+ blackKeyWidthRatio?: number;
504
+ /**
505
+ * Height of a black key, as a fraction of the height of the keyboard.
506
+ * @default 0.6
507
+ */
508
+ blackKeyHeightRatio?: number;
509
+ /** @default `fill ? '100%' : 160` */
362
510
  height?: number | string;
363
- style?: CSSProperties;
364
- onPlayNote?: (note: number, velocity?: number) => void;
365
- onStopNote?: (note: number) => void;
366
- label?: (note: number, index: number) => ReactNode;
511
+ style?: CSSProperties & CSSVariables;
512
+ /**
513
+ * What to draw inside a key. `''`, `null` and `undefined` leave it bare, so
514
+ * a layout with gaps — {@link SHORTCUTS.HOME_ROW_NATURAL}, say — needs no
515
+ * special casing.
516
+ */
517
+ label?: (note: number, state: KeyState) => ReactNode;
367
518
  /**
368
- * \<WhiteKey /> | \<BlackKey />
519
+ * Extra props for one key, by note: a class, a style, a `data-*` attribute
520
+ * to select on.
521
+ *
522
+ * The geometry of the key (`left`, `width`, `height`) is applied after the
523
+ * returned `style` and cannot be overridden, so a key cannot be drawn
524
+ * somewhere other than where it responds.
525
+ *
526
+ * @example highlight the notes of a scale
527
+ * ```tsx
528
+ * keyProps={(note) => ({ 'data-in-scale': inScale(note, root, 'major') })}
529
+ * ```
369
530
  */
370
- children?: ReactElement | ReactElement[];
531
+ keyProps?: (note: number, state: KeyState) => KeyAttributes;
532
+ onPlayNote?: (note: number, velocity?: number) => void;
533
+ onStopNote?: (note: number) => void;
371
534
  }
372
535
  interface PianoMethods {
373
536
  playNote: (note: number, velocity?: number) => void;
@@ -377,25 +540,32 @@ interface PianoMethods {
377
540
  * Customizable piano component.
378
541
  */
379
542
  declare const Piano: {
380
- Root: React$1.ForwardRefExoticComponent<PianoProps & Omit<Omit<React$1.DetailedHTMLProps<React$1.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof PianoProps> & React$1.RefAttributes<PianoMethods>>;
381
- WhiteKey: React$1.ForwardRefExoticComponent<KeyProps & Omit<Omit<React$1.DetailedHTMLProps<React$1.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof KeyProps> & React$1.RefAttributes<KeyMethods>>;
382
- BlackKey: React$1.ForwardRefExoticComponent<KeyProps & Omit<Omit<React$1.DetailedHTMLProps<React$1.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof KeyProps> & React$1.RefAttributes<KeyMethods>>;
383
- KeyLabel: typeof KeyLabel;
543
+ Root: _$react.ForwardRefExoticComponent<PianoProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof PianoProps> & _$react.RefAttributes<PianoMethods>>;
384
544
  };
385
545
  //#endregion
386
546
  //#region src/components/PointsEditor/Background.d.ts
547
+ interface PointsEditorBackgroundProps {
548
+ /** Whatever the points are placed over: a graph, a canvas, an image. */
549
+ children?: ReactNode;
550
+ }
387
551
  declare function Background({
388
552
  className,
389
553
  children,
390
554
  ...props
391
- }: ComponentPropsWithoutRef<'div'>): _$react.JSX.Element;
555
+ }: PointsEditorBackgroundProps & Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorBackgroundProps>): _$react.JSX.Element;
392
556
  //#endregion
393
557
  //#region src/components/PointsEditor/Container.d.ts
558
+ interface PointsEditorContainerProps {
559
+ /** `<PointsEditor.Point />` goes here. */
560
+ children?: ReactNode;
561
+ ref?: Ref<HTMLDivElement>;
562
+ }
394
563
  declare function Container({
395
- className,
396
564
  children,
565
+ className,
566
+ ref,
397
567
  ...props
398
- }: ComponentPropsWithoutRef<'div'>): _$react.JSX.Element;
568
+ }: PointsEditorContainerProps & Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorContainerProps>): _$react.JSX.Element;
399
569
  //#endregion
400
570
  //#region src/components/PointsEditor/Point.d.ts
401
571
  type PointBaseType = {
@@ -414,8 +584,14 @@ interface PointProps<T extends PointBaseType> {
414
584
  width?: number | string;
415
585
  height?: number | string;
416
586
  color?: string;
587
+ /** Overrides the `disabled` of `PointsEditor.Root`. */
417
588
  disabled?: boolean;
589
+ /** Overrides the `readonly` of `PointsEditor.Root`. */
418
590
  readonly?: boolean;
591
+ /** Overrides the `wheel` of `PointsEditor.Root`. */
592
+ wheel?: InputEventOption | null;
593
+ /** Overrides the `keyboard` of `PointsEditor.Root`. */
594
+ keyboard?: InputEventOption | null;
419
595
  onChange?: (value: PointBaseType) => void;
420
596
  onDragStart?: (value: PointBaseType) => void;
421
597
  onDragEnd?: (value: PointBaseType) => void;
@@ -428,38 +604,93 @@ declare function Point<T extends PointBaseType>({
428
604
  width,
429
605
  height,
430
606
  color,
431
- disabled,
432
- readonly,
607
+ disabled: _disabled,
608
+ readonly: _readonly,
609
+ wheel: _wheel,
610
+ keyboard: _keyboard,
433
611
  onChange,
434
612
  onDragStart,
435
613
  onDragEnd,
436
614
  className,
437
615
  style,
438
616
  onPointerDown,
617
+ onKeyDown,
439
618
  ...props
440
619
  }: PointProps<T> & Omit<ComponentPropsWithoutRef<'div'>, keyof PointProps<T>>): _$react.JSX.Element;
441
620
  //#endregion
621
+ //#region src/components/PointsEditor/context.d.ts
622
+ type PointsEditorContextValue = {
623
+ disabled: boolean;
624
+ readonly: boolean; /** Inherited by every `Point`; `null` turns the wheel off. */
625
+ wheel: InputEventOption | null; /** Inherited by every `Point`; `null` turns the keyboard off. */
626
+ keyboard: InputEventOption | null;
627
+ externalStyles: {
628
+ userSelectNone?: boolean;
629
+ cursor?: Cursor;
630
+ };
631
+ /**
632
+ * `Container` registers its element here; `Point` normalizes the pointer
633
+ * against it, so a point is placed by its position within the container.
634
+ */
635
+ containerRef: RefObject<HTMLDivElement | null>;
636
+ };
637
+ /**
638
+ * The settings `Root` was given, for the subcomponents to read. There is no
639
+ * state to keep in sync: a point's value belongs to the `Point` that draws it.
640
+ */
641
+ declare function usePointsEditorContext(): PointsEditorContextValue;
642
+ declare function usePointsEditorContext<T>(selector: (state: PointsEditorContextValue) => T): T;
643
+ //#endregion
442
644
  //#region src/components/PointsEditor/index.d.ts
443
645
  interface PointsEditorProps {
444
646
  width?: number | string;
445
647
  height?: number | string;
446
- grid?: number | PointBaseType;
648
+ /**
649
+ * Only the appearance will change.
650
+ * Please consider using with readonly.
651
+ * aria-disabled property is also applied.
652
+ */
447
653
  disabled?: boolean;
654
+ /**
655
+ * Make the points unmovable.
656
+ * aria-readonly property is also applied.
657
+ */
448
658
  readonly?: boolean;
449
659
  externalStyles?: {
450
660
  userSelectNone?: boolean;
451
661
  cursor?: Cursor;
452
662
  };
453
663
  /**
454
- * wheel control option
664
+ * wheel control option for every `Point`. Shift selects the x axis.
455
665
  * If null, no event will be triggered
666
+ *
667
+ * A `Point` can override it with a `wheel` of its own.
456
668
  */
457
669
  wheel?: InputEventOption | null;
458
670
  /**
459
- * keyboard control option
671
+ * keyboard control option for every `Point`, on the arrow keys.
460
672
  * If null, no event will be triggered
673
+ *
674
+ * A `Point` can override it with a `keyboard` of its own.
461
675
  */
462
676
  keyboard?: InputEventOption | null;
677
+ /**
678
+ * The editor renders exactly what you compose here; there is no default
679
+ * markup to fall back to.
680
+ *
681
+ * @example
682
+ * <PointsEditor.Root>
683
+ * <PointsEditor.Background>
684
+ * <svg viewBox="0 0 200 100">...</svg>
685
+ * </PointsEditor.Background>
686
+ * <PointsEditor.Container>
687
+ * {points.map((point, i) => (
688
+ * <PointsEditor.Point key={i} value={point} onChange={...} />
689
+ * ))}
690
+ * </PointsEditor.Container>
691
+ * </PointsEditor.Root>
692
+ */
693
+ children: ReactNode;
463
694
  }
464
695
  /**
465
696
  * Multiple Point Controller
@@ -472,28 +703,28 @@ declare const PointsEditor: {
472
703
  };
473
704
  //#endregion
474
705
  //#region src/components/Slider/type.d.ts
475
- type ScaleType = 'mark' | 'mark-number' | 'number';
476
- type ScaleOptions = ['step', ScaleType] | [number, ScaleType];
706
+ type MarksType = 'mark' | 'mark-number' | 'number';
707
+ type MarksOptions = ['step', MarksType] | [number, MarksType];
477
708
  //#endregion
478
- //#region src/components/Slider/Scale.d.ts
479
- interface ScaleProps {
709
+ //#region src/components/Slider/Marks.d.ts
710
+ interface MarksProps {
480
711
  gap?: number | string;
481
- options?: ScaleOptions;
712
+ options?: MarksOptions;
482
713
  children?: ReactNode;
483
714
  }
484
- declare function Scale({
715
+ declare function Marks({
485
716
  gap,
486
717
  options,
487
718
  children,
488
719
  className,
489
720
  style,
490
721
  ...props
491
- }: ScaleProps & Omit<ComponentPropsWithoutRef<'div'>, keyof ScaleProps>): _$react.JSX.Element;
722
+ }: MarksProps & Omit<ComponentPropsWithoutRef<'div'>, keyof MarksProps>): _$react.JSX.Element;
492
723
  //#endregion
493
- //#region src/components/Slider/ScaleOption.d.ts
494
- interface ScaleOptionProps {
724
+ //#region src/components/Slider/MarksOption.d.ts
725
+ interface MarksOptionProps {
495
726
  value: number;
496
- type?: ScaleType;
727
+ type?: MarksType;
497
728
  /** Display text instead of value. */
498
729
  label?: string;
499
730
  /** mark thickness */
@@ -512,7 +743,7 @@ interface ScaleOptionProps {
512
743
  label?: CSSProperties;
513
744
  };
514
745
  }
515
- declare function ScaleOption({
746
+ declare function MarksOption({
516
747
  value,
517
748
  type,
518
749
  label,
@@ -525,24 +756,31 @@ declare function ScaleOption({
525
756
  className,
526
757
  style,
527
758
  ...props
528
- }: ScaleOptionProps & Omit<ComponentPropsWithoutRef<'div'>, keyof ScaleOptionProps>): _$react.JSX.Element;
759
+ }: MarksOptionProps & Omit<ComponentPropsWithoutRef<'div'>, keyof MarksOptionProps>): _$react.JSX.Element;
529
760
  //#endregion
530
761
  //#region src/components/Slider/Thumb.d.ts
531
762
  interface SliderThumbProps {
532
- size?: number | string;
533
- width?: number | string;
534
- height?: number | string;
763
+ /**
764
+ * Size comes from the `--thumb-size` CSS variable on `Slider.Root`, so that
765
+ * the root can reserve the matching amount of space around the track.
766
+ */
535
767
  color?: string;
536
768
  className?: string;
537
769
  style?: CSSProperties;
538
770
  children?: ReactNode;
539
- /** @internal */
540
- __percent?: number;
771
+ ref?: Ref<SliderThumbMethods>;
541
772
  }
542
773
  interface SliderThumbMethods {
543
774
  focus: () => void;
544
775
  blur: () => void;
545
776
  }
777
+ declare function Thumb$1({
778
+ color,
779
+ children,
780
+ className,
781
+ style,
782
+ ref
783
+ }: SliderThumbProps): _$react.JSX.Element;
546
784
  //#endregion
547
785
  //#region src/components/Slider/Track.d.ts
548
786
  interface SliderTrackProps {
@@ -553,11 +791,9 @@ interface SliderTrackProps {
553
791
  defaultStyle?: boolean;
554
792
  className?: string;
555
793
  style?: CSSProperties;
556
- children?: ReactElement;
557
- /** @internal */
558
- __thumb?: ReactElement;
559
- /** @internal */
560
- __percent?: number;
794
+ /** `<Slider.Thumb />` goes here. */
795
+ children?: ReactNode;
796
+ ref?: Ref<HTMLDivElement>;
561
797
  }
562
798
  declare function Track({
563
799
  length,
@@ -568,23 +804,35 @@ declare function Track({
568
804
  className,
569
805
  style,
570
806
  defaultStyle,
571
- __thumb,
572
- __percent,
807
+ ref,
573
808
  ...props
574
809
  }: SliderTrackProps & Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>): _$react.JSX.Element;
575
810
  //#endregion
576
811
  //#region src/components/Slider/context.d.ts
577
- type State = {
812
+ type SliderContextValue = {
813
+ value: number;
578
814
  min: number;
579
815
  max: number;
580
- step: number;
581
- skew: number;
816
+ step: number; /** How the value is distributed across the travel. */
817
+ scale: Scale;
582
818
  vertical: boolean;
583
819
  reverse: boolean;
584
820
  disabled: boolean;
585
821
  readonly: boolean;
822
+ /**
823
+ * Position of the thumb, 0-100, already accounting for the display direction
824
+ * implied by `vertical` and `reverse`.
825
+ */
826
+ percent: number; /** `Track` registers its element here; `Root` normalizes the pointer against it. */
827
+ trackRef: RefObject<HTMLDivElement | null>; /** `Thumb` registers itself here; `Root` focuses it when a drag starts. */
828
+ thumbRef: RefObject<SliderThumbMethods | null>;
586
829
  };
587
- declare function useSliderContext<T>(selector: (state: State) => T): T;
830
+ /**
831
+ * Everything here is derived during render, so there is no state to keep in
832
+ * sync: `value` comes from the props of `Root` and the rest follows from it.
833
+ */
834
+ declare function useSliderContext(): SliderContextValue;
835
+ declare function useSliderContext<T>(selector: (state: SliderContextValue) => T): T;
588
836
  //#endregion
589
837
  //#region src/components/Slider/index.d.ts
590
838
  interface SliderProps {
@@ -592,7 +840,16 @@ interface SliderProps {
592
840
  min: number;
593
841
  max: number;
594
842
  step?: number;
595
- skew?: number;
843
+ /**
844
+ * How the value is distributed across the travel.
845
+ *
846
+ * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,
847
+ * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or
848
+ * `skewScale(n)` for a value that has to match a JUCE parameter.
849
+ *
850
+ * @default linearScale
851
+ */
852
+ scale?: Scale;
596
853
  /**
597
854
  * slider orientation
598
855
  * aria-orientation property is also applied.
@@ -630,8 +887,18 @@ interface SliderProps {
630
887
  onChange?: (value: number) => void;
631
888
  onDragStart?: (value: number) => void;
632
889
  onDragEnd?: (value: number) => void;
633
- /** \<SliderThumb /> | \<SliderTrack /> */
634
- children?: ReactElement | ReactElement[];
890
+ /**
891
+ * The slider renders exactly what you compose here; there is no default
892
+ * markup to fall back to.
893
+ *
894
+ * @example
895
+ * <Slider.Root value={value} min={0} max={100} onChange={setValue}>
896
+ * <Slider.Track>
897
+ * <Slider.Thumb />
898
+ * </Slider.Track>
899
+ * </Slider.Root>
900
+ */
901
+ children: ReactNode;
635
902
  }
636
903
  interface SliderMethods {
637
904
  focus: () => void;
@@ -641,11 +908,11 @@ interface SliderMethods {
641
908
  * Customizable slider
642
909
  */
643
910
  declare const Slider: {
644
- Root: React$1.ForwardRefExoticComponent<SliderProps & Omit<Omit<React$1.DetailedHTMLProps<React$1.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof SliderProps> & React$1.RefAttributes<SliderMethods>>;
645
- Thumb: React$1.ForwardRefExoticComponent<SliderThumbProps & React$1.RefAttributes<SliderThumbMethods>>;
911
+ Root: React.ForwardRefExoticComponent<SliderProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof SliderProps> & React.RefAttributes<SliderMethods>>;
912
+ Thumb: typeof Thumb$1;
646
913
  Track: typeof Track;
647
- Scale: typeof Scale;
648
- ScaleOption: typeof ScaleOption;
914
+ Marks: typeof Marks;
915
+ MarksOption: typeof MarksOption;
649
916
  };
650
917
  //#endregion
651
918
  //#region src/components/XYPad/Area.d.ts
@@ -655,9 +922,9 @@ interface XYPadAreaProps {
655
922
  color?: string;
656
923
  className?: string;
657
924
  style?: CSSProperties;
658
- children?: ReactElement;
659
- /** inherit */
660
- __thumb?: ReactElement;
925
+ /** `<XYPad.Thumb />` goes here. */
926
+ children?: ReactNode;
927
+ ref?: Ref<HTMLDivElement>;
661
928
  }
662
929
  declare function Area({
663
930
  width,
@@ -666,65 +933,92 @@ declare function Area({
666
933
  children,
667
934
  className,
668
935
  style,
669
- __thumb,
936
+ ref,
670
937
  ...props
671
938
  }: XYPadAreaProps & Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadAreaProps>): _$react.JSX.Element;
672
939
  //#endregion
673
940
  //#region src/components/XYPad/Thumb.d.ts
674
941
  interface XYPadThumbProps {
675
- size?: number | string;
676
- width?: number | string;
677
- height?: number | string;
942
+ /**
943
+ * Size comes from the `--thumb-size` CSS variable on `XYPad.Root`, so that
944
+ * the root can reserve the matching amount of space around the area.
945
+ */
678
946
  color?: string;
679
947
  wrapperClassName?: string;
680
948
  wrapperStyle?: CSSProperties;
681
949
  children?: ReactNode;
682
- /** @internal */
683
- __disabled?: boolean;
684
- /** @internal */
685
- __readonly?: boolean;
686
- /** @internal */
687
- __css?: CSSProperties;
950
+ ref?: Ref<XYPadThumbMethods>;
688
951
  }
689
952
  interface XYPadThumbMethods {
690
953
  focus: () => void;
691
954
  blur: () => void;
692
955
  }
956
+ type Props = XYPadThumbProps & Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadThumbProps>;
957
+ declare function Thumb({
958
+ color,
959
+ children,
960
+ wrapperClassName,
961
+ wrapperStyle,
962
+ className,
963
+ style,
964
+ ref,
965
+ ...props
966
+ }: Props): _$react.JSX.Element;
693
967
  //#endregion
694
968
  //#region src/components/XYPad/index.d.ts
695
969
  /**
696
970
  * Two-dimensional slider component.
971
+ *
972
+ * The per-axis settings mirror `Slider`, given as `[x, y]` tuples. A plain
973
+ * value applies to both axes.
697
974
  */
698
- interface ValueOptions {
699
- value: number;
700
- min: number;
701
- max: number;
702
- step?: number;
703
- skew?: number;
704
- reverse?: boolean;
975
+ interface XYPadProps {
976
+ value: XY<number>;
977
+ min: XYInput<number>;
978
+ max: XYInput<number>;
979
+ step?: XYInput<number>;
705
980
  /**
706
- * wheel control option
981
+ * How the value of each axis is distributed across the travel.
982
+ *
983
+ * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,
984
+ * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or
985
+ * `skewScale(n)` for a value that has to match a JUCE parameter.
986
+ *
987
+ * @default linearScale
988
+ */
989
+ scale?: XYInput<Scale>;
990
+ reverse?: XYInput<boolean>;
991
+ /**
992
+ * wheel control option. Shift selects the x axis.
993
+ * If null, no event will be triggered
707
994
  */
708
995
  wheel?: InputEventOption | null;
709
996
  /**
710
997
  * keyboard control option
998
+ * If null, no event will be triggered
711
999
  */
712
1000
  keyboard?: InputEventOption | null;
713
- }
714
- interface XYPadProps {
715
- x: ValueOptions;
716
- y: ValueOptions;
717
1001
  externalStyles?: {
718
1002
  userSelectNone?: boolean;
719
1003
  cursor?: Cursor;
720
1004
  };
721
1005
  disabled?: boolean;
722
1006
  readonly?: boolean;
723
- onChange?: (valueX: number, valueY: number) => void;
724
- onDragStart?: (valueX: number, valueY: number) => void;
725
- onDragEnd?: (valueX: number, valueY: number) => void;
726
- /** \<XYPadThumb /> | \<XYPadArea /> */
727
- children?: ReactElement | ReactElement[];
1007
+ onChange?: (value: XY<number>) => void;
1008
+ onDragStart?: (value: XY<number>) => void;
1009
+ onDragEnd?: (value: XY<number>) => void;
1010
+ /**
1011
+ * The pad renders exactly what you compose here; there is no default
1012
+ * markup to fall back to.
1013
+ *
1014
+ * @example
1015
+ * <XYPad.Root value={[x, y]} min={0} max={100} onChange={setValue}>
1016
+ * <XYPad.Area>
1017
+ * <XYPad.Thumb />
1018
+ * </XYPad.Area>
1019
+ * </XYPad.Root>
1020
+ */
1021
+ children: ReactNode;
728
1022
  }
729
1023
  interface XYPadMethods {
730
1024
  focus: () => void;
@@ -735,8 +1029,8 @@ interface XYPadMethods {
735
1029
  * Simple XYPad
736
1030
  */
737
1031
  declare const XYPad: {
738
- Root: React$1.ForwardRefExoticComponent<XYPadProps & Omit<Omit<React$1.DetailedHTMLProps<React$1.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof XYPadProps> & React$1.RefAttributes<XYPadMethods>>;
739
- Thumb: React$1.ForwardRefExoticComponent<XYPadThumbProps & Omit<Omit<React$1.DetailedHTMLProps<React$1.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof XYPadThumbProps> & React$1.RefAttributes<XYPadThumbMethods>>;
1032
+ Root: _$react.ForwardRefExoticComponent<XYPadProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof XYPadProps> & _$react.RefAttributes<XYPadMethods>>;
1033
+ Thumb: typeof Thumb;
740
1034
  Area: typeof Area;
741
1035
  };
742
1036
  //#endregion
@@ -769,40 +1063,46 @@ declare function useDrag<T extends Element>({
769
1063
  onDragEnd
770
1064
  }: UseDragProps): (node: T | null) => void;
771
1065
  //#endregion
772
- //#region src/hooks/useDragWithElement.d.ts
773
- interface UseDragWithElement<T extends Element> {
774
- baseElementRef: RefObject<T | null>;
1066
+ //#region src/hooks/useDragValue.d.ts
1067
+ interface UseDragValueOptions {
1068
+ /** Scaling of each axis; a single value applies to both. */
1069
+ axis: XYInput$1<AxisOptions>;
775
1070
  /**
776
- * Report the position on pointer down, before any movement.
1071
+ * Normalize the pointer against the bounding rect of this element, so that
1072
+ * the value *is* the position pointed at.
777
1073
  *
778
- * Enable it where the pointer position *is* the value, so that a plain click
779
- * jumps to it. Leave it off where the element being dragged is an object in
780
- * its own right, so that grabbing its edge does not shift it under the cursor.
1074
+ * Give either this or `getValue`.
1075
+ */
1076
+ baseElementRef?: RefObject<Element | null>;
1077
+ /**
1078
+ * Move the value away from where it stood when the drag started, by the
1079
+ * distance dragged. The pointer position itself carries no meaning.
781
1080
  *
782
- * @default false
1081
+ * Give either this or `baseElementRef`.
783
1082
  */
1083
+ getValue?: () => XY$1<number>;
1084
+ /**
1085
+ * Pixels of movement that span the whole range, with `getValue`.
1086
+ * @default 100
1087
+ */
1088
+ pixelRange?: XYInput$1<number>;
1089
+ /** @see DragValueOptions.updateOnPointerDown */
784
1090
  updateOnPointerDown?: boolean;
1091
+ /** @default 0 */
1092
+ threshold?: number;
785
1093
  /** CSS cursor to show while dragging. Applied to the element itself. */
786
1094
  cursor?: string;
787
- onDrag: (normalizedX: number, normalizedY: number) => void;
788
- onDragStart?: (normalizedX: number, normalizedY: number) => void;
789
- onDragEnd?: (normalizedX: number, normalizedY: number) => void;
1095
+ onChange?: (value: XY$1<number>) => void;
1096
+ onDragStart?: (value: XY$1<number>) => void;
1097
+ onDragEnd?: (value: XY$1<number>) => void;
790
1098
  }
791
1099
  /**
792
- * Track a pointer drag, reporting the position normalized against
793
- * the bounding rect of `baseElementRef`.
1100
+ * Drive a value with a pointer drag.
794
1101
  *
795
1102
  * @returns a ref callback for the element that starts the drag, and whether a drag is in progress
796
1103
  */
797
- declare function useDragWithElement<T extends Element>({
798
- baseElementRef,
799
- updateOnPointerDown,
800
- cursor,
801
- onDrag,
802
- onDragStart,
803
- onDragEnd
804
- }: UseDragWithElement<T>): {
805
- refCallback: _$react.Dispatch<_$react.SetStateAction<Element | null>>;
1104
+ declare function useDragValue<T extends Element>(options: UseDragValueOptions): {
1105
+ refCallback: (node: T | null) => void;
806
1106
  dragging: boolean;
807
1107
  };
808
1108
  //#endregion
@@ -842,14 +1142,32 @@ declare function useMIDIInput(midiAccess: MIDIAccess | null, onNoteOnEvent?: (no
842
1142
  declare function useMIDIMessage(midiAccess: MIDIAccess | null, onMIDIMessage: (event: MIDIMessageEvent) => void): void;
843
1143
  //#endregion
844
1144
  //#region src/hooks/useWheel.d.ts
1145
+ interface UseWheelOptions extends WheelOptions {
1146
+ /**
1147
+ * Listen on this element rather than on the one the returned ref callback is
1148
+ * attached to, and ignore that callback.
1149
+ *
1150
+ * For a control made of several movable parts: a wheel event only reaches
1151
+ * what the cursor is over, so a listener per part responds only while the
1152
+ * cursor is over that same part. Listening on the element they share lets
1153
+ * every part see the event, and each one decide whether it is the one to act.
1154
+ *
1155
+ * The element is read when the listener is attached, so a ref filled in by a
1156
+ * parent is fine as long as the parent is above in the tree.
1157
+ */
1158
+ target?: RefObject<Element | null>;
1159
+ }
845
1160
  /**
846
1161
  * Listen to wheel events on an element.
847
1162
  *
848
1163
  * The listener is not passive, so the handler may call `preventDefault()`.
849
1164
  *
850
- * @returns a ref callback to attach to the element
1165
+ * @returns a ref callback to attach to the element, unused when `target` is given
851
1166
  */
852
- declare function useWheel<T extends Element>(onWheel: (event: WheelEvent) => void): (node: T | null) => void;
1167
+ declare function useWheel<T extends Element>(onWheel: (event: WheelEvent) => void, {
1168
+ requireFocus,
1169
+ target
1170
+ }?: UseWheelOptions): (node: T | null) => void;
853
1171
  //#endregion
854
- export { type AbsoluteSizingProps, AnimationCanvas, type AnimationCanvasProps, type CommonProps, type DecrementStepperProps, type DrawFunction, type IncrementStepperProps, type InitFunction, type KeyLabelProps, type KeyMethods, type KeyProps, type KeyboardShortcuts, Knob, type KnobMethods, type KnobProps, type MIDIAccessError, NOT_SUPPORTED, NumberInput, type NumberInputMethods, type NumberInputProps, PERMISSION_DENIED, Piano, type PianoMethods, type PianoProps, type PointBaseType, type PointProps, PointsEditor, type PointsEditorProps, type RelativeSizingProps, SHORTCUTS, type ScaleOptionProps, type ScaleProps, Slider, type SliderMethods, type SliderProps, type SliderThumbMethods, type SliderThumbProps, type SliderTrackProps, type StepperProps, type ValueOptions, XYPad, type XYPadAreaProps, type XYPadMethods, type XYPadProps, type XYPadThumbMethods, type XYPadThumbProps, clampPoint, getNoteRangeArray, useAnimationFrame, useDrag, useDragWithElement, useEventListener, useInterval, useLongPress, useMIDIAccess, useMIDIInput, useMIDIMessage, useSliderContext, useWheel };
1172
+ export { type AbsoluteSizingProps, AnimationCanvas, type AnimationCanvasProps, type CSSVariables, type CommonProps, type DecrementStepperProps, type DrawFunction, type IncrementStepperProps, type InitFunction, type KeyAttributes, type KeyState, type KeyboardShortcuts, Knob, type KnobMethods, type KnobProps, type MIDIAccessError, type MarksOptionProps, type MarksProps, NOT_SUPPORTED, NumberInput, type NumberInputFieldProps, type NumberInputMethods, type NumberInputProps, PERMISSION_DENIED, Piano, type PianoMethods, type PianoProps, type PointBaseType, type PointProps, PointsEditor, type PointsEditorBackgroundProps, type PointsEditorContainerProps, type PointsEditorContextValue, type PointsEditorProps, type RelativeSizingProps, SHORTCUTS, Slider, type SliderMethods, type SliderProps, type SliderThumbMethods, type SliderThumbProps, type SliderTrackProps, type StepperProps, type UseDragValueOptions, type UseWheelOptions, type XY, type XYInput, XYPad, type XYPadAreaProps, type XYPadMethods, type XYPadProps, type XYPadThumbMethods, type XYPadThumbProps, clampPoint, useAnimationFrame, useDrag, useDragValue, useEventListener, useInterval, useLongPress, useMIDIAccess, useMIDIInput, useMIDIMessage, useNumberInputContext, usePointsEditorContext, useSliderContext, useWheel };
855
1173
  //# sourceMappingURL=index.d.cts.map