@tremolo-ui/react 0.4.0 → 0.6.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 (90) hide show
  1. package/dist/compose-refs.cjs +72 -0
  2. package/dist/compose-refs.cjs.map +1 -0
  3. package/dist/compose-refs.d.cts +43 -0
  4. package/dist/compose-refs.d.cts.map +1 -0
  5. package/dist/compose-refs.d.ts +43 -0
  6. package/dist/compose-refs.d.ts.map +1 -0
  7. package/dist/compose-refs.js +70 -0
  8. package/dist/compose-refs.js.map +1 -0
  9. package/dist/index.cjs +2272 -1650
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1461 -475
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.ts +1461 -475
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2258 -1648
  16. package/dist/index.js.map +1 -1
  17. package/package.json +32 -31
  18. package/src/components/AnimationCanvas/index.tsx +162 -194
  19. package/src/components/Knob/ActiveLine.tsx +9 -8
  20. package/src/components/Knob/InactiveLine.tsx +11 -14
  21. package/src/components/Knob/SVGRoot.tsx +10 -17
  22. package/src/components/Knob/Thumb.tsx +33 -11
  23. package/src/components/Knob/context.tsx +73 -89
  24. package/src/components/Knob/index.tsx +227 -105
  25. package/src/components/NumberInput/DecrementStepper.tsx +21 -50
  26. package/src/components/NumberInput/IncrementStepper.tsx +22 -50
  27. package/src/components/NumberInput/InputField.tsx +206 -0
  28. package/src/components/NumberInput/Stepper.tsx +150 -21
  29. package/src/components/NumberInput/StepperButton.tsx +95 -0
  30. package/src/components/NumberInput/context.tsx +104 -149
  31. package/src/components/NumberInput/index.tsx +461 -110
  32. package/src/components/Piano/index.tsx +413 -263
  33. package/src/components/Piano/keyboard-shortcuts.ts +61 -0
  34. package/src/components/PointsEditor/Background.tsx +19 -4
  35. package/src/components/PointsEditor/Container.tsx +92 -22
  36. package/src/components/PointsEditor/Point.tsx +293 -53
  37. package/src/components/PointsEditor/SelectionBox.tsx +54 -0
  38. package/src/components/PointsEditor/context.tsx +97 -75
  39. package/src/components/PointsEditor/index.tsx +497 -62
  40. package/src/components/Slider/Marks.tsx +84 -0
  41. package/src/components/Slider/MarksOption.tsx +115 -0
  42. package/src/components/Slider/Thumb.tsx +99 -54
  43. package/src/components/Slider/Track.tsx +73 -62
  44. package/src/components/Slider/context.tsx +40 -53
  45. package/src/components/Slider/index.tsx +237 -190
  46. package/src/components/Slider/type.ts +27 -9
  47. package/src/components/XYPad/Area.tsx +52 -33
  48. package/src/components/XYPad/Thumb.tsx +121 -60
  49. package/src/components/XYPad/context.tsx +49 -0
  50. package/src/components/XYPad/index.tsx +338 -246
  51. package/src/components/_util/Placement.tsx +68 -0
  52. package/src/components/_util/VisuallyHiddenRangeInput.tsx +24 -0
  53. package/src/components/_util/css-length.ts +13 -0
  54. package/src/{components/_util/composeRefs.tsx → compose-refs/index.tsx} +33 -5
  55. package/src/hooks/{useCallbackRef.ts → _internal/useCallbackRef.ts} +3 -1
  56. package/src/hooks/_internal/useCheckSteps.ts +170 -0
  57. package/src/hooks/useAnimationFrame.ts +17 -7
  58. package/src/hooks/useDrag.ts +39 -9
  59. package/src/hooks/useDragValue.ts +194 -0
  60. package/src/hooks/useEventListener.ts +11 -7
  61. package/src/hooks/useLongPress.ts +42 -8
  62. package/src/hooks/useMIDIAccess.ts +82 -11
  63. package/src/hooks/useMIDIInput.ts +45 -13
  64. package/src/hooks/useMIDIMessage.ts +23 -5
  65. package/src/hooks/useWheel.ts +29 -9
  66. package/src/index.ts +50 -27
  67. package/src/input-event.ts +32 -0
  68. package/dist/index.css +0 -536
  69. package/src/Globals.d.ts +0 -3
  70. package/src/components/AnimationCanvas/canvas.ts +0 -46
  71. package/src/components/Knob/index.css +0 -77
  72. package/src/components/NumberInput/InternalInput.tsx +0 -216
  73. package/src/components/NumberInput/index.css +0 -185
  74. package/src/components/NumberInput/type.ts +0 -58
  75. package/src/components/Piano/KeyLabel.tsx +0 -51
  76. package/src/components/Piano/context.tsx +0 -73
  77. package/src/components/Piano/index.css +0 -90
  78. package/src/components/Piano/key.tsx +0 -205
  79. package/src/components/Piano/keyboardShortcuts.ts +0 -31
  80. package/src/components/PointsEditor/index.css +0 -30
  81. package/src/components/Slider/Scale.tsx +0 -52
  82. package/src/components/Slider/ScaleOption.tsx +0 -103
  83. package/src/components/Slider/index.css +0 -112
  84. package/src/components/XYPad/index.css +0 -48
  85. package/src/components/_util/index.ts +0 -28
  86. package/src/components/_util/type.ts +0 -1
  87. package/src/hooks/useDragWithElement.ts +0 -100
  88. package/src/hooks/usePianoDrag.ts +0 -75
  89. package/src/hooks/useRefCallbackEvent.ts +0 -34
  90. package/src/styles/global.css +0 -19
package/dist/index.d.cts CHANGED
@@ -1,159 +1,323 @@
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";
4
- import * as _$_tremolo_ui_dom0 from "@tremolo-ui/dom";
5
- import { MIDIAccessError, NOT_SUPPORTED, PERMISSION_DENIED } from "@tremolo-ui/dom";
6
-
1
+ import React, { AriaAttributes, CSSProperties, ComponentPropsWithoutRef, DependencyList, ReactNode, Ref, RefObject, SVGProps } from "react";
2
+ import { AnimationFrame, AxisOptions, DragState, MIDIAccessError, MIDIAccessOptions, MIDIAccessOptions as MIDIAccessOptions$1, MIDIInputHandlers, NOT_SUPPORTED, PERMISSION_DENIED, PITCH_BEND_CENTER, SelectionBoxRect, UNAVAILABLE, WheelOptions, XY, XY as XY$1, XYInput, XYInput as XYInput$1 } from "@tremolo-ui/dom";
3
+ import { InputEventOption, ModifierState, ModifierValue, NoteRange, Scale, ValueRange } from "@tremolo-ui/functions";
7
4
  //#region src/components/AnimationCanvas/index.d.ts
8
5
  type InitFunction = (context: CanvasRenderingContext2D, option: {
9
- /** current canvas width */width: number; /** current canvas height */
6
+ /** current canvas width */
7
+ width: number;
8
+ /** current canvas height */
10
9
  height: number;
11
10
  }) => 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;
20
- interface CommonProps {
11
+ type DrawFunction = (context: CanvasRenderingContext2D, option: AnimationFrame) => void;
12
+ interface AnimationCanvasCommonProps {
13
+ /**
14
+ * Draw one frame. It is given the 2D context and the frame: the size in CSS
15
+ * pixels, `count`, `deltaTime`, `elapsedTime` and `fps`.
16
+ *
17
+ * A new function replaces the old one in place, so writing it inline does
18
+ * not restart anything.
19
+ */
21
20
  draw: DrawFunction;
21
+ /**
22
+ * Called once before the first frame, with the context and the size in CSS
23
+ * pixels. Set up what every frame shares here.
24
+ */
22
25
  init?: InitFunction;
26
+ /**
27
+ * Redraw on every animation frame. Turn it off to draw only when there is a
28
+ * reason to: when the canvas mounts, when it is resized, and when the
29
+ * component re-renders.
30
+ *
31
+ * @default true
32
+ */
23
33
  animate?: boolean;
24
34
  /**
35
+ * Read once, when the 2D context is created, so changing it later has no
36
+ * effect. Passing a fresh object on every render is therefore harmless.
37
+ *
25
38
  * @see https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement/getContext#contextattributes
26
39
  */
27
40
  options?: CanvasRenderingContext2DSettings;
41
+ /**
42
+ * Carry the drawing across a resize, so that the canvas does not blank for a
43
+ * frame while the new size is drawn. A fixed canvas is resized too, when its
44
+ * `width` or `height` changes.
45
+ *
46
+ * @default true
47
+ */
48
+ reduceFlickering?: boolean;
28
49
  }
29
- interface AbsoluteSizingProps {
50
+ /** A canvas of the size given in CSS pixels. This is the default. */
51
+ interface AnimationCanvasFixedProps {
52
+ /** Leave it out, or pass `false`, for a canvas of the size below. */
53
+ resizable?: false;
54
+ /**
55
+ * Width of the canvas in CSS pixels.
56
+ * @default 100
57
+ */
30
58
  width?: number;
59
+ /**
60
+ * Height of the canvas in CSS pixels.
61
+ * @default 100
62
+ */
31
63
  height?: number;
32
64
  }
33
- interface RelativeSizingProps {
34
- relativeSize?: boolean;
35
- reduceFlickering?: boolean;
65
+ /**
66
+ * A canvas that follows the size of its parent element, so the parent needs a
67
+ * size of its own.
68
+ */
69
+ interface AnimationCanvasResizableProps {
70
+ /**
71
+ * Follow the size of the parent element instead of `width` and `height`.
72
+ * Switching it rebuilds the canvas.
73
+ */
74
+ resizable: true;
75
+ /** Not accepted: the size comes from the parent element. */
76
+ width?: never;
77
+ /** Not accepted: the size comes from the parent element. */
78
+ height?: never;
36
79
  }
37
- type AnimationCanvasProps = CommonProps & AbsoluteSizingProps & RelativeSizingProps;
80
+ type AnimationCanvasProps = AnimationCanvasCommonProps & (AnimationCanvasFixedProps | AnimationCanvasResizableProps);
81
+ type Props = AnimationCanvasProps & Omit<ComponentPropsWithoutRef<'canvas'>, keyof AnimationCanvasCommonProps | 'resizable' | 'width' | 'height'>;
38
82
  /**
39
83
  * A simple animatable canvas with requestAnimationFrame()
40
84
  */
41
- declare function AnimationCanvas(props: CommonProps & AbsoluteSizingProps & Omit<ComponentPropsWithoutRef<'canvas'>, keyof AnimationCanvasProps>): ReactElement;
42
- declare function AnimationCanvas(pros: CommonProps & RelativeSizingProps & Omit<ComponentPropsWithoutRef<'canvas'>, keyof AnimationCanvasProps>): ReactElement;
43
- //#endregion
44
- //#region src/components/_util/index.d.ts
45
- declare const cursorStyles: {
46
- grabbing: string;
47
- grab: string;
48
- pointer: string;
49
- move: string;
50
- none: string;
51
- };
52
- type Cursor = keyof typeof cursorStyles;
85
+ declare const AnimationCanvas: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<HTMLCanvasElement>>;
53
86
  //#endregion
54
87
  //#region src/components/Knob/ActiveLine.d.ts
55
- declare function ActiveLine({
56
- stroke,
57
- strokeWidth,
58
- className,
59
- ...props
60
- }: Omit<SVGProps<SVGPathElement>, 'd'>): _$react.JSX.Element;
88
+ declare function ActiveLine({ stroke, strokeWidth, className, ...props }: Omit<SVGProps<SVGPathElement>, 'd'>): import("react").JSX.Element;
61
89
  //#endregion
62
90
  //#region src/components/Knob/InactiveLine.d.ts
63
- declare function InactiveLine({
64
- stroke,
65
- strokeWidth,
66
- className,
67
- ...props
68
- }: Omit<SVGProps<SVGPathElement>, 'd'>): _$react.JSX.Element;
69
- //#endregion
70
- //#region src/components/_util/type.d.ts
71
- type Override<T, U> = T & Omit<U, keyof T>;
91
+ declare function InactiveLine({ stroke, strokeWidth, className, ...props }: Omit<SVGProps<SVGPathElement>, 'd'>): import("react").JSX.Element;
72
92
  //#endregion
73
93
  //#region src/components/Knob/SVGRoot.d.ts
74
- interface SVGRoot {
75
- block?: boolean;
76
- overflowVisible?: boolean;
94
+ interface KnobSVGRootProps {
95
+ /**
96
+ * `<Knob.InactiveLine />`, `<Knob.ActiveLine />` and `<Knob.Thumb />` go
97
+ * here, in the order you want them painted.
98
+ */
99
+ children: ReactNode;
77
100
  }
78
- declare function SVGRoot({
79
- children,
80
- style,
81
- ...props
82
- }: Override<SVGRoot, SVGProps<SVGSVGElement>>): _$react.JSX.Element;
101
+ declare function SVGRoot({ children, style, ...props }: KnobSVGRootProps & Omit<SVGProps<SVGSVGElement>, keyof KnobSVGRootProps>): import("react").JSX.Element;
83
102
  //#endregion
84
103
  //#region src/components/Knob/Thumb.d.ts
85
- interface Props {
86
- /** color */
104
+ interface KnobThumbProps {
105
+ /**
106
+ * Fill colour of the circle.
107
+ * @default 'currentColor'
108
+ */
87
109
  thumb?: string;
88
- /** color */
110
+ /**
111
+ * Colour of the line that points at the value.
112
+ * @default 'currentColor'
113
+ */
89
114
  thumbLine?: string;
90
- /** percent (0-100) */
115
+ /**
116
+ * Diameter of the circle, as a percentage of the knob.
117
+ * @default 84
118
+ */
91
119
  thumbSize?: number;
92
- /** percent (0-100) */
120
+ /**
121
+ * Thickness of the line, as a percentage of the knob.
122
+ * @default 6
123
+ */
93
124
  thumbLineWeight?: number;
94
- /** percent (0-100) */
125
+ /**
126
+ * How far down the line reaches, as a percentage of the knob from its top.
127
+ * The line starts at the edge of the circle.
128
+ * @default 35
129
+ */
95
130
  thumbLineLength?: number;
131
+ /**
132
+ * Classes for what the thumb draws inside itself: `thumbLine` is the line
133
+ * that points at the value.
134
+ */
96
135
  classes?: {
97
- thumb?: string;
98
136
  thumbLine?: string;
99
137
  };
100
138
  }
101
- declare function Thumb({
102
- className,
103
- thumb,
104
- thumbLine,
105
- thumbSize,
106
- thumbLineWeight,
107
- thumbLineLength,
108
- classes,
109
- ...props
110
- }: Props & Omit<SVGProps<SVGSVGElement>, 'd' | keyof Props>): _$react.JSX.Element;
139
+ declare function Thumb({ className, thumb, thumbLine, thumbSize, thumbLineWeight, thumbLineLength, classes, ...props }: KnobThumbProps & Omit<SVGProps<SVGSVGElement>, 'd' | keyof KnobThumbProps>): import("react").JSX.Element;
140
+ //#endregion
141
+ //#region src/components/Knob/context.d.ts
142
+ type KnobConfig = {
143
+ value: number;
144
+ min: number;
145
+ max: number;
146
+ step: number;
147
+ scale: Scale;
148
+ startValue: number;
149
+ /** angle range [degree] */
150
+ angleRange: number;
151
+ };
152
+ type KnobContextValue = KnobConfig & {
153
+ /** normalized value */
154
+ p: number;
155
+ /** ロータリー開始位置 */
156
+ r1: number;
157
+ /** activeLine の開始位置 */
158
+ r2: number;
159
+ /** activeLine の終了位置 */
160
+ r3: number;
161
+ /** ロータリー終了位置 */
162
+ r4: number;
163
+ };
164
+ /**
165
+ * Everything here is derived during render, so there is no state to keep in
166
+ * sync: `value` comes from the props of `Root` and the rest follows from it.
167
+ */
168
+ declare function useKnobContext(): KnobContextValue;
169
+ declare function useKnobContext<T>(selector: (state: KnobContextValue) => T): T;
111
170
  //#endregion
112
171
  //#region src/components/Knob/index.d.ts
113
172
  interface KnobProps {
173
+ /** The current value. The knob shows only this, so update it from `onChange`. */
114
174
  value: number;
175
+ /** The value with the knob turned all the way down. */
115
176
  min: number;
177
+ /** The value with the knob turned all the way up. */
116
178
  max: number;
179
+ /**
180
+ * Granularity of the value. A drag, the wheel and the arrow keys snap it to
181
+ * multiples of `step`.
182
+ *
183
+ * @default 1
184
+ */
117
185
  step?: number;
118
- skew?: number;
119
186
  /**
120
- * value set when double-clicking
121
- * restriction: enableDoubleClickDefault = true
187
+ * How the value is distributed across the travel.
188
+ *
189
+ * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,
190
+ * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or
191
+ * `skewScale(n)` for a value that has to match a JUCE parameter.
192
+ *
193
+ * @default linearScale
194
+ */
195
+ scale?: Scale;
196
+ /**
197
+ * The value a double click restores, while `enableDoubleClickDefault` is on.
122
198
  * @default min
123
- * @see enableDoubleClickDefault
124
199
  */
125
200
  defaultValue?: number;
126
201
  /**
127
- * Value to be used as the starting point of the line when drawing.
202
+ * Where the active arc starts. Put it at the centre of a bipolar control,
203
+ * such as a pan knob, so that the arc grows from there either way.
128
204
  * @default min
129
205
  */
130
206
  startValue?: number;
131
- /** width and height */
207
+ /**
208
+ * Width and height of the knob. Sets `--knob-size`; the size the theme
209
+ * gives it stands when this is omitted.
210
+ */
132
211
  size?: number | string;
133
212
  /**
134
- * Global style to apply when dragged
135
- * @default defaultExternalStyles
213
+ * The cursor to show while dragging. It is set on the dragged element, so it
214
+ * stays while the pointer is outside the knob.
215
+ *
216
+ * @default { cursor: 'grabbing' }
136
217
  */
137
218
  externalStyles?: {
138
- userSelectNone?: boolean;
139
- cursor?: Cursor;
219
+ cursor?: CSSProperties['cursor'];
140
220
  };
141
221
  /**
142
- * wheel control option
143
- * If null, no event will be triggered
222
+ * How much one notch of the wheel moves the value. It only acts while the
223
+ * focus is inside, so that scrolling the page past the knob leaves it alone.
224
+ *
225
+ * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the
226
+ * range between `min` and `max`. The result is snapped to `step`, except for
227
+ * an amount set on a modifier key (`{ default: …, shift: … }`). `null` turns
228
+ * the wheel off.
229
+ *
230
+ * @default ['raw', 1]
231
+ */
232
+ wheel?: ModifierValue<InputEventOption> | null;
233
+ /**
234
+ * How much a drag moves the value, per modifier key.
235
+ *
236
+ * `1` is the normal travel of 100px for the whole range; `0.1` makes the
237
+ * same movement cover a tenth of it. Shift is bound to `0.1` by default, to
238
+ * match what it does on the arrow keys.
239
+ *
240
+ * Pressing or releasing the key mid-drag does not disturb the value: the
241
+ * travel so far is kept and the new sensitivity applies from there. It takes
242
+ * effect on the next movement, since a key on its own produces no pointer
243
+ * event.
244
+ *
245
+ * @default { default: 1, shift: 0.1 }
246
+ */
247
+ dragSensitivity?: ModifierValue<number>;
248
+ /**
249
+ * Hide the cursor while dragging and read the pointer movement directly,
250
+ * rather than letting it wander off across the screen.
251
+ *
252
+ * A knob does not care where the pointer is, only how far it moved, and
253
+ * letting it wander costs twice: the cursor ends up far from the knob it is
254
+ * holding, and **the drag stops at the edge of the screen**, where the
255
+ * operating system pins the pointer and the coordinates stop changing. A
256
+ * `dragSensitivity` below 1 reaches that edge quickly.
257
+ *
258
+ * Off by default because it is not free: the browser shows its own notice,
259
+ * Esc takes the lock back, and the request needs a user gesture and can be
260
+ * refused. A refused request is not an error — the drag simply carries on as
261
+ * an ordinary one.
262
+ *
263
+ * @default false
144
264
  */
145
- wheel?: InputEventOption | null;
265
+ pointerLock?: boolean;
146
266
  /**
147
- * keyboard control option
148
- * If null, no event will be triggered
267
+ * How much one arrow key press moves the value.
268
+ *
269
+ * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the
270
+ * range between `min` and `max`. The result is snapped to `step`, except for
271
+ * an amount set on a modifier key, which is what lets shift move off the
272
+ * grid. `null` turns the arrow keys off.
273
+ *
274
+ * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise
275
+ * the amount to match: 1 would round straight back to where it started, and
276
+ * a development build warns about it.
277
+ *
278
+ * @default { default: ['raw', 1], shift: ['raw', 0.1] }
279
+ */
280
+ keyboard?: ModifierValue<InputEventOption> | null;
281
+ /**
282
+ * Restore `defaultValue` on a double click.
283
+ * @default true
149
284
  */
150
- keyboard?: InputEventOption | null;
151
285
  enableDoubleClickDefault?: boolean;
286
+ /**
287
+ * Make the knob unchangeable and remove it from the tab order.
288
+ * The parts carry `data-disabled` while it is set.
289
+ */
152
290
  disabled?: boolean;
291
+ /**
292
+ * Make the knob unchangeable while leaving it focusable.
293
+ * The parts carry `data-readonly` while it is set.
294
+ */
153
295
  readonly?: boolean;
154
- /** angle range [degree] */
296
+ /**
297
+ * How far the knob turns from `min` to `max`, in degrees, centred on the
298
+ * top.
299
+ * @default 270
300
+ */
155
301
  angleRange?: number;
302
+ /**
303
+ * Called with the new value when a drag, the wheel, an arrow key or a double
304
+ * click moves it.
305
+ */
156
306
  onChange?: (value: number) => void;
307
+ /**
308
+ * The knob renders exactly what you compose here; there is no default
309
+ * markup to fall back to.
310
+ *
311
+ * @example
312
+ * <Knob.Root value={value} min={0} max={100} onChange={setValue}>
313
+ * <Knob.SVGRoot>
314
+ * <Knob.InactiveLine />
315
+ * <Knob.ActiveLine />
316
+ * <Knob.Thumb />
317
+ * </Knob.SVGRoot>
318
+ * </Knob.Root>
319
+ */
320
+ children: ReactNode;
157
321
  }
158
322
  interface KnobMethods {
159
323
  focus: () => void;
@@ -163,109 +327,309 @@ interface KnobMethods {
163
327
  * Interactive rotary knob component implemented in SVG.
164
328
  */
165
329
  declare const Knob: {
166
- Root: _$react.ForwardRefExoticComponent<KnobProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof KnobProps> & _$react.RefAttributes<KnobMethods>>;
330
+ Root: import("react").ForwardRefExoticComponent<KnobProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof KnobProps> & import("react").RefAttributes<KnobMethods>>;
167
331
  SVGRoot: typeof SVGRoot;
168
332
  InactiveLine: typeof InactiveLine;
169
333
  ActiveLine: typeof ActiveLine;
170
334
  Thumb: typeof Thumb;
171
335
  };
172
336
  //#endregion
173
- //#region src/components/NumberInput/DecrementStepper.d.ts
174
- interface DecrementStepperProps {
175
- size?: number;
176
- children?: ReactNode;
337
+ //#region src/components/NumberInput/InputField.d.ts
338
+ interface NumberInputInputFieldProps {
339
+ className?: string;
340
+ style?: CSSProperties;
177
341
  }
178
- declare function DecrementStepper({
179
- size,
180
- children,
181
- className,
182
- ...props
183
- }: DecrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof DecrementStepperProps>): _$react.JSX.Element;
184
342
  //#endregion
185
- //#region src/components/NumberInput/IncrementStepper.d.ts
186
- interface IncrementStepperProps {
187
- size?: number;
343
+ //#region src/components/NumberInput/Stepper.d.ts
344
+ interface NumberInputStepperProps {
345
+ className?: string;
346
+ style?: CSSProperties;
347
+ /** `<NumberInput.IncrementStepper />` and `<NumberInput.DecrementStepper />` go here. */
188
348
  children?: ReactNode;
189
349
  }
190
- declare function IncrementStepper({
191
- size,
192
- children,
193
- className,
194
- ...props
195
- }: IncrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof IncrementStepperProps>): _$react.JSX.Element;
196
350
  //#endregion
197
- //#region src/components/NumberInput/Stepper.d.ts
198
- interface StepperProps {
199
- /** Display only when hovering. */
200
- dynamic?: boolean;
351
+ //#region src/components/NumberInput/StepperButton.d.ts
352
+ interface StepperButtonProps {
353
+ className?: string;
354
+ style?: CSSProperties;
355
+ /** Replaces the default arrow. Size it with the `--stepper-icon-size` variable. */
201
356
  children?: ReactNode;
202
357
  }
203
- declare function Stepper({
204
- dynamic,
205
- children,
206
- className,
207
- ...props
208
- }: StepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof StepperProps>): _$react.JSX.Element;
209
358
  //#endregion
210
- //#region src/components/NumberInput/type.d.ts
359
+ //#region src/components/NumberInput/DecrementStepper.d.ts
360
+ interface NumberInputDecrementStepperProps extends StepperButtonProps {}
361
+ /** Lowers the value by one `step`, repeating while held. */
362
+ declare function DecrementStepper(props: NumberInputDecrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof NumberInputDecrementStepperProps>): import("react").JSX.Element;
363
+ //#endregion
364
+ //#region src/components/NumberInput/IncrementStepper.d.ts
365
+ interface NumberInputIncrementStepperProps extends StepperButtonProps {}
366
+ /** Raises the value by one `step`, repeating while held. */
367
+ declare function IncrementStepper(props: NumberInputIncrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof NumberInputIncrementStepperProps>): import("react").JSX.Element;
368
+ //#endregion
369
+ //#region src/components/NumberInput/context.d.ts
370
+ type NumberInputContextValue = {
371
+ value: number;
372
+ /**
373
+ * The range the caller asked for. Left undefined when unbounded, which is
374
+ * what `aria-valuemin` / `aria-valuemax` and the steppers need to tell apart
375
+ * from a range that happens to sit at the edge of the safe integers.
376
+ */
377
+ min?: number;
378
+ max?: number;
379
+ step: number;
380
+ /** How the value is distributed across the travel. */
381
+ scale: Scale;
382
+ disabled: boolean;
383
+ readonly: boolean;
384
+ clampValue: boolean;
385
+ /**
386
+ * The effective scaling for `applyDelta`, with unbounded ends filled in.
387
+ * Ignores `min` / `max` when `clampValue` is off.
388
+ */
389
+ range: ValueRange;
390
+ /** The effective range for raw input, whose open ends cover finite numbers. */
391
+ rawRange: ValueRange;
392
+ keyboard: ModifierValue<InputEventOption> | null;
393
+ /** Pixels of vertical drag on `Stepper` that move the value by one `step`. */
394
+ drag: number | null;
395
+ /** How much a `Stepper` drag counts, per modifier key. */
396
+ dragSensitivity: ModifierValue<number>;
397
+ /** Whether a `Stepper` drag hides the pointer and reads its movement. */
398
+ pointerLock: boolean;
399
+ /** What `InputField` selects when it takes focus. */
400
+ selectOnFocus: 'all' | 'number' | 'none';
401
+ /** Whether `InputField` shows the plain value while it has focus. */
402
+ unformatOnFocus: boolean;
403
+ /** Whether `InputField` puts the caret back after an arrow key steps the value. */
404
+ keepCaretOnStep: boolean;
405
+ /** Whether Enter leaves `InputField` as well as committing. */
406
+ blurOnEnter: boolean;
407
+ /** What the input shows: the draft while editing, the formatted value otherwise. */
408
+ text: string;
409
+ editing: boolean;
410
+ /** Whether the value sits outside `min` / `max`. Not judged while editing. */
411
+ outOfRange: boolean;
412
+ /** Whether a stepper would have no effect, for `aria-disabled`. */
413
+ atMin: boolean;
414
+ atMax: boolean;
415
+ format: (value: number) => string;
416
+ parse: (text: string) => number;
417
+ /** Typing. Replaces the draft and reports the parsed value, unclamped. */
418
+ setDraft: (text: string) => void;
419
+ /** Blur or Enter. Optionally clamps the draft, reports it, and drops it. */
420
+ commitDraft: () => void;
421
+ /** Drops the draft and replaces the value: steppers, wheel, keyboard, drag. */
422
+ changeValue: (next: number) => void;
423
+ /** Moves the value by one `option` in `direction`, normally +1 or -1. */
424
+ nudge: (direction: number, option: ModifierValue<InputEventOption>, modifiers?: ModifierState) => void;
425
+ /** `InputField` registers itself here; `Root` focuses it through its methods. */
426
+ inputRef: RefObject<HTMLInputElement | null>;
427
+ };
211
428
  /**
212
- * [unit, scale][]
213
- * @example
214
- * [['Hz', 1], ['kHz', 1000]]
429
+ * The only state behind this is the editing draft; `value` comes from the props
430
+ * of `Root` and everything else is derived during render.
215
431
  */
216
- type Units = [string, number][];
432
+ declare function useNumberInputContext(): NumberInputContextValue;
433
+ declare function useNumberInputContext<T>(selector: (state: NumberInputContextValue) => T): T;
217
434
  //#endregion
218
435
  //#region src/components/NumberInput/index.d.ts
219
436
  interface NumberInputProps {
220
- value: number | string;
221
437
  /**
222
- * Number.MIN_SAFE_INTEGER
438
+ * The value. What the input shows is `format(value)`, except while the user
439
+ * is typing, when their own text stands until it is committed.
440
+ */
441
+ value: number;
442
+ /**
443
+ * Lowest value. Leave it out for no lower bound.
444
+ *
445
+ * It is enforced when a value is committed or stepped, while `clampValue` is
446
+ * on. A value below it carries `data-out-of-range` on `InputField`.
223
447
  */
224
448
  min?: number;
225
449
  /**
226
- * Number.MAX_SAFE_INTEGER
450
+ * Highest value. Leave it out for no upper bound.
451
+ *
452
+ * It is enforced when a value is committed or stepped, while `clampValue` is
453
+ * on. A value above it carries `data-out-of-range` on `InputField`.
227
454
  */
228
455
  max?: number;
456
+ /**
457
+ * Granularity of the value. The steppers, a drag on `Stepper`, the wheel and
458
+ * the arrow keys snap it to multiples of `step`; a typed value is left as it
459
+ * is.
460
+ *
461
+ * @default 1
462
+ */
229
463
  step?: number;
230
464
  /**
231
- * @example
232
- * units='Hz'
233
- * units={[['Hz', 1], ['kHz', 1000]]}
234
- * units={[['ms', 1], ['s', 1000]]}
465
+ * How the value is distributed across the travel of a drag or a
466
+ * `'normalized'` wheel / keyboard nudge.
467
+ *
468
+ * @default linearScale
235
469
  */
236
- units?: string | Units;
470
+ scale?: Scale;
237
471
  /**
238
- * Digits for rounding numbers.
472
+ * Render the value as text. Plain digits by default.
473
+ *
474
+ * `unitFormat` from `@tremolo-ui/functions` builds this and `parse` together
475
+ * for a unit, as a pair to spread into the root.
476
+ *
239
477
  * @example
240
- * with:
241
- * units={[['Hz', 1], ['kHz', 1000]]}
242
- * digit={3}
243
- * results:
244
- * value=100 -> 100Hz, value=1600 -> 1.60Hz
478
+ * <NumberInput.Root {...unitFormat('Hz', { digits: 2 })} value={v} />
479
+ */
480
+ format?: (value: number) => string;
481
+ /**
482
+ * Read a value back out of the text. Has to undo `format`.
483
+ *
484
+ * Text with no number in it reads as `NaN`, which leaves the value alone.
485
+ */
486
+ parse?: (text: string) => number;
487
+ /**
488
+ * Keep the value within `min` and `max` when it is committed or stepped.
489
+ * Typing is never clamped, so that a value can be entered digit by digit.
490
+ * @default true
491
+ */
492
+ clampValue?: boolean;
493
+ /**
494
+ * How much one notch of the wheel moves the value. It only acts while the
495
+ * focus is inside, so that scrolling the page past the input leaves it
496
+ * alone.
497
+ *
498
+ * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the
499
+ * range between `min` and `max`. The result is snapped to `step`, except for
500
+ * an amount set on a modifier key (`{ default: …, shift: … }`). `null` turns
501
+ * the wheel off.
502
+ *
503
+ * @default ['raw', 1]
504
+ */
505
+ wheel?: ModifierValue<InputEventOption> | null;
506
+ /**
507
+ * How much one arrow key press moves the value.
508
+ *
509
+ * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the
510
+ * range between `min` and `max`. The result is snapped to `step`, except for
511
+ * an amount set on a modifier key, which is what lets shift move off the
512
+ * grid. `null` turns the arrow keys off.
513
+ *
514
+ * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise
515
+ * the amount to match: 1 would round straight back to where it started, and
516
+ * a development build warns about it.
517
+ *
518
+ * @default { default: ['raw', 1], shift: ['raw', 0.1] }
519
+ */
520
+ keyboard?: ModifierValue<InputEventOption> | null;
521
+ /**
522
+ * Pixels of vertical drag on `Stepper` that move the value by one `step`.
523
+ * `null` turns the drag off.
524
+ * @default 1
525
+ */
526
+ drag?: number | null;
527
+ /**
528
+ * How much a `Stepper` drag counts, per modifier key.
529
+ *
530
+ * `1` is `drag` pixels per `step`; `0.1` makes the same movement cover a
531
+ * tenth of that. Shift is bound to `0.1` by default, to match what it does
532
+ * on the arrow keys.
533
+ *
534
+ * A modifier entry is not snapped to `step`, which is what lets a finer
535
+ * amount move at all.
536
+ *
537
+ * @default { default: 1, shift: 0.1 }
538
+ */
539
+ dragSensitivity?: ModifierValue<number>;
540
+ /**
541
+ * Hide the cursor while dragging a `Stepper` and read the pointer movement
542
+ * directly, rather than letting it wander off across the screen.
543
+ *
544
+ * The drag is already relative, so the pointer position carries nothing —
545
+ * but it still runs into the edge of the screen, where the operating system
546
+ * pins it and the coordinates stop changing.
547
+ *
548
+ * Off by default: the browser shows its own notice, Esc takes the lock back,
549
+ * and the request can be refused. A refused request is not an error, and the
550
+ * drag carries on as an ordinary one.
551
+ *
552
+ * @default false
553
+ */
554
+ pointerLock?: boolean;
555
+ /**
556
+ * Select the text when `InputField` takes focus: `'all'` selects all of it,
557
+ * `'number'` only the leading number, leaving whatever the format appended,
558
+ * and `'none'` leaves the caret where the click put it.
559
+ * @default 'none'
560
+ */
561
+ selectOnFocus?: 'all' | 'number' | 'none';
562
+ /**
563
+ * Show the plain value while `InputField` has focus, dropping whatever
564
+ * `format` put around it: an input reading `1.23kHz` shows `1230` to be
565
+ * typed over.
566
+ *
567
+ * The number shown is the value itself, not the number inside the formatted
568
+ * text. Those differ whenever the format scales — `1.23` out of `1.23kHz`
569
+ * would read back as 1.23 and lose a factor of a thousand — and it is also
570
+ * why a rounded display no longer becomes the value: `1.6` shown as `2Hz`
571
+ * offers `1.6` for editing, not `2`.
572
+ *
573
+ * @default false
574
+ */
575
+ unformatOnFocus?: boolean;
576
+ /**
577
+ * Put the caret in `InputField` back where it was after an arrow key steps
578
+ * the value.
579
+ *
580
+ * A controlled input whose `value` is replaced drops the caret at the end,
581
+ * so without this the second press of a repeated step always acts on the
582
+ * last digit. With it, the digit under the caret stays under the caret and
583
+ * a column can be held while stepping.
584
+ *
585
+ * The position is measured from the decimal point rather than from either
586
+ * end, so it survives the number growing or shrinking: the caret between
587
+ * `9` and `.9` is still between `10` and `.0`.
588
+ *
589
+ * It only restores the caret. Which digit it sits on does not change the
590
+ * size of the step — that is `keyboard`'s to say.
591
+ *
592
+ * @default false
593
+ */
594
+ keepCaretOnStep?: boolean;
595
+ /**
596
+ * Commit and leave `InputField` when Enter is pressed. Enter commits either
597
+ * way.
598
+ * @default true
245
599
  */
246
- digit?: number;
247
- readonly?: boolean;
248
- variant?: 'outline' | 'filled' | 'flushed' | 'unstyled';
249
- selectWithFocus?: 'all' | 'number' | 'none';
250
600
  blurOnEnter?: boolean;
251
- keepWithinRange?: boolean;
252
- clampValueOnBlur?: boolean;
253
601
  /**
254
- * wheel control option
255
- * If null, no event will be triggered
602
+ * Make the input unchangeable and remove it from the tab order.
603
+ * The parts carry `data-disabled` while it is set.
256
604
  */
257
- wheel?: InputEventOption | null;
605
+ disabled?: boolean;
258
606
  /**
259
- * keyboard control option
260
- * If null, no event will be triggered
607
+ * Make the value unchangeable.
608
+ * The parts carry `data-readonly` while it is set.
261
609
  */
262
- 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;
610
+ readonly?: boolean;
611
+ className?: string;
612
+ style?: CSSProperties;
613
+ /**
614
+ * Called with the new value. While the user types, it is called for every
615
+ * entry that reads as a number, unclamped; committing the entry calls it
616
+ * again if clamping changes the value.
617
+ */
618
+ onChange?: (value: number) => void;
619
+ /**
620
+ * The input renders exactly what you compose here; there is no default
621
+ * markup to fall back to.
622
+ *
623
+ * @example
624
+ * <NumberInput.Root value={value} min={0} max={100} onChange={setValue}>
625
+ * <NumberInput.InputField />
626
+ * <NumberInput.Stepper>
627
+ * <NumberInput.IncrementStepper />
628
+ * <NumberInput.DecrementStepper />
629
+ * </NumberInput.Stepper>
630
+ * </NumberInput.Root>
631
+ */
632
+ children: ReactNode;
269
633
  }
270
634
  interface NumberInputMethods {
271
635
  focus: () => void;
@@ -275,99 +639,172 @@ interface NumberInputMethods {
275
639
  * Input with some useful functions for entering numerical values.
276
640
  */
277
641
  declare const NumberInput: {
278
- Root: _$react.ForwardRefExoticComponent<NumberInputProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "type" | keyof NumberInputProps> & _$react.RefAttributes<NumberInputMethods>>;
279
- Stepper: typeof Stepper;
642
+ Root: import("react").ForwardRefExoticComponent<NumberInputProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof NumberInputProps> & import("react").RefAttributes<NumberInputMethods>>;
643
+ InputField: import("react").ForwardRefExoticComponent<NumberInputInputFieldProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "defaultValue" | "value" | "type" | keyof NumberInputInputFieldProps> & import("react").RefAttributes<HTMLInputElement>>;
644
+ Stepper: import("react").ForwardRefExoticComponent<NumberInputStepperProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof NumberInputStepperProps> & import("react").RefAttributes<HTMLDivElement>>;
280
645
  IncrementStepper: typeof IncrementStepper;
281
646
  DecrementStepper: typeof DecrementStepper;
282
647
  };
283
648
  //#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;
649
+ //#region src/components/Piano/keyboard-shortcuts.d.ts
650
+ type KeyboardShortcuts = {
294
651
  /**
295
- * \<KeyLabel />
652
+ * Keys laid out from `noteRange.first`, one entry per semitone.
653
+ *
654
+ * An empty string leaves that note without a shortcut: `KeyboardEvent.key` is
655
+ * never empty, so the entry can never match. Use it to skip the black keys
656
+ * (see {@link SHORTCUTS.HOME_ROW_NATURAL}) and keep the remaining entries
657
+ * lined up with the notes.
296
658
  */
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
- //#region src/components/Piano/keyboardShortcuts.d.ts
314
- type KeyboardShortcuts = {
315
659
  keys: string[];
316
- flags?: {
317
- naturalOnly?: boolean;
318
- };
319
660
  };
661
+ /**
662
+ * Ready-made keyboard layouts. Both assume `noteRange.first` is a C.
663
+ */
320
664
  declare const SHORTCUTS: {
665
+ /** Every semitone from C, over the two rows of a QWERTY keyboard. */
321
666
  HOME_ROW: {
322
667
  keys: string[];
323
668
  };
669
+ /** The white keys only, on the home row. Black keys have no shortcut. */
670
+ HOME_ROW_NATURAL: {
671
+ keys: string[];
672
+ };
324
673
  };
325
674
  //#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
675
  //#region src/components/Piano/index.d.ts
676
+ type KeyboardShortcutsScope = 'root' | 'window';
677
+ /**
678
+ * `style` that also takes CSS custom properties, which is how a key's colours
679
+ * are set: see `index.css` for the ones each key type reads.
680
+ */
681
+ type CSSVariables = Record<`--${string}`, string | number | undefined>;
350
682
  /**
351
- * [noteRange.first, noteRange.first + 1 ..., noteRange.last]
683
+ * What {@link PianoProps.keyProps} may return for one key.
684
+ *
685
+ * `data-*` attributes are spelled out because TypeScript only allows them on
686
+ * JSX syntax, not on an object type, and selecting on one is the usual way to
687
+ * mark a key out.
352
688
  */
353
- declare function getNoteRangeArray(noteRange: NoteRange): number[];
689
+ type KeyAttributes = Omit<ComponentPropsWithoutRef<'div'>, 'style'> & {
690
+ style?: CSSProperties & CSSVariables;
691
+ } & Record<`data-${string}`, string | number | boolean | undefined>;
692
+ /** What a key is, when {@link PianoProps.label} or `keyProps` is asked about it. */
693
+ interface KeyState {
694
+ /** Position in the note range, counting from `noteRange.first`. */
695
+ index: number;
696
+ keyType: 'white' | 'black';
697
+ /** Whether the note is currently sounding. */
698
+ active: boolean;
699
+ /** Whether the note is above {@link PianoProps.midiMax} and cannot sound. */
700
+ disabled: boolean;
701
+ }
354
702
  interface PianoProps {
703
+ /**
704
+ * Classes for what a key draws inside itself: `keyLabelWrapper`, and
705
+ * `keyLabel` around what `label` returns. The key itself takes a class
706
+ * through `keyProps`, which is also where the note and its state are.
707
+ */
708
+ classes?: {
709
+ keyLabelWrapper?: string;
710
+ keyLabel?: string;
711
+ };
712
+ /**
713
+ * The notes to draw keys for, as MIDI note numbers from `first` to `last`,
714
+ * both included.
715
+ */
355
716
  noteRange: NoteRange;
717
+ /**
718
+ * Let a pointer slide from one key to the next while it is down. With it
719
+ * off, the key that was pressed sounds until the pointer is released.
720
+ *
721
+ * @default true
722
+ */
356
723
  glissando?: boolean;
724
+ /**
725
+ * Highest note that can sound. Keys above it carry `data-disabled`.
726
+ *
727
+ * @default 127
728
+ */
357
729
  midiMax?: number;
730
+ /**
731
+ * Play notes from the computer keyboard: `keys[i]` plays
732
+ * `noteRange.first + i`. `SHORTCUTS` has ready-made layouts, which assume
733
+ * `noteRange.first` is a C. Where they listen is `keyboardShortcutsScope`.
734
+ */
358
735
  keyboardShortcuts?: KeyboardShortcuts;
359
- fill?: boolean;
360
- whiteNoteWidth?: number;
361
- blackNoteWidth?: number;
736
+ /**
737
+ * Where keyboard shortcuts listen. `root` handles keys only while the Piano
738
+ * root or one of its descendants has focus; `window` handles them anywhere
739
+ * on the page except in editable elements.
740
+ *
741
+ * @default 'root'
742
+ */
743
+ keyboardShortcutsScope?: KeyboardShortcutsScope;
744
+ /**
745
+ * Follow the width of the parent element, deriving the width of a white key
746
+ * from it. {@link PianoProps.whiteKeyWidth} is ignored.
747
+ *
748
+ * @default false
749
+ */
750
+ resizable?: boolean;
751
+ /**
752
+ * Width of a white key in pixels, not counting `keyGap`. Ignored while
753
+ * `resizable` is on.
754
+ * @default 40
755
+ */
756
+ whiteKeyWidth?: number;
757
+ /**
758
+ * Space between two white keys, in pixels.
759
+ * @default 1
760
+ */
761
+ keyGap?: number;
762
+ /**
763
+ * Width of a black key, as a fraction of {@link PianoProps.whiteKeyWidth}.
764
+ * @default 0.65
765
+ */
766
+ blackKeyWidthRatio?: number;
767
+ /**
768
+ * Height of a black key, as a fraction of the height of the keyboard.
769
+ * @default 0.6
770
+ */
771
+ blackKeyHeightRatio?: number;
772
+ /**
773
+ * Height of the keyboard. Sets `--height`; when it is omitted, the height
774
+ * the theme gives stands, and the theme can tell `resizable` apart through
775
+ * `data-resizable`.
776
+ */
362
777
  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;
778
+ style?: CSSProperties & CSSVariables;
367
779
  /**
368
- * \<WhiteKey /> | \<BlackKey />
780
+ * What to draw inside a key. `''`, `null` and `undefined` leave it bare, so
781
+ * a layout with gaps — {@link SHORTCUTS.HOME_ROW_NATURAL}, say — needs no
782
+ * special casing.
369
783
  */
370
- children?: ReactElement | ReactElement[];
784
+ label?: (note: number, state: KeyState) => ReactNode;
785
+ /**
786
+ * Extra props for one key, by note: a class, a style, a `data-*` attribute
787
+ * to select on.
788
+ *
789
+ * The geometry of the key (`left`, `width`, `height`) is applied after the
790
+ * returned `style` and cannot be overridden, so a key cannot be drawn
791
+ * somewhere other than where it responds.
792
+ *
793
+ * @example highlight the notes of a scale
794
+ * ```tsx
795
+ * keyProps={(note) => ({ 'data-in-scale': inScale(note, root, 'major') ? '' : undefined })}
796
+ * ```
797
+ */
798
+ keyProps?: (note: number, state: KeyState) => KeyAttributes;
799
+ /**
800
+ * Called when a note starts sounding, whether a pointer, a keyboard shortcut
801
+ * or `playNote` asked for it. A note held by several of them at once is
802
+ * reported once. `velocity` is what `playNote` was given, and `undefined`
803
+ * otherwise.
804
+ */
805
+ onPlayNote?: (note: number, velocity?: number) => void;
806
+ /** Called once everything holding a note has let go of it. */
807
+ onStopNote?: (note: number) => void;
371
808
  }
372
809
  interface PianoMethods {
373
810
  playNote: (note: number, velocity?: number) => void;
@@ -377,25 +814,21 @@ interface PianoMethods {
377
814
  * Customizable piano component.
378
815
  */
379
816
  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;
817
+ Root: import("react").ForwardRefExoticComponent<PianoProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof PianoProps> & import("react").RefAttributes<PianoMethods>>;
384
818
  };
385
819
  //#endregion
386
- //#region src/components/PointsEditor/Background.d.ts
387
- declare function Background({
388
- className,
389
- children,
390
- ...props
391
- }: ComponentPropsWithoutRef<'div'>): _$react.JSX.Element;
392
- //#endregion
393
820
  //#region src/components/PointsEditor/Container.d.ts
394
- declare function Container({
395
- className,
396
- children,
397
- ...props
398
- }: ComponentPropsWithoutRef<'div'>): _$react.JSX.Element;
821
+ interface PointsEditorContainerProps {
822
+ /** `<PointsEditor.Point />` goes here. */
823
+ children?: ReactNode;
824
+ }
825
+ //#endregion
826
+ //#region src/components/PointsEditor/Background.d.ts
827
+ interface PointsEditorBackgroundProps {
828
+ /** Whatever the points are placed over: a graph, a canvas, an image. */
829
+ children?: ReactNode;
830
+ }
831
+ declare function Background({ className, children, style, ...props }: PointsEditorBackgroundProps & Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorBackgroundProps>): import("react").JSX.Element;
399
832
  //#endregion
400
833
  //#region src/components/PointsEditor/Point.d.ts
401
834
  type PointBaseType = {
@@ -406,138 +839,301 @@ declare function clampPoint(point: PointBaseType, min?: Partial<PointBaseType>,
406
839
  x: number;
407
840
  y: number;
408
841
  };
409
- interface PointProps<T extends PointBaseType> {
842
+ interface PointsEditorPointProps<T extends PointBaseType> {
843
+ /**
844
+ * Where the point is, as `{ x, y }` from 0 to 1 on each axis, with `y`
845
+ * growing downwards.
846
+ */
410
847
  value: T;
848
+ /** Drawn inside the point: the theme's own dot stands when it is left out. */
849
+ children?: ReactNode;
850
+ /**
851
+ * How the selection refers to this point. One is generated when it is left
852
+ * out, which lasts as long as the point is mounted — give your own if the
853
+ * selection has to survive a remount, or be recognised in your own state.
854
+ */
855
+ id?: string;
856
+ /** The lowest position the point can take, per axis. An axis left out is 0. */
411
857
  min?: Partial<PointBaseType>;
858
+ /** The highest position the point can take, per axis. An axis left out is 1. */
412
859
  max?: Partial<PointBaseType>;
860
+ /** Width and height at once. Sets both `--width` and `--height`. */
413
861
  size?: number | string;
862
+ /** Sets `--width`; the size the theme gives it stands when omitted. */
414
863
  width?: number | string;
864
+ /** Sets `--height`; the size the theme gives it stands when omitted. */
415
865
  height?: number | string;
866
+ /** Sets `--color`, for the theme to colour the point with. */
416
867
  color?: string;
868
+ /** Overrides the `disabled` of `PointsEditor.Root`. */
417
869
  disabled?: boolean;
870
+ /** Overrides the `readonly` of `PointsEditor.Root`. */
418
871
  readonly?: boolean;
872
+ /** Overrides the `wheel` of `PointsEditor.Root`. */
873
+ wheel?: ModifierValue<InputEventOption> | null;
874
+ /** Overrides the `keyboard` of `PointsEditor.Root`. */
875
+ keyboard?: ModifierValue<InputEventOption> | null;
876
+ /**
877
+ * The accessible name of each axis. There are two range inputs inside the
878
+ * point, so this takes one name per axis; a single string names them both.
879
+ */
880
+ 'aria-label'?: string | Partial<Record<'x' | 'y', string>>;
881
+ /** What the value of each axis means, when the number does not say it. */
882
+ 'aria-valuetext'?: string | Partial<Record<'x' | 'y', string>>;
883
+ /**
884
+ * Called with the new position when the point is dragged or moved by the
885
+ * arrow keys or the wheel, including when it moves along with a selection.
886
+ */
419
887
  onChange?: (value: PointBaseType) => void;
888
+ /** Called when a drag on this point starts, with where the point is. */
420
889
  onDragStart?: (value: PointBaseType) => void;
890
+ /** Called when that drag ends, with where the point is. */
421
891
  onDragEnd?: (value: PointBaseType) => void;
422
892
  }
423
- declare function Point<T extends PointBaseType>({
424
- value,
425
- min,
426
- max,
427
- size,
428
- width,
429
- height,
430
- color,
431
- disabled,
432
- readonly,
433
- onChange,
434
- onDragStart,
435
- onDragEnd,
436
- className,
437
- style,
438
- onPointerDown,
439
- ...props
440
- }: PointProps<T> & Omit<ComponentPropsWithoutRef<'div'>, keyof PointProps<T>>): _$react.JSX.Element;
893
+ declare function Point<T extends PointBaseType>({ value, children, id: idProp, min, max, size, width, height, color, disabled: _disabled, readonly: _readonly, wheel: _wheel, keyboard: _keyboard, 'aria-label': ariaLabel, 'aria-valuetext': ariaValuetext, onChange, onDragStart, onDragEnd, className, style, onPointerDown, onKeyDown, onFocus, ...props }: PointsEditorPointProps<T> & Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorPointProps<T>>): import("react").JSX.Element;
894
+ //#endregion
895
+ //#region src/components/PointsEditor/SelectionBox.d.ts
896
+ interface PointsEditorSelectionBoxProps {
897
+ /** Drawn inside the box, which is only there while a drag is running. */
898
+ children?: ReactNode;
899
+ }
900
+ /**
901
+ * The box a drag on empty space draws, and what it covers is selected.
902
+ *
903
+ * Nothing is rendered while no drag is running, and leaving it out leaves the
904
+ * editor without one — the selection still works, it just cannot be seen.
905
+ */
906
+ declare function SelectionBox({ children, className, style, ...props }: PointsEditorSelectionBoxProps & Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorSelectionBoxProps>): import("react").JSX.Element | null;
907
+ //#endregion
908
+ //#region src/components/PointsEditor/context.d.ts
909
+ /**
910
+ * What a `Point` tells the editor about itself, so that a selection can be
911
+ * moved without the editor knowing how the points are stored.
912
+ *
913
+ * Held behind a ref and rewritten on every render: the value changes on every
914
+ * frame of a drag, and a registry keyed on it would be rebuilt just as often.
915
+ */
916
+ interface PointRegistration {
917
+ value: PointBaseType;
918
+ min?: Partial<PointBaseType>;
919
+ max?: Partial<PointBaseType>;
920
+ readonly: boolean;
921
+ onChange?: (value: PointBaseType) => void;
922
+ /** The point's own element, to match the focus against. */
923
+ element: HTMLElement | null;
924
+ /** The wheel option this point resolved, root inheritance applied. */
925
+ wheel: ModifierValue<InputEventOption> | null;
926
+ }
927
+ type PointsEditorContextValue = {
928
+ disabled: boolean;
929
+ readonly: boolean;
930
+ /** Inherited by every `Point`; `null` turns the wheel off. */
931
+ wheel: ModifierValue<InputEventOption> | null;
932
+ /** Inherited by every `Point`; `null` turns the keyboard off. */
933
+ keyboard: ModifierValue<InputEventOption> | null;
934
+ /** Inherited by every `Point`. See `PointsEditorProps.dragSensitivity`. */
935
+ dragSensitivity: ModifierValue<number>;
936
+ externalStyles: {
937
+ cursor?: CSSProperties['cursor'];
938
+ };
939
+ /**
940
+ * `Container` registers its element here; `Point` normalizes the pointer
941
+ * against it, so a point is placed by its position within the container.
942
+ */
943
+ containerRef: RefObject<HTMLDivElement | null>;
944
+ /** Whether points can be selected at all. See `PointsEditorProps.selectable`. */
945
+ selectable: boolean;
946
+ /** Ids of the points currently selected. Always empty while `selectable` is off. */
947
+ selection: readonly string[];
948
+ /** Register a point so that a selection can move it with the rest. */
949
+ registerPoint: (id: string, entry: RefObject<PointRegistration>) => () => void;
950
+ /**
951
+ * Whether the element is a point, or inside one. The registry knows which
952
+ * elements the points are, so nothing has to be recognised by a class name.
953
+ */
954
+ isPointElement: (element: Element | null | undefined) => boolean;
955
+ /**
956
+ * A pointer went down on a point: works out the new selection and takes the
957
+ * snapshot the move will be measured against.
958
+ */
959
+ beginPointDrag: (id: string, modifiers: ModifierState) => void;
960
+ /** Move everything the drag picked up, by one amount, clamped as one. */
961
+ movePointDrag: (delta: PointBaseType) => void;
962
+ /**
963
+ * Move the selection by an amount that did not come from a drag — an arrow
964
+ * key or a wheel notch. The current values are the starting point.
965
+ */
966
+ nudgeSelection: (id: string, delta: PointBaseType) => void;
967
+ /**
968
+ * Move the point that currently holds focus by one notch. The wheel listener
969
+ * belongs to `Container` rather than to every `Point`, so the lookup of who
970
+ * has focus happens here, where the registry is. Returns whether a point
971
+ * took it, so the caller knows whether to consume the event.
972
+ */
973
+ nudgeFocusedPoint: (axis: 'x' | 'y', direction: number, modifiers: ModifierState) => boolean;
974
+ /** The selection box, while one is being dragged. */
975
+ selectionBox: SelectionBoxRect | null;
976
+ beginSelectionBox: (at: PointBaseType, modifiers: ModifierState) => void;
977
+ moveSelectionBox: (to: PointBaseType) => void;
978
+ endSelectionBox: () => void;
979
+ };
980
+ /**
981
+ * The settings `Root` was given, for the subcomponents to read. A point's
982
+ * value still belongs to the `Point` that draws it; what the root keeps is
983
+ * which points are selected, and a registry of who they are.
984
+ */
985
+ declare function usePointsEditorContext(): PointsEditorContextValue;
986
+ declare function usePointsEditorContext<T>(selector: (state: PointsEditorContextValue) => T): T;
441
987
  //#endregion
442
988
  //#region src/components/PointsEditor/index.d.ts
443
989
  interface PointsEditorProps {
990
+ /** Sets `--width`; the size the theme gives it stands when omitted. */
444
991
  width?: number | string;
992
+ /** Sets `--height`; the size the theme gives it stands when omitted. */
445
993
  height?: number | string;
446
- grid?: number | PointBaseType;
994
+ /**
995
+ * Make the points unchangeable and remove them from the tab order.
996
+ * The parts carry `data-disabled` while it is set.
997
+ */
447
998
  disabled?: boolean;
999
+ /**
1000
+ * Make the points unmovable.
1001
+ * The parts carry `data-readonly` while it is set.
1002
+ */
448
1003
  readonly?: boolean;
1004
+ /**
1005
+ * The cursor to show while dragging a point. It is set on the dragged
1006
+ * point, so it stays while the pointer is outside it.
1007
+ *
1008
+ * @default { cursor: 'grabbing' }
1009
+ */
449
1010
  externalStyles?: {
450
- userSelectNone?: boolean;
451
- cursor?: Cursor;
1011
+ cursor?: CSSProperties['cursor'];
452
1012
  };
453
1013
  /**
454
- * wheel control option
455
- * If null, no event will be triggered
1014
+ * How much one notch of the wheel moves the focused `Point`. Scrolling
1015
+ * sideways, or with shift held, moves x; otherwise it moves y.
1016
+ *
1017
+ * A position runs from 0 to 1, so `['normalized', n]` and `['raw', n]` both
1018
+ * move it by `n` of the editor. `null` turns the wheel off, and a `Point`
1019
+ * can override it with a `wheel` of its own.
1020
+ *
1021
+ * @default ['normalized', 0.01]
1022
+ */
1023
+ wheel?: ModifierValue<InputEventOption> | null;
1024
+ /**
1025
+ * How much one arrow key press moves a `Point`. Left and right move x, up
1026
+ * and down move y.
1027
+ *
1028
+ * A position runs from 0 to 1, so `['normalized', n]` and `['raw', n]` both
1029
+ * move it by `n` of the editor; give a map to set an amount per modifier
1030
+ * key. `null` turns the arrow keys off, and a `Point` can override it with a
1031
+ * `keyboard` of its own.
1032
+ *
1033
+ * @default { default: ['normalized', 0.01], shift: ['normalized', 0.001] }
1034
+ */
1035
+ keyboard?: ModifierValue<InputEventOption> | null;
1036
+ /**
1037
+ * How much a drag moves a `Point`, per modifier key.
1038
+ *
1039
+ * `1` is the pointer position itself, which is what dragging a point
1040
+ * normally is. **Anything else turns the drag relative**: `0.1` makes the
1041
+ * same movement cover a tenth of the editor, so the point stops following
1042
+ * the pointer and starts moving a tenth as fast. Shift is bound to `0.1` by
1043
+ * default, to match what it does on the arrow keys.
1044
+ *
1045
+ * Pressing or releasing the key mid-drag does not disturb the point: the
1046
+ * travel so far is kept and the new sensitivity applies from there. **The
1047
+ * pointer and the point stay apart for the rest of the drag** — snapping
1048
+ * them back together on release would move the point nobody asked to move.
1049
+ *
1050
+ * @default { default: 1, shift: 0.1 }
1051
+ */
1052
+ dragSensitivity?: ModifierValue<number>;
1053
+ /**
1054
+ * Let points be selected, and a selection be moved as one.
1055
+ *
1056
+ * Off by default, because it changes what a press and a drag mean: a press
1057
+ * on empty space starts a selection box rather than doing nothing, and a drag
1058
+ * on a point moves everything else that is selected. An editor whose points
1059
+ * each mean something different — the four handles of an ADSR envelope, say
1060
+ * — has nothing to gain from moving them together.
1061
+ *
1062
+ * **A selection calls `onChange` on several points in the same tick**, so
1063
+ * each of them has to update from the previous state rather than from a
1064
+ * value captured in the render:
1065
+ *
1066
+ * ```jsx
1067
+ * onChange={(v) => setPoints((prev) => ({ ...prev, [id]: v }))}
1068
+ * ```
1069
+ *
1070
+ * Written the other way round — `setPoints({ ...points, [id]: v })` — every
1071
+ * call but the last is thrown away, and only one point appears to move.
1072
+ *
1073
+ * @default false
1074
+ */
1075
+ selectable?: boolean;
1076
+ /**
1077
+ * Ids of the selected points, to hold the selection yourself. Leave it out
1078
+ * and the editor keeps its own.
1079
+ *
1080
+ * A `Point` takes its id from its `id` prop, or generates one that lasts as
1081
+ * long as it is mounted.
456
1082
  */
457
- wheel?: InputEventOption | null;
1083
+ selection?: string[];
1084
+ /** The selection to start with, when the editor keeps its own. */
1085
+ defaultSelection?: string[];
1086
+ /** Called whenever the selection changes, controlled or not. */
1087
+ onSelectionChange?: (selection: string[]) => void;
458
1088
  /**
459
- * keyboard control option
460
- * If null, no event will be triggered
1089
+ * The editor renders exactly what you compose here; there is no default
1090
+ * markup to fall back to.
1091
+ *
1092
+ * @example
1093
+ * <PointsEditor.Root>
1094
+ * <PointsEditor.Background>
1095
+ * <svg viewBox="0 0 200 100">...</svg>
1096
+ * </PointsEditor.Background>
1097
+ * <PointsEditor.Container>
1098
+ * {points.map((point, i) => (
1099
+ * <PointsEditor.Point key={i} value={point} onChange={...} />
1100
+ * ))}
1101
+ * </PointsEditor.Container>
1102
+ * </PointsEditor.Root>
461
1103
  */
462
- keyboard?: InputEventOption | null;
1104
+ children: ReactNode;
463
1105
  }
464
1106
  /**
465
1107
  * Multiple Point Controller
466
1108
  */
467
1109
  declare const PointsEditor: {
468
- Root: _$react.ForwardRefExoticComponent<PointsEditorProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof PointsEditorProps> & _$react.RefAttributes<HTMLDivElement>>;
1110
+ Root: import("react").ForwardRefExoticComponent<PointsEditorProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof PointsEditorProps> & import("react").RefAttributes<HTMLDivElement>>;
469
1111
  Background: typeof Background;
470
- Container: typeof Container;
1112
+ Container: import("react").ForwardRefExoticComponent<PointsEditorContainerProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "children"> & import("react").RefAttributes<HTMLDivElement>>;
471
1113
  Point: typeof Point;
1114
+ SelectionBox: typeof SelectionBox;
472
1115
  };
473
1116
  //#endregion
474
- //#region src/components/Slider/type.d.ts
475
- type ScaleType = 'mark' | 'mark-number' | 'number';
476
- type ScaleOptions = ['step', ScaleType] | [number, ScaleType];
477
- //#endregion
478
- //#region src/components/Slider/Scale.d.ts
479
- interface ScaleProps {
480
- gap?: number | string;
481
- options?: ScaleOptions;
482
- children?: ReactNode;
483
- }
484
- declare function Scale({
485
- gap,
486
- options,
487
- children,
488
- className,
489
- style,
490
- ...props
491
- }: ScaleProps & Omit<ComponentPropsWithoutRef<'div'>, keyof ScaleProps>): _$react.JSX.Element;
492
- //#endregion
493
- //#region src/components/Slider/ScaleOption.d.ts
494
- interface ScaleOptionProps {
495
- value: number;
496
- type?: ScaleType;
497
- /** Display text instead of value. */
498
- label?: string;
499
- /** mark thickness */
500
- thickness?: number | string;
501
- /** mark length */
502
- length?: number | string;
503
- /** Gap between mark and label. */
504
- gap?: number | string;
505
- labelWidth?: number | string;
506
- classes?: {
507
- mark?: string;
508
- label?: string;
509
- };
510
- styles?: {
511
- mark?: CSSProperties;
512
- label?: CSSProperties;
513
- };
514
- }
515
- declare function ScaleOption({
516
- value,
517
- type,
518
- label,
519
- thickness,
520
- length,
521
- gap,
522
- labelWidth,
523
- classes,
524
- styles,
525
- className,
526
- style,
527
- ...props
528
- }: ScaleOptionProps & Omit<ComponentPropsWithoutRef<'div'>, keyof ScaleOptionProps>): _$react.JSX.Element;
529
- //#endregion
530
1117
  //#region src/components/Slider/Thumb.d.ts
531
1118
  interface SliderThumbProps {
532
- size?: number | string;
533
- width?: number | string;
534
- height?: number | string;
1119
+ /**
1120
+ * Sets `--color`, for the theme to colour the thumb with. The size is not
1121
+ * set here but by `--thumb-size` on `Slider.Root`, so that the root can
1122
+ * leave the matching room around the track.
1123
+ */
535
1124
  color?: string;
536
1125
  className?: string;
537
1126
  style?: CSSProperties;
1127
+ /**
1128
+ * Rendered inside the thumb. The thumb is one element either way, so what
1129
+ * is passed here is decoration on top of it rather than a replacement for
1130
+ * it — `className` and `style` are how its own appearance is changed.
1131
+ */
538
1132
  children?: ReactNode;
539
- /** @internal */
540
- __percent?: number;
1133
+ 'aria-label'?: AriaAttributes['aria-label'];
1134
+ 'aria-labelledby'?: AriaAttributes['aria-labelledby'];
1135
+ 'aria-describedby'?: AriaAttributes['aria-describedby'];
1136
+ 'aria-valuetext'?: AriaAttributes['aria-valuetext'];
541
1137
  }
542
1138
  interface SliderThumbMethods {
543
1139
  focus: () => void;
@@ -546,92 +1142,248 @@ interface SliderThumbMethods {
546
1142
  //#endregion
547
1143
  //#region src/components/Slider/Track.d.ts
548
1144
  interface SliderTrackProps {
1145
+ /**
1146
+ * How long the track is along the axis the slider runs. Sets `--length`;
1147
+ * the size the theme gives it stands when this is omitted.
1148
+ */
549
1149
  length?: number | string;
1150
+ /** How thick the track is across that axis. Sets `--thickness`. */
550
1151
  thickness?: number | string;
1152
+ /** Colour of the part from `min` to the value. Sets `--active`. */
551
1153
  active?: string;
1154
+ /** Colour of the rest of the track. Sets `--inactive`. */
552
1155
  inactive?: string;
553
- defaultStyle?: boolean;
554
1156
  className?: string;
555
1157
  style?: CSSProperties;
556
- children?: ReactElement;
557
- /** @internal */
558
- __thumb?: ReactElement;
559
- /** @internal */
560
- __percent?: number;
561
- }
562
- declare function Track({
563
- length,
564
- thickness,
565
- active,
566
- inactive,
567
- children,
568
- className,
569
- style,
570
- defaultStyle,
571
- __thumb,
572
- __percent,
573
- ...props
574
- }: SliderTrackProps & Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>): _$react.JSX.Element;
1158
+ /** `<Slider.Thumb />` goes here. */
1159
+ children?: ReactNode;
1160
+ }
1161
+ //#endregion
1162
+ //#region src/components/Slider/type.d.ts
1163
+ /**
1164
+ * How `Slider.Marks` fills itself in when it is given no children: one option
1165
+ * every `per`, or every `step` of the slider. The object form turns off the
1166
+ * mark or the label for the whole set; a single option is customized by
1167
+ * writing `Slider.MarksOption` out instead.
1168
+ */
1169
+ type MarksOptions = 'step' | number | {
1170
+ per: 'step' | number;
1171
+ mark?: boolean;
1172
+ label?: boolean;
1173
+ };
1174
+ //#endregion
1175
+ //#region src/components/Slider/Marks.d.ts
1176
+ interface SliderMarksProps {
1177
+ /**
1178
+ * Space between the marks and the track. Sets `--gap`; the theme's own
1179
+ * spacing stands when this is omitted.
1180
+ */
1181
+ gap?: number | string;
1182
+ /**
1183
+ * Build the marks instead of writing `Slider.MarksOption` out: a number puts
1184
+ * one every that many, `'step'` one every `step`, and `{ per, mark, label }`
1185
+ * also turns the mark or the label off for all of them. `children` is
1186
+ * ignored while it is set.
1187
+ *
1188
+ * `'step'` makes one per step, which is a great many for a fine `step`.
1189
+ */
1190
+ options?: MarksOptions;
1191
+ /**
1192
+ * `<Slider.MarksOption />` goes here, one per mark. Ignored while `options`
1193
+ * is set.
1194
+ */
1195
+ children?: ReactNode;
1196
+ }
1197
+ declare function Marks({ gap, options, children, className, style, ...props }: SliderMarksProps & Omit<ComponentPropsWithoutRef<'div'>, keyof SliderMarksProps>): import("react").JSX.Element;
1198
+ //#endregion
1199
+ //#region src/components/Slider/MarksOption.d.ts
1200
+ interface SliderMarksOptionProps {
1201
+ /** Where the mark sits, on the same scale as the thumb. */
1202
+ value: number;
1203
+ /**
1204
+ * Draw the mark itself. Turn it off to show the label alone.
1205
+ * @default true
1206
+ */
1207
+ mark?: boolean;
1208
+ /**
1209
+ * Text shown next to the mark. Leave it out to show the value; `null` leaves
1210
+ * the label out, and an empty string draws an empty one.
1211
+ */
1212
+ label?: number | string | null;
1213
+ /** Thickness of the mark. Sets `--thickness`. */
1214
+ thickness?: number | string;
1215
+ /** Length of the mark. Sets `--length`. */
1216
+ length?: number | string;
1217
+ /** Space between the mark and the label. Sets `--gap`. */
1218
+ gap?: number | string;
1219
+ /** Width of the label. Sets `--label-width`. */
1220
+ labelWidth?: number | string;
1221
+ /** Classes for the mark and the label inside the option. */
1222
+ classes?: {
1223
+ mark?: string;
1224
+ label?: string;
1225
+ };
1226
+ /** Styles for the mark and the label inside the option. */
1227
+ styles?: {
1228
+ mark?: CSSProperties;
1229
+ label?: CSSProperties;
1230
+ };
1231
+ }
1232
+ declare function MarksOption({ value, mark, label, thickness, length, gap, labelWidth, classes, styles, className, style, ...props }: SliderMarksOptionProps & Omit<ComponentPropsWithoutRef<'div'>, keyof SliderMarksOptionProps>): import("react").JSX.Element;
575
1233
  //#endregion
576
1234
  //#region src/components/Slider/context.d.ts
577
- type State = {
1235
+ type SliderContextValue = {
1236
+ value: number;
578
1237
  min: number;
579
1238
  max: number;
580
1239
  step: number;
581
- skew: number;
1240
+ /** How the value is distributed across the travel. */
1241
+ scale: Scale;
582
1242
  vertical: boolean;
583
1243
  reverse: boolean;
584
1244
  disabled: boolean;
585
1245
  readonly: boolean;
1246
+ onChange?: (value: number) => void;
1247
+ /**
1248
+ * Position of the thumb, 0-100, already accounting for the display direction
1249
+ * implied by `vertical` and `reverse`.
1250
+ */
1251
+ percent: number;
1252
+ /** `Track` registers its element here; `Root` normalizes the pointer against it. */
1253
+ trackRef: RefObject<HTMLDivElement | null>;
1254
+ /** `Thumb` registers itself here; `Root` focuses it when a drag starts. */
1255
+ thumbRef: RefObject<SliderThumbMethods | null>;
586
1256
  };
587
- declare function useSliderContext<T>(selector: (state: State) => T): T;
1257
+ /**
1258
+ * Everything here is derived during render, so there is no state to keep in
1259
+ * sync: `value` comes from the props of `Root` and the rest follows from it.
1260
+ */
1261
+ declare function useSliderContext(): SliderContextValue;
1262
+ declare function useSliderContext<T>(selector: (state: SliderContextValue) => T): T;
588
1263
  //#endregion
589
1264
  //#region src/components/Slider/index.d.ts
590
1265
  interface SliderProps {
1266
+ /** The current value. The slider shows only this, so update it from `onChange`. */
591
1267
  value: number;
1268
+ /** The value at the start of the travel. */
592
1269
  min: number;
1270
+ /** The value at the end of the travel. */
593
1271
  max: number;
1272
+ /**
1273
+ * Granularity of the value. A drag, the wheel and the arrow keys snap it to
1274
+ * multiples of `step`.
1275
+ *
1276
+ * @default 1
1277
+ */
594
1278
  step?: number;
595
- skew?: number;
596
1279
  /**
597
- * slider orientation
598
- * aria-orientation property is also applied.
1280
+ * How the value is distributed across the travel.
1281
+ *
1282
+ * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,
1283
+ * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or
1284
+ * `skewScale(n)` for a value that has to match a JUCE parameter.
1285
+ *
1286
+ * @default linearScale
1287
+ */
1288
+ scale?: Scale;
1289
+ /**
1290
+ * Run the slider vertically, with the value growing upwards. The range input
1291
+ * inside the thumb takes its `aria-orientation` from this.
1292
+ *
1293
+ * @default false
599
1294
  */
600
1295
  vertical?: boolean;
1296
+ /**
1297
+ * Grow the value the other way: leftwards, or downwards when `vertical`.
1298
+ * The arrow keys follow the direction on screen.
1299
+ *
1300
+ * @default false
1301
+ */
601
1302
  reverse?: boolean;
602
- /** Global style to apply when dragged */
1303
+ /**
1304
+ * The cursor to show while dragging. It is set on the dragged element, so it
1305
+ * stays while the pointer is outside the slider.
1306
+ *
1307
+ * @default { cursor: 'pointer' }
1308
+ */
603
1309
  externalStyles?: {
604
- userSelectNone?: boolean;
605
- cursor?: Cursor;
1310
+ cursor?: CSSProperties['cursor'];
606
1311
  };
607
1312
  /**
608
- * wheel control option
609
- * If null, no event will be triggered
1313
+ * How much one notch of the wheel moves the value. It only acts while the
1314
+ * focus is inside, so that scrolling the page past the slider leaves it
1315
+ * alone.
1316
+ *
1317
+ * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the
1318
+ * range between `min` and `max`. The result is snapped to `step`, except for
1319
+ * an amount set on a modifier key (`{ default: …, shift: … }`). `null` turns
1320
+ * the wheel off.
1321
+ *
1322
+ * @default ['raw', 1]
1323
+ */
1324
+ wheel?: ModifierValue<InputEventOption> | null;
1325
+ /**
1326
+ * How much a drag moves the value, per modifier key.
1327
+ *
1328
+ * `1` is the pointer position itself, which is what a drag normally is here.
1329
+ * **Anything else turns the drag relative**: `0.1` makes the same movement
1330
+ * cover a tenth of the travel, so the value stops following the pointer and
1331
+ * starts moving a tenth as fast. Shift is bound to `0.1` by default, to
1332
+ * match what it does on the arrow keys.
1333
+ *
1334
+ * Pressing or releasing the key mid-drag does not disturb the value: the
1335
+ * travel so far is kept and the new sensitivity applies from there. **The
1336
+ * pointer and the value stay apart for the rest of the drag** — snapping
1337
+ * them back together on release would move the value nobody asked to move.
1338
+ *
1339
+ * @default { default: 1, shift: 0.1 }
610
1340
  */
611
- wheel?: InputEventOption | null;
1341
+ dragSensitivity?: ModifierValue<number>;
612
1342
  /**
613
- * keyboard control option
614
- * If null, no event will be triggered
1343
+ * How much one arrow key press moves the value.
1344
+ *
1345
+ * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the
1346
+ * range between `min` and `max`. The result is snapped to `step`, except for
1347
+ * an amount set on a modifier key, which is what lets shift move off the
1348
+ * grid. `null` turns the arrow keys off.
1349
+ *
1350
+ * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise
1351
+ * the amount to match: 1 would round straight back to where it started, and
1352
+ * a development build warns about it.
1353
+ *
1354
+ * @default { default: ['raw', 1], shift: ['raw', 0.1] }
615
1355
  */
616
- keyboard?: InputEventOption | null;
1356
+ keyboard?: ModifierValue<InputEventOption> | null;
617
1357
  /**
618
- * Only the appearance will change.
619
- * Please consider using with readonly.
620
- * aria-disabled property is also applied.
1358
+ * Make the slider unchangeable and remove it from the tab order.
1359
+ * The parts carry `data-disabled` while it is set.
621
1360
  */
622
1361
  disabled?: boolean;
623
1362
  /**
624
1363
  * Make the value unchangeable.
625
- * aria-readonly property is also applied.
1364
+ * The parts carry `data-readonly` while it is set.
626
1365
  */
627
1366
  readonly?: boolean;
628
1367
  className?: string;
629
1368
  style?: CSSProperties;
1369
+ /** Called with the new value when a drag, the wheel or an arrow key moves it. */
630
1370
  onChange?: (value: number) => void;
1371
+ /** Called when a drag starts, with the value where the track was pressed. */
631
1372
  onDragStart?: (value: number) => void;
1373
+ /** Called when the drag ends, with the value it ended on. */
632
1374
  onDragEnd?: (value: number) => void;
633
- /** \<SliderThumb /> | \<SliderTrack /> */
634
- children?: ReactElement | ReactElement[];
1375
+ /**
1376
+ * The slider renders exactly what you compose here; there is no default
1377
+ * markup to fall back to.
1378
+ *
1379
+ * @example
1380
+ * <Slider.Root value={value} min={0} max={100} onChange={setValue}>
1381
+ * <Slider.Track>
1382
+ * <Slider.Thumb aria-label="Level" />
1383
+ * </Slider.Track>
1384
+ * </Slider.Root>
1385
+ */
1386
+ children: ReactNode;
635
1387
  }
636
1388
  interface SliderMethods {
637
1389
  focus: () => void;
@@ -641,90 +1393,214 @@ interface SliderMethods {
641
1393
  * Customizable slider
642
1394
  */
643
1395
  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>>;
646
- Track: typeof Track;
647
- Scale: typeof Scale;
648
- ScaleOption: typeof ScaleOption;
1396
+ Root: React.ForwardRefExoticComponent<SliderProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof SliderProps> & React.RefAttributes<SliderMethods>>;
1397
+ Thumb: React.ForwardRefExoticComponent<SliderThumbProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof SliderThumbProps> & React.RefAttributes<SliderThumbMethods>>;
1398
+ Track: React.ForwardRefExoticComponent<SliderTrackProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof SliderTrackProps> & React.RefAttributes<HTMLDivElement>>;
1399
+ Marks: typeof Marks;
1400
+ MarksOption: typeof MarksOption;
649
1401
  };
650
1402
  //#endregion
651
- //#region src/components/XYPad/Area.d.ts
652
- interface XYPadAreaProps {
653
- width?: number | string;
654
- height?: number | string;
655
- color?: string;
656
- className?: string;
657
- style?: CSSProperties;
658
- children?: ReactElement;
659
- /** inherit */
660
- __thumb?: ReactElement;
661
- }
662
- declare function Area({
663
- width,
664
- height,
665
- color,
666
- children,
667
- className,
668
- style,
669
- __thumb,
670
- ...props
671
- }: XYPadAreaProps & Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadAreaProps>): _$react.JSX.Element;
1403
+ //#region src/components/XYPad/context.d.ts
1404
+ type XYPadContextValue = {
1405
+ value: XY<number>;
1406
+ min: XY<number>;
1407
+ max: XY<number>;
1408
+ step: XY<number>;
1409
+ /** How the value of each axis is distributed across the travel. */
1410
+ scale: XY<Scale>;
1411
+ reverse: XY<boolean>;
1412
+ disabled: boolean;
1413
+ readonly: boolean;
1414
+ onChange?: (value: XY<number>) => void;
1415
+ /**
1416
+ * Position of the thumb, 0-100 per axis, already accounting for `reverse`.
1417
+ */
1418
+ percent: XY<number>;
1419
+ /** `Area` registers its element here; `Root` normalizes the pointer against it. */
1420
+ areaRef: RefObject<HTMLDivElement | null>;
1421
+ /** `Thumb` registers itself here; `Root` focuses it when a drag starts. */
1422
+ thumbRef: RefObject<XYPadThumbMethods | null>;
1423
+ };
1424
+ /**
1425
+ * Everything here is derived during render, so there is no state to keep in
1426
+ * sync: `value` comes from the props of `Root` and the rest follows from it.
1427
+ */
1428
+ declare function useXYPadContext(): XYPadContextValue;
1429
+ declare function useXYPadContext<T>(selector: (state: XYPadContextValue) => T): T;
672
1430
  //#endregion
673
1431
  //#region src/components/XYPad/Thumb.d.ts
674
1432
  interface XYPadThumbProps {
675
- size?: number | string;
676
- width?: number | string;
677
- height?: number | string;
1433
+ /**
1434
+ * Sets `--color`, for the theme to colour the thumb with. The size is not
1435
+ * set here but by `--thumb-size` on `XYPad.Root`, so that the root can leave
1436
+ * the matching room around the area.
1437
+ */
678
1438
  color?: string;
679
- wrapperClassName?: string;
680
- wrapperStyle?: CSSProperties;
1439
+ className?: string;
1440
+ style?: CSSProperties;
1441
+ /**
1442
+ * Rendered inside the thumb. The thumb is one element either way, so what
1443
+ * is passed here is decoration on top of it rather than a replacement for
1444
+ * it — `className` and `style` are how its own appearance is changed.
1445
+ */
681
1446
  children?: ReactNode;
682
- /** @internal */
683
- __disabled?: boolean;
684
- /** @internal */
685
- __readonly?: boolean;
686
- /** @internal */
687
- __css?: CSSProperties;
1447
+ /**
1448
+ * The accessible name of each axis, as `[x, y]`. There are two range inputs
1449
+ * inside the thumb, so each takes a name of its own; a single string names
1450
+ * them both, which is rarely what you want.
1451
+ */
1452
+ 'aria-label'?: XYInput<AriaAttributes['aria-label']>;
1453
+ /** The ids of what labels each axis, as `[x, y]` or one for both. */
1454
+ 'aria-labelledby'?: XYInput<AriaAttributes['aria-labelledby']>;
1455
+ /** The ids of what describes each axis, as `[x, y]` or one for both. */
1456
+ 'aria-describedby'?: XYInput<AriaAttributes['aria-describedby']>;
1457
+ /**
1458
+ * What the value of each axis means, when the number does not say it, as
1459
+ * `[x, y]` or one for both.
1460
+ */
1461
+ 'aria-valuetext'?: XYInput<AriaAttributes['aria-valuetext']>;
688
1462
  }
689
1463
  interface XYPadThumbMethods {
690
1464
  focus: () => void;
691
1465
  blur: () => void;
692
1466
  }
693
1467
  //#endregion
1468
+ //#region src/components/XYPad/Area.d.ts
1469
+ interface XYPadAreaProps {
1470
+ /** Sets `--width`; the size the theme gives it stands when omitted. */
1471
+ width?: number | string;
1472
+ /** Sets `--height`; the size the theme gives it stands when omitted. */
1473
+ height?: number | string;
1474
+ /** Sets `--color`, for the theme to colour the area with. */
1475
+ color?: string;
1476
+ className?: string;
1477
+ style?: CSSProperties;
1478
+ /** `<XYPad.Thumb />` goes here. */
1479
+ children?: ReactNode;
1480
+ }
1481
+ //#endregion
694
1482
  //#region src/components/XYPad/index.d.ts
695
1483
  /**
696
1484
  * Two-dimensional slider component.
1485
+ *
1486
+ * The per-axis settings mirror `Slider`, given as `[x, y]` tuples. A plain
1487
+ * value applies to both axes.
697
1488
  */
698
- interface ValueOptions {
699
- value: number;
700
- min: number;
701
- max: number;
702
- step?: number;
703
- skew?: number;
704
- reverse?: boolean;
1489
+ interface XYPadProps {
1490
+ /** The current value as `[x, y]`. The pad shows only this, so update it from `onChange`. */
1491
+ value: XY<number>;
1492
+ /** The value at the start of each axis, as `[x, y]` or one number for both. */
1493
+ min: XYInput<number>;
1494
+ /** The value at the end of each axis, as `[x, y]` or one number for both. */
1495
+ max: XYInput<number>;
705
1496
  /**
706
- * wheel control option
1497
+ * Granularity of each axis, as `[x, y]` or one number for both. A drag, the
1498
+ * wheel and the arrow keys snap the value to multiples of it.
1499
+ *
1500
+ * @default 1
707
1501
  */
708
- wheel?: InputEventOption | null;
1502
+ step?: XYInput<number>;
709
1503
  /**
710
- * keyboard control option
1504
+ * How the value of each axis is distributed across the travel, as `[x, y]`
1505
+ * or one scale for both.
1506
+ *
1507
+ * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,
1508
+ * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or
1509
+ * `skewScale(n)` for a value that has to match a JUCE parameter.
1510
+ *
1511
+ * @default linearScale
1512
+ */
1513
+ scale?: XYInput<Scale>;
1514
+ /**
1515
+ * Grow an axis the other way, as `[x, y]` or one for both. By default x grows
1516
+ * rightwards and y downwards. The arrow keys follow the direction on screen.
1517
+ *
1518
+ * @default false
1519
+ */
1520
+ reverse?: XYInput<boolean>;
1521
+ /**
1522
+ * How much one notch of the wheel moves the value. It only acts while the
1523
+ * focus is inside, so that scrolling the page past the pad leaves it alone.
1524
+ * Scrolling sideways, or with shift held, moves x; otherwise it moves y.
1525
+ *
1526
+ * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the
1527
+ * range of that axis. The result is snapped to `step`, except for an amount
1528
+ * set on a modifier key (`{ default: …, alt: … }`). `null` turns the wheel
1529
+ * off.
1530
+ *
1531
+ * @default ['raw', 1]
1532
+ */
1533
+ wheel?: ModifierValue<InputEventOption> | null;
1534
+ /**
1535
+ * How much a drag moves the value, per modifier key.
1536
+ *
1537
+ * `1` is the pointer position itself, which is what a drag normally is here.
1538
+ * **Anything else turns the drag relative**: `0.1` makes the same movement
1539
+ * cover a tenth of the travel, so the value stops following the pointer and
1540
+ * starts moving a tenth as fast. Shift is bound to `0.1` by default, to
1541
+ * match what it does on the arrow keys.
1542
+ *
1543
+ * Pressing or releasing the key mid-drag does not disturb the value: the
1544
+ * travel so far is kept and the new sensitivity applies from there. **The
1545
+ * pointer and the value stay apart for the rest of the drag** — snapping
1546
+ * them back together on release would move the value nobody asked to move.
1547
+ *
1548
+ * @default { default: 1, shift: 0.1 }
1549
+ */
1550
+ dragSensitivity?: ModifierValue<number>;
1551
+ /**
1552
+ * How much one arrow key press moves the value. Left and right move x, up
1553
+ * and down move y.
1554
+ *
1555
+ * `['raw', n]` moves the value by `n`, and `['normalized', n]` by `n` of the
1556
+ * range of that axis. The result is snapped to `step`, except for an amount
1557
+ * set on a modifier key, which is what lets shift move off the grid. `null`
1558
+ * turns the arrow keys off.
1559
+ *
1560
+ * The default moves by 1, and by 0.1 with shift. With a `step` above 1, raise
1561
+ * the amount to match: 1 would round straight back to where it started, and
1562
+ * a development build warns about it.
1563
+ *
1564
+ * @default { default: ['raw', 1], shift: ['raw', 0.1] }
1565
+ */
1566
+ keyboard?: ModifierValue<InputEventOption> | null;
1567
+ /**
1568
+ * The cursor to show while dragging. It is set on the dragged element, so it
1569
+ * stays while the pointer is outside the pad.
1570
+ *
1571
+ * @default { cursor: 'pointer' }
711
1572
  */
712
- keyboard?: InputEventOption | null;
713
- }
714
- interface XYPadProps {
715
- x: ValueOptions;
716
- y: ValueOptions;
717
1573
  externalStyles?: {
718
- userSelectNone?: boolean;
719
- cursor?: Cursor;
1574
+ cursor?: CSSProperties['cursor'];
720
1575
  };
1576
+ /**
1577
+ * Make the pad unchangeable and remove its thumb from the tab order.
1578
+ * The parts carry `data-disabled` while it is set.
1579
+ */
721
1580
  disabled?: boolean;
1581
+ /**
1582
+ * Make the pad unchangeable while leaving its thumb focusable.
1583
+ * The parts carry `data-readonly` while it is set.
1584
+ */
722
1585
  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[];
1586
+ /** Called with the new value when a drag, the wheel or an arrow key moves it. */
1587
+ onChange?: (value: XY<number>) => void;
1588
+ /** Called when a drag starts, with the value where the area was pressed. */
1589
+ onDragStart?: (value: XY<number>) => void;
1590
+ /** Called when the drag ends, with the value it ended on. */
1591
+ onDragEnd?: (value: XY<number>) => void;
1592
+ /**
1593
+ * The pad renders exactly what you compose here; there is no default
1594
+ * markup to fall back to.
1595
+ *
1596
+ * @example
1597
+ * <XYPad.Root value={[x, y]} min={0} max={100} onChange={setValue}>
1598
+ * <XYPad.Area>
1599
+ * <XYPad.Thumb />
1600
+ * </XYPad.Area>
1601
+ * </XYPad.Root>
1602
+ */
1603
+ children: ReactNode;
728
1604
  }
729
1605
  interface XYPadMethods {
730
1606
  focus: () => void;
@@ -735,9 +1611,9 @@ interface XYPadMethods {
735
1611
  * Simple XYPad
736
1612
  */
737
1613
  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>>;
740
- Area: typeof Area;
1614
+ Root: import("react").ForwardRefExoticComponent<XYPadProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof XYPadProps> & import("react").RefAttributes<XYPadMethods>>;
1615
+ Thumb: import("react").ForwardRefExoticComponent<XYPadThumbProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof XYPadThumbProps> & import("react").RefAttributes<XYPadThumbMethods>>;
1616
+ Area: import("react").ForwardRefExoticComponent<XYPadAreaProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof XYPadAreaProps> & import("react").RefAttributes<HTMLDivElement>>;
741
1617
  };
742
1618
  //#endregion
743
1619
  //#region src/hooks/useAnimationFrame.d.ts
@@ -752,57 +1628,102 @@ interface UseDragProps {
752
1628
  threshold?: number;
753
1629
  /** CSS cursor to show while dragging. Applied to the element itself. */
754
1630
  cursor?: string;
755
- onDrag?: (x: number, y: number, deltaX: number, deltaY: number) => void;
756
- onDragStart?: () => void;
757
- onDragEnd?: () => void;
1631
+ /**
1632
+ * Hide the pointer and read its movement directly, instead of following it
1633
+ * around the screen.
1634
+ *
1635
+ * @see DragOptions.pointerLock
1636
+ * @default false
1637
+ */
1638
+ pointerLock?: boolean;
1639
+ /**
1640
+ * @param state the whole drag, for anything the four numbers leave out —
1641
+ * the pointer event and its modifier keys, most of all.
1642
+ */
1643
+ onDrag?: (x: number, y: number, deltaX: number, deltaY: number, state: DragState) => void;
1644
+ onDragStart?: (state: DragState) => void;
1645
+ onDragEnd?: (state: DragState) => void;
758
1646
  }
759
1647
  /**
760
1648
  * Track a pointer drag on an element.
761
1649
  *
762
1650
  * @returns a ref callback to attach to the element being dragged
763
1651
  */
764
- declare function useDrag<T extends Element>({
765
- threshold,
766
- cursor,
767
- onDrag,
768
- onDragStart,
769
- onDragEnd
770
- }: UseDragProps): (node: T | null) => void;
1652
+ declare function useDrag<T extends Element>({ threshold, cursor, pointerLock, onDrag, onDragStart, onDragEnd }: UseDragProps): (node: T | null) => void;
771
1653
  //#endregion
772
- //#region src/hooks/useDragWithElement.d.ts
773
- interface UseDragWithElement<T extends Element> {
774
- baseElementRef: RefObject<T | null>;
1654
+ //#region src/hooks/useDragValue.d.ts
1655
+ interface UseDragValueOptions {
1656
+ /** Scaling of each axis; a single value applies to both. */
1657
+ axis: XYInput$1<AxisOptions>;
775
1658
  /**
776
- * Report the position on pointer down, before any movement.
1659
+ * Normalize the pointer against the bounding rect of this element, so that
1660
+ * the value *is* the position pointed at.
777
1661
  *
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.
1662
+ * Give either this or `getValue`.
1663
+ */
1664
+ baseElementRef?: RefObject<Element | null>;
1665
+ /**
1666
+ * Move the value away from where it stood when the drag started, by the
1667
+ * distance dragged. The pointer position itself carries no meaning.
781
1668
  *
782
- * @default false
1669
+ * Give either this or `baseElementRef`.
1670
+ */
1671
+ getValue?: () => XY$1<number>;
1672
+ /**
1673
+ * Pixels of movement that span the whole range, with `getValue`.
1674
+ * @default 100
1675
+ */
1676
+ pixelRange?: XYInput$1<number>;
1677
+ /**
1678
+ * How much the movement counts, read on every move. `0.1` makes the same
1679
+ * movement cover a tenth of the range, which is what a fine-adjustment
1680
+ * modifier wants.
1681
+ *
1682
+ * With `baseElementRef` the value is normally the position pointed at, so
1683
+ * anything but `1` turns the mapping relative and leaves the pointer and the
1684
+ * value apart for the rest of the drag.
1685
+ *
1686
+ * @see relativeMapping
1687
+ * @see elementMapping
783
1688
  */
1689
+ sensitivity?: (state: DragState) => number;
1690
+ /** @see DragValueOptions.updateOnPointerDown */
784
1691
  updateOnPointerDown?: boolean;
1692
+ /** @default 0 */
1693
+ threshold?: number;
785
1694
  /** CSS cursor to show while dragging. Applied to the element itself. */
786
1695
  cursor?: string;
787
- onDrag: (normalizedX: number, normalizedY: number) => void;
788
- onDragStart?: (normalizedX: number, normalizedY: number) => void;
789
- onDragEnd?: (normalizedX: number, normalizedY: number) => void;
1696
+ /**
1697
+ * Hide the pointer and read its movement directly, instead of following it
1698
+ * around the screen. Only with `getValue`: there is no pointer position to
1699
+ * normalize against a `baseElementRef` while the pointer is locked.
1700
+ *
1701
+ * @see DragOptions.pointerLock
1702
+ * @default false
1703
+ */
1704
+ pointerLock?: boolean;
1705
+ /**
1706
+ * Decide whether a pointerdown starts a drag at all, before the pointer is
1707
+ * captured.
1708
+ *
1709
+ * @see DragOptions.shouldStart
1710
+ */
1711
+ shouldStart?: (event: PointerEvent) => boolean;
1712
+ /**
1713
+ * @param state the whole drag, for anything the value leaves out — the
1714
+ * pointer event and its modifier keys, most of all.
1715
+ */
1716
+ onChange?: (value: XY$1<number>, state: DragState) => void;
1717
+ onDragStart?: (value: XY$1<number>, state: DragState) => void;
1718
+ onDragEnd?: (value: XY$1<number>, state: DragState) => void;
790
1719
  }
791
1720
  /**
792
- * Track a pointer drag, reporting the position normalized against
793
- * the bounding rect of `baseElementRef`.
1721
+ * Drive a value with a pointer drag.
794
1722
  *
795
1723
  * @returns a ref callback for the element that starts the drag, and whether a drag is in progress
796
1724
  */
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>>;
1725
+ declare function useDragValue<T extends Element>(options: UseDragValueOptions): {
1726
+ refCallback: (node: T | null) => void;
806
1727
  dragging: boolean;
807
1728
  };
808
1729
  //#endregion
@@ -817,39 +1738,104 @@ declare function useEventListener<K extends keyof GlobalEventHandlersEventMap>(t
817
1738
  declare function useInterval(callback: () => void, delay: number | null): void;
818
1739
  //#endregion
819
1740
  //#region src/hooks/useLongPress.d.ts
820
- declare function useLongPress(callback: () => void, initialDelay?: number, interval?: number): () => void;
1741
+ declare function useLongPress(callback: () => void, initialDelay?: number, interval?: number): (event?: Pick<PointerEvent, "button" | "pointerId">) => void;
821
1742
  //#endregion
822
1743
  //#region src/hooks/useMIDIAccess.d.ts
823
1744
  /**
824
1745
  * Hooks for requesting MIDI access in the browser. The first argument allows you to choose whether to request access on mount.
1746
+ *
1747
+ * `inputs` follows the devices: it changes as one is plugged in or unplugged,
1748
+ * so a list of devices needs no `statechange` listener of its own.
1749
+ *
1750
+ * `request` takes `{ sysex: true }` when the app needs system exclusive
1751
+ * messages. Browsers treat that as a separate, more sensitive permission, so
1752
+ * ask for it only when it is actually used.
825
1753
  */
826
1754
  declare function useMIDIAccess(requestOnMount?: boolean): {
827
- request: () => void;
1755
+ request: (options?: MIDIAccessOptions$1) => void;
828
1756
  midiAccess: MIDIAccess | null;
829
- error: _$_tremolo_ui_dom0.MIDIAccessError | null;
1757
+ error: import("@tremolo-ui/dom").MIDIAccessError | null;
1758
+ inputs: readonly MIDIInput[];
830
1759
  };
831
1760
  //#endregion
832
1761
  //#region src/hooks/useMIDIInput.d.ts
833
1762
  /**
834
- * Hooks for handling note on/off events. To be used with useMIDIAccess. Internally uses useMIDIMessage.
1763
+ * Handle MIDI input events. To be used with {@link useMIDIAccess}.
1764
+ *
1765
+ * The handlers are read fresh on every event, so writing them inline is fine:
1766
+ * the listeners are attached once per `midiAccess` and stay attached. Devices
1767
+ * plugged in later are picked up without anything having to be rebuilt.
1768
+ *
1769
+ * @example
1770
+ * const { midiAccess } = useMIDIAccess()
1771
+ * useMIDIInput(midiAccess, {
1772
+ * onNoteOnEvent: (note, velocity) => play(note, velocity / 127),
1773
+ * onNoteOffEvent: (note) => stop(note),
1774
+ * onControlChangeEvent: (controller, value) => {
1775
+ * if (controller === 1) setModulation(value / 127)
1776
+ * },
1777
+ * })
835
1778
  */
836
- declare function useMIDIInput(midiAccess: MIDIAccess | null, onNoteOnEvent?: (note: number, velocity: number) => void, onNoteOffEvent?: (note: number) => void, onPitchBendEvent?: (msb: number, lsb: number) => void): void;
1779
+ declare function useMIDIInput(midiAccess: MIDIAccess | null, handlers: MIDIInputHandlers): void;
837
1780
  //#endregion
838
1781
  //#region src/hooks/useMIDIMessage.d.ts
839
1782
  /**
840
1783
  * Hooks for when you want to process MIDI events in more detail than useMIDIInput.
1784
+ *
1785
+ * The handler is read fresh on every event, so writing it inline is fine: the
1786
+ * listeners are attached once per `midiAccess` and stay attached, and devices
1787
+ * plugged in later are picked up.
841
1788
  */
842
1789
  declare function useMIDIMessage(midiAccess: MIDIAccess | null, onMIDIMessage: (event: MIDIMessageEvent) => void): void;
843
1790
  //#endregion
844
1791
  //#region src/hooks/useWheel.d.ts
1792
+ interface UseWheelOptions extends WheelOptions {
1793
+ /**
1794
+ * Listen on this element rather than on the one the returned ref callback is
1795
+ * attached to, and ignore that callback.
1796
+ *
1797
+ * For a control made of several movable parts: a wheel event only reaches
1798
+ * what the cursor is over, so a listener per part responds only while the
1799
+ * cursor is over that same part. Listening on the element they share lets
1800
+ * every part see the event, and each one decide whether it is the one to act.
1801
+ *
1802
+ * The element is read when the listener is attached, so a ref filled in by a
1803
+ * parent is fine as long as the parent is above in the tree.
1804
+ */
1805
+ target?: RefObject<Element | null>;
1806
+ }
845
1807
  /**
846
1808
  * Listen to wheel events on an element.
847
1809
  *
848
1810
  * The listener is not passive, so the handler may call `preventDefault()`.
849
1811
  *
850
- * @returns a ref callback to attach to the element
1812
+ * @returns a ref callback to attach to the element, unused when `target` is given
1813
+ */
1814
+ declare function useWheel<T extends Element>(onWheel: (event: WheelEvent) => void, { requireFocus, target }?: UseWheelOptions): (node: T | null) => void;
1815
+ //#endregion
1816
+ //#region src/input-event.d.ts
1817
+ /**
1818
+ * The keyboard amount used by Knob, NumberInput, Slider, and XYPad by default:
1819
+ * 1 per press in the units of the value, and 0.1 with shift held. That is one
1820
+ * `step` only while `step` is 1; a coarser `step` rounds 1 straight back, which
1821
+ * `useCheckSteps` warns about.
1822
+ *
1823
+ * A modifier entry is not snapped to `step`, which is what lets the finer
1824
+ * amount move at all — see `applyDelta` in `@tremolo-ui/functions`.
1825
+ */
1826
+ declare const DEFAULT_KEYBOARD_OPTIONS: ModifierValue<InputEventOption>;
1827
+ /**
1828
+ * The wheel amount used by Knob, NumberInput, Slider, and XYPad by default.
1829
+ *
1830
+ * Browsers turn shift+wheel into horizontal scrolling, which empties `deltaY`
1831
+ * and fills `deltaX`, so no modifier is bound here.
1832
+ */
1833
+ declare const DEFAULT_WHEEL_OPTIONS: ModifierValue<InputEventOption>;
1834
+ /**
1835
+ * The drag sensitivity used by value controls by default. Shift makes the
1836
+ * same movement cover a tenth of the range, matching the arrow keys.
851
1837
  */
852
- declare function useWheel<T extends Element>(onWheel: (event: WheelEvent) => void): (node: T | null) => void;
1838
+ declare const DEFAULT_DRAG_SENSITIVITY: ModifierValue<number>;
853
1839
  //#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 };
1840
+ export { AnimationCanvas, type AnimationCanvasCommonProps, type AnimationCanvasFixedProps, type AnimationCanvasProps, type AnimationCanvasResizableProps, type CSSVariables, DEFAULT_DRAG_SENSITIVITY, DEFAULT_KEYBOARD_OPTIONS, DEFAULT_WHEEL_OPTIONS, type DrawFunction, type InitFunction, type KeyAttributes, type KeyState, type KeyboardShortcuts, Knob, type KnobContextValue, type KnobMethods, type KnobProps, type KnobSVGRootProps, type KnobThumbProps, type MIDIAccessError, type MIDIAccessOptions, type MIDIInputHandlers, type MarksOptions, NOT_SUPPORTED, NumberInput, type NumberInputContextValue, type NumberInputDecrementStepperProps, type NumberInputIncrementStepperProps, type NumberInputInputFieldProps, type NumberInputMethods, type NumberInputProps, type NumberInputStepperProps, PERMISSION_DENIED, PITCH_BEND_CENTER, Piano, type PianoMethods, type PianoProps, type PointBaseType, PointsEditor, type PointsEditorBackgroundProps, type PointsEditorContainerProps, type PointsEditorContextValue, type PointsEditorPointProps, type PointsEditorProps, type PointsEditorSelectionBoxProps, SHORTCUTS, Slider, type SliderContextValue, type SliderMarksOptionProps, type SliderMarksProps, type SliderMethods, type SliderProps, type SliderThumbMethods, type SliderThumbProps, type SliderTrackProps, UNAVAILABLE, type UseDragValueOptions, type UseWheelOptions, type XY, type XYInput, XYPad, type XYPadAreaProps, type XYPadContextValue, type XYPadMethods, type XYPadProps, type XYPadThumbMethods, type XYPadThumbProps, clampPoint, useAnimationFrame, useDrag, useDragValue, useEventListener, useInterval, useKnobContext, useLongPress, useMIDIAccess, useMIDIInput, useMIDIMessage, useNumberInputContext, usePointsEditorContext, useSliderContext, useWheel, useXYPadContext };
855
1841
  //# sourceMappingURL=index.d.cts.map