@tremolo-ui/react 0.5.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 (76) 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 +1468 -534
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1103 -435
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.ts +1103 -435
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1457 -534
  16. package/dist/index.js.map +1 -1
  17. package/package.json +28 -26
  18. package/src/components/AnimationCanvas/index.tsx +115 -76
  19. package/src/components/Knob/ActiveLine.tsx +7 -6
  20. package/src/components/Knob/InactiveLine.tsx +9 -10
  21. package/src/components/Knob/SVGRoot.tsx +5 -3
  22. package/src/components/Knob/Thumb.tsx +33 -11
  23. package/src/components/Knob/context.tsx +17 -1
  24. package/src/components/Knob/index.tsx +148 -55
  25. package/src/components/NumberInput/DecrementStepper.tsx +8 -4
  26. package/src/components/NumberInput/IncrementStepper.tsx +9 -4
  27. package/src/components/NumberInput/InputField.tsx +188 -105
  28. package/src/components/NumberInput/Stepper.tsx +123 -72
  29. package/src/components/NumberInput/{stepperButton.tsx → StepperButton.tsx} +27 -22
  30. package/src/components/NumberInput/context.tsx +35 -8
  31. package/src/components/NumberInput/index.tsx +272 -75
  32. package/src/components/Piano/index.tsx +354 -188
  33. package/src/components/PointsEditor/Background.tsx +11 -2
  34. package/src/components/PointsEditor/Container.tsx +87 -27
  35. package/src/components/PointsEditor/Point.tsx +239 -79
  36. package/src/components/PointsEditor/SelectionBox.tsx +54 -0
  37. package/src/components/PointsEditor/context.tsx +78 -10
  38. package/src/components/PointsEditor/index.tsx +440 -39
  39. package/src/components/Slider/Marks.tsx +48 -16
  40. package/src/components/Slider/MarksOption.tsx +57 -43
  41. package/src/components/Slider/Thumb.tsx +108 -50
  42. package/src/components/Slider/Track.tsx +69 -58
  43. package/src/components/Slider/context.tsx +4 -2
  44. package/src/components/Slider/index.tsx +131 -65
  45. package/src/components/Slider/type.ts +26 -8
  46. package/src/components/XYPad/Area.tsx +45 -35
  47. package/src/components/XYPad/Thumb.tsx +135 -62
  48. package/src/components/XYPad/context.tsx +4 -1
  49. package/src/components/XYPad/index.tsx +180 -48
  50. package/src/components/_util/Placement.tsx +68 -0
  51. package/src/components/_util/VisuallyHiddenRangeInput.tsx +24 -0
  52. package/src/components/_util/css-length.ts +13 -0
  53. package/src/{components/_util/composeRefs.tsx → compose-refs/index.tsx} +33 -5
  54. package/src/hooks/_internal/useCallbackRef.ts +3 -1
  55. package/src/hooks/_internal/useCheckSteps.ts +170 -0
  56. package/src/hooks/useAnimationFrame.ts +17 -7
  57. package/src/hooks/useDrag.ts +38 -8
  58. package/src/hooks/useDragValue.ts +54 -9
  59. package/src/hooks/useEventListener.ts +10 -6
  60. package/src/hooks/useLongPress.ts +42 -8
  61. package/src/hooks/useMIDIAccess.ts +82 -11
  62. package/src/hooks/useMIDIInput.ts +45 -13
  63. package/src/hooks/useMIDIMessage.ts +23 -5
  64. package/src/index.ts +38 -21
  65. package/src/input-event.ts +32 -0
  66. package/dist/index.css +0 -501
  67. package/src/Globals.d.ts +0 -3
  68. package/src/components/Knob/index.css +0 -81
  69. package/src/components/NumberInput/index.css +0 -121
  70. package/src/components/Piano/index.css +0 -90
  71. package/src/components/PointsEditor/index.css +0 -35
  72. package/src/components/Slider/index.css +0 -125
  73. package/src/components/XYPad/index.css +0 -60
  74. package/src/components/_util/index.ts +0 -28
  75. package/src/styles/global.css +0 -19
  76. /package/src/components/Piano/{keyboardShortcuts.ts → keyboard-shortcuts.ts} +0 -0
package/dist/index.d.cts CHANGED
@@ -1,18 +1,35 @@
1
- import * as _$react from "react";
2
- import React, { CSSProperties, ComponentPropsWithoutRef, DependencyList, ReactElement, ReactNode, Ref, RefObject, SVGProps } from "react";
3
- import * as _$_tremolo_ui_dom0 from "@tremolo-ui/dom";
4
- import { AnimationFrame, AxisOptions, MIDIAccessError, NOT_SUPPORTED, PERMISSION_DENIED, WheelOptions, XY, XY as XY$1, XYInput, XYInput as XYInput$1 } from "@tremolo-ui/dom";
5
- import { InputEventOption, NoteRange, Scale, Units, ValueRange } from "@tremolo-ui/functions";
6
-
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
11
  type DrawFunction = (context: CanvasRenderingContext2D, option: AnimationFrame) => void;
13
- interface CommonProps {
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
+ */
14
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
+ */
15
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
+ */
16
33
  animate?: boolean;
17
34
  /**
18
35
  * Read once, when the 2D context is created, so changing it later has no
@@ -21,95 +38,150 @@ interface CommonProps {
21
38
  * @see https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement/getContext#contextattributes
22
39
  */
23
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;
24
49
  }
25
- 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
+ */
26
58
  width?: number;
59
+ /**
60
+ * Height of the canvas in CSS pixels.
61
+ * @default 100
62
+ */
27
63
  height?: number;
28
64
  }
29
- interface RelativeSizingProps {
30
- relativeSize?: boolean;
31
- 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;
32
79
  }
33
- type AnimationCanvasProps = CommonProps & AbsoluteSizingProps & RelativeSizingProps;
80
+ type AnimationCanvasProps = AnimationCanvasCommonProps & (AnimationCanvasFixedProps | AnimationCanvasResizableProps);
81
+ type Props = AnimationCanvasProps & Omit<ComponentPropsWithoutRef<'canvas'>, keyof AnimationCanvasCommonProps | 'resizable' | 'width' | 'height'>;
34
82
  /**
35
83
  * A simple animatable canvas with requestAnimationFrame()
36
84
  */
37
- declare function AnimationCanvas(props: CommonProps & AbsoluteSizingProps & Omit<ComponentPropsWithoutRef<'canvas'>, keyof AnimationCanvasProps>): ReactElement;
38
- declare function AnimationCanvas(pros: CommonProps & RelativeSizingProps & Omit<ComponentPropsWithoutRef<'canvas'>, keyof AnimationCanvasProps>): ReactElement;
39
- //#endregion
40
- //#region src/components/_util/index.d.ts
41
- declare const cursorStyles: {
42
- grabbing: string;
43
- grab: string;
44
- pointer: string;
45
- move: string;
46
- none: string;
47
- };
48
- type Cursor = keyof typeof cursorStyles;
85
+ declare const AnimationCanvas: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<HTMLCanvasElement>>;
49
86
  //#endregion
50
87
  //#region src/components/Knob/ActiveLine.d.ts
51
- declare function ActiveLine({
52
- stroke,
53
- strokeWidth,
54
- className,
55
- ...props
56
- }: Omit<SVGProps<SVGPathElement>, 'd'>): _$react.JSX.Element;
88
+ declare function ActiveLine({ stroke, strokeWidth, className, ...props }: Omit<SVGProps<SVGPathElement>, 'd'>): import("react").JSX.Element;
57
89
  //#endregion
58
90
  //#region src/components/Knob/InactiveLine.d.ts
59
- declare function InactiveLine({
60
- stroke,
61
- strokeWidth,
62
- className,
63
- ...props
64
- }: Omit<SVGProps<SVGPathElement>, 'd'>): _$react.JSX.Element;
91
+ declare function InactiveLine({ stroke, strokeWidth, className, ...props }: Omit<SVGProps<SVGPathElement>, 'd'>): import("react").JSX.Element;
65
92
  //#endregion
66
93
  //#region src/components/Knob/SVGRoot.d.ts
67
- interface SVGRootProps {
94
+ interface KnobSVGRootProps {
68
95
  /**
69
96
  * `<Knob.InactiveLine />`, `<Knob.ActiveLine />` and `<Knob.Thumb />` go
70
97
  * here, in the order you want them painted.
71
98
  */
72
99
  children: ReactNode;
73
100
  }
74
- declare function SVGRoot({
75
- children,
76
- style,
77
- ...props
78
- }: SVGRootProps & Omit<SVGProps<SVGSVGElement>, keyof SVGRootProps>): _$react.JSX.Element;
101
+ declare function SVGRoot({ children, style, ...props }: KnobSVGRootProps & Omit<SVGProps<SVGSVGElement>, keyof KnobSVGRootProps>): import("react").JSX.Element;
79
102
  //#endregion
80
103
  //#region src/components/Knob/Thumb.d.ts
81
- interface Props$1 {
82
- /** color */
104
+ interface KnobThumbProps {
105
+ /**
106
+ * Fill colour of the circle.
107
+ * @default 'currentColor'
108
+ */
83
109
  thumb?: string;
84
- /** color */
110
+ /**
111
+ * Colour of the line that points at the value.
112
+ * @default 'currentColor'
113
+ */
85
114
  thumbLine?: string;
86
- /** percent (0-100) */
115
+ /**
116
+ * Diameter of the circle, as a percentage of the knob.
117
+ * @default 84
118
+ */
87
119
  thumbSize?: number;
88
- /** percent (0-100) */
120
+ /**
121
+ * Thickness of the line, as a percentage of the knob.
122
+ * @default 6
123
+ */
89
124
  thumbLineWeight?: number;
90
- /** 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
+ */
91
130
  thumbLineLength?: number;
131
+ /**
132
+ * Classes for what the thumb draws inside itself: `thumbLine` is the line
133
+ * that points at the value.
134
+ */
92
135
  classes?: {
93
- thumb?: string;
94
136
  thumbLine?: string;
95
137
  };
96
138
  }
97
- declare function Thumb$2({
98
- className,
99
- thumb,
100
- thumbLine,
101
- thumbSize,
102
- thumbLineWeight,
103
- thumbLineLength,
104
- classes,
105
- ...props
106
- }: Props$1 & Omit<SVGProps<SVGSVGElement>, 'd' | keyof Props$1>): _$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;
107
170
  //#endregion
108
171
  //#region src/components/Knob/index.d.ts
109
172
  interface KnobProps {
173
+ /** The current value. The knob shows only this, so update it from `onChange`. */
110
174
  value: number;
175
+ /** The value with the knob turned all the way down. */
111
176
  min: number;
177
+ /** The value with the knob turned all the way up. */
112
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
+ */
113
185
  step?: number;
114
186
  /**
115
187
  * How the value is distributed across the travel.
@@ -122,46 +194,115 @@ interface KnobProps {
122
194
  */
123
195
  scale?: Scale;
124
196
  /**
125
- * value set when double-clicking
126
- * restriction: enableDoubleClickDefault = true
197
+ * The value a double click restores, while `enableDoubleClickDefault` is on.
127
198
  * @default min
128
- * @see enableDoubleClickDefault
129
199
  */
130
200
  defaultValue?: number;
131
201
  /**
132
- * 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.
133
204
  * @default min
134
205
  */
135
206
  startValue?: number;
136
- /** width and height */
137
207
  /**
138
- * Width and height of the knob.
139
- * Defaults to the `--knob-size` CSS variable (50px).
208
+ * Width and height of the knob. Sets `--knob-size`; the size the theme
209
+ * gives it stands when this is omitted.
140
210
  */
141
211
  size?: number | string;
142
212
  /**
143
- * Global style to apply when dragged
144
- * @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' }
145
217
  */
146
218
  externalStyles?: {
147
- userSelectNone?: boolean;
148
- cursor?: Cursor;
219
+ cursor?: CSSProperties['cursor'];
149
220
  };
150
221
  /**
151
- * wheel control option
152
- * 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
264
+ */
265
+ pointerLock?: boolean;
266
+ /**
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] }
153
279
  */
154
- wheel?: InputEventOption | null;
280
+ keyboard?: ModifierValue<InputEventOption> | null;
155
281
  /**
156
- * keyboard control option
157
- * If null, no event will be triggered
282
+ * Restore `defaultValue` on a double click.
283
+ * @default true
158
284
  */
159
- keyboard?: InputEventOption | null;
160
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
+ */
161
290
  disabled?: boolean;
291
+ /**
292
+ * Make the knob unchangeable while leaving it focusable.
293
+ * The parts carry `data-readonly` while it is set.
294
+ */
162
295
  readonly?: boolean;
163
- /** 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
+ */
164
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
+ */
165
306
  onChange?: (value: number) => void;
166
307
  /**
167
308
  * The knob renders exactly what you compose here; there is no default
@@ -186,88 +327,44 @@ interface KnobMethods {
186
327
  * Interactive rotary knob component implemented in SVG.
187
328
  */
188
329
  declare const Knob: {
189
- 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>>;
190
331
  SVGRoot: typeof SVGRoot;
191
332
  InactiveLine: typeof InactiveLine;
192
333
  ActiveLine: typeof ActiveLine;
193
- Thumb: typeof Thumb$2;
334
+ Thumb: typeof Thumb;
194
335
  };
195
336
  //#endregion
196
- //#region src/components/NumberInput/stepperButton.d.ts
197
- interface StepperButtonProps {
337
+ //#region src/components/NumberInput/InputField.d.ts
338
+ interface NumberInputInputFieldProps {
198
339
  className?: string;
199
340
  style?: CSSProperties;
200
- /** Replaces the default arrow. Size it with the `--stepper-icon-size` variable. */
201
- children?: ReactNode;
202
341
  }
203
342
  //#endregion
204
- //#region src/components/NumberInput/DecrementStepper.d.ts
205
- type DecrementStepperProps = StepperButtonProps;
206
- /** Lowers the value by one `step`, repeating while held. */
207
- declare function DecrementStepper(props: DecrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof DecrementStepperProps>): _$react.JSX.Element;
208
- //#endregion
209
- //#region src/components/NumberInput/IncrementStepper.d.ts
210
- type IncrementStepperProps = StepperButtonProps;
211
- /** Raises the value by one `step`, repeating while held. */
212
- declare function IncrementStepper(props: IncrementStepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof IncrementStepperProps>): _$react.JSX.Element;
213
- //#endregion
214
- //#region src/components/NumberInput/InputField.d.ts
215
- interface NumberInputFieldProps {
216
- /**
217
- * Select the text when the input takes focus. `'number'` selects the leading
218
- * number, leaving whatever the format appended to it.
219
- * @default 'none'
220
- */
221
- selectOnFocus?: 'all' | 'number' | 'none';
222
- /**
223
- * Commit and leave the input when Enter is pressed. Enter commits either way.
224
- * @default true
225
- */
226
- blurOnEnter?: boolean;
343
+ //#region src/components/NumberInput/Stepper.d.ts
344
+ interface NumberInputStepperProps {
227
345
  className?: string;
228
346
  style?: CSSProperties;
229
- ref?: Ref<HTMLInputElement>;
347
+ /** `<NumberInput.IncrementStepper />` and `<NumberInput.DecrementStepper />` go here. */
348
+ children?: ReactNode;
230
349
  }
231
- /**
232
- * The text field of a `NumberInput`, and the only place the value can be typed.
233
- *
234
- * While the user types, their own text stands rather than `format(value)`, so
235
- * that a half-finished entry is not rewritten under the caret.
236
- */
237
- declare function InputField({
238
- selectOnFocus,
239
- blurOnEnter,
240
- className,
241
- style,
242
- ref,
243
- onFocus,
244
- onBlur,
245
- onKeyDown,
246
- ...props
247
- }: NumberInputFieldProps & Omit<ComponentPropsWithoutRef<'input'>, keyof NumberInputFieldProps | 'type' | 'value' | 'defaultValue'>): _$react.JSX.Element;
248
350
  //#endregion
249
- //#region src/components/NumberInput/Stepper.d.ts
250
- interface StepperProps {
351
+ //#region src/components/NumberInput/StepperButton.d.ts
352
+ interface StepperButtonProps {
251
353
  className?: string;
252
354
  style?: CSSProperties;
253
- /** `<NumberInput.IncrementStepper />` and `<NumberInput.DecrementStepper />` go here. */
355
+ /** Replaces the default arrow. Size it with the `--stepper-icon-size` variable. */
254
356
  children?: ReactNode;
255
- ref?: Ref<HTMLDivElement>;
256
357
  }
257
- /**
258
- * The area the steppers sit in, and a drag handle in its own right: dragging it
259
- * up and down moves the value one `step` every `drag` pixels.
260
- *
261
- * The drag lives here rather than on `InputField` because `createDrag` turns
262
- * off text selection on whatever element it is attached to.
263
- */
264
- declare function Stepper({
265
- className,
266
- style,
267
- children,
268
- ref,
269
- ...props
270
- }: StepperProps & Omit<ComponentPropsWithoutRef<'div'>, keyof StepperProps>): _$react.JSX.Element;
358
+ //#endregion
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;
271
368
  //#endregion
272
369
  //#region src/components/NumberInput/context.d.ts
273
370
  type NumberInputContextValue = {
@@ -279,29 +376,53 @@ type NumberInputContextValue = {
279
376
  */
280
377
  min?: number;
281
378
  max?: number;
282
- step: number; /** How the value is distributed across the travel. */
379
+ step: number;
380
+ /** How the value is distributed across the travel. */
283
381
  scale: Scale;
284
382
  disabled: boolean;
285
383
  readonly: boolean;
286
384
  clampValue: boolean;
287
385
  /**
288
- * The effective scaling for `applyDelta` and for clamping on commit, with the
289
- * unbounded ends filled in. Ignores `min` / `max` when `clampValue` is off.
386
+ * The effective scaling for `applyDelta`, with unbounded ends filled in.
387
+ * Ignores `min` / `max` when `clampValue` is off.
290
388
  */
291
389
  range: ValueRange;
292
- keyboard: InputEventOption | null; /** Pixels of vertical drag on `Stepper` that move the value by one `step`. */
293
- drag: number | null; /** What the input shows: the draft while editing, the formatted value otherwise. */
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. */
294
408
  text: string;
295
- editing: boolean; /** Whether the value sits outside `min` / `max`. Not judged while editing. */
296
- outOfRange: boolean; /** Whether a stepper would have no effect, for `aria-disabled`. */
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`. */
297
413
  atMin: boolean;
298
414
  atMax: boolean;
299
415
  format: (value: number) => string;
300
- parse: (text: string) => number; /** Typing. Replaces the draft and reports the parsed value, unclamped. */
301
- setDraft: (text: string) => void; /** Blur or Enter. Clamps the draft, reports it, and drops the draft. */
302
- commitDraft: () => void; /** Drops the draft and replaces the value: steppers, wheel, keyboard, drag. */
303
- changeValue: (next: number) => void; /** Moves the value by one `option` in `direction`, normally +1 or -1. */
304
- nudge: (direction: number, option: InputEventOption) => void; /** `InputField` registers itself here; `Root` focuses it through its methods. */
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. */
305
426
  inputRef: RefObject<HTMLInputElement | null>;
306
427
  };
307
428
  /**
@@ -318,8 +439,27 @@ interface NumberInputProps {
318
439
  * is typing, when their own text stands until it is committed.
319
440
  */
320
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`.
447
+ */
321
448
  min?: number;
449
+ /**
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`.
454
+ */
322
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
+ */
323
463
  step?: number;
324
464
  /**
325
465
  * How the value is distributed across the travel of a drag or a
@@ -329,26 +469,20 @@ interface NumberInputProps {
329
469
  */
330
470
  scale?: Scale;
331
471
  /**
332
- * Unit to display the value in, or a list to pick from by magnitude.
333
- * Builds the default `format` and `parse`.
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.
334
476
  *
335
477
  * @example
336
- * units='Hz'
337
- * units={[['Hz', 1], ['kHz', 1000]]}
338
- * units={[['ms', 1], ['s', 1000]]}
478
+ * <NumberInput.Root {...unitFormat('Hz', { digits: 2 })} value={v} />
339
479
  */
340
- units?: string | Units;
480
+ format?: (value: number) => string;
341
481
  /**
342
- * Digits after the decimal point, for the default `format`.
482
+ * Read a value back out of the text. Has to undo `format`.
343
483
  *
344
- * @example
345
- * // units={[['Hz', 1], ['kHz', 1000]]} digit={2}
346
- * // 100 -> 100.00Hz, 1600 -> 1.60kHz
484
+ * Text with no number in it reads as `NaN`, which leaves the value alone.
347
485
  */
348
- digit?: number;
349
- /** Render the value as text. Takes precedence over `units` / `digit`. */
350
- format?: (value: number) => string;
351
- /** Read a value back out of the text. Takes precedence over `units`. */
352
486
  parse?: (text: string) => number;
353
487
  /**
354
488
  * Keep the value within `min` and `max` when it is committed or stepped.
@@ -357,35 +491,130 @@ interface NumberInputProps {
357
491
  */
358
492
  clampValue?: boolean;
359
493
  /**
360
- * Wheel control option. Only applies while the focus is inside, so that
361
- * scrolling past the input does not change it.
362
- * If null, no event will be triggered
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]
363
504
  */
364
- wheel?: InputEventOption | null;
505
+ wheel?: ModifierValue<InputEventOption> | null;
365
506
  /**
366
- * keyboard control option
367
- * If null, no event will be triggered
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] }
368
519
  */
369
- keyboard?: InputEventOption | null;
520
+ keyboard?: ModifierValue<InputEventOption> | null;
370
521
  /**
371
522
  * Pixels of vertical drag on `Stepper` that move the value by one `step`.
372
- * If null, no event will be triggered
523
+ * `null` turns the drag off.
373
524
  * @default 1
374
525
  */
375
526
  drag?: number | null;
376
527
  /**
377
- * Only the appearance will change.
378
- * Please consider using with readonly.
379
- * aria-disabled property is also applied.
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
599
+ */
600
+ blurOnEnter?: boolean;
601
+ /**
602
+ * Make the input unchangeable and remove it from the tab order.
603
+ * The parts carry `data-disabled` while it is set.
380
604
  */
381
605
  disabled?: boolean;
382
606
  /**
383
607
  * Make the value unchangeable.
384
- * aria-readonly property is also applied.
608
+ * The parts carry `data-readonly` while it is set.
385
609
  */
386
610
  readonly?: boolean;
387
611
  className?: string;
388
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
+ */
389
618
  onChange?: (value: number) => void;
390
619
  /**
391
620
  * The input renders exactly what you compose here; there is no default
@@ -410,14 +639,14 @@ interface NumberInputMethods {
410
639
  * Input with some useful functions for entering numerical values.
411
640
  */
412
641
  declare const NumberInput: {
413
- Root: _$react.ForwardRefExoticComponent<NumberInputProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof NumberInputProps> & _$react.RefAttributes<NumberInputMethods>>;
414
- InputField: typeof InputField;
415
- 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>>;
416
645
  IncrementStepper: typeof IncrementStepper;
417
646
  DecrementStepper: typeof DecrementStepper;
418
647
  };
419
648
  //#endregion
420
- //#region src/components/Piano/keyboardShortcuts.d.ts
649
+ //#region src/components/Piano/keyboard-shortcuts.d.ts
421
650
  type KeyboardShortcuts = {
422
651
  /**
423
652
  * Keys laid out from `noteRange.first`, one entry per semitone.
@@ -433,15 +662,18 @@ type KeyboardShortcuts = {
433
662
  * Ready-made keyboard layouts. Both assume `noteRange.first` is a C.
434
663
  */
435
664
  declare const SHORTCUTS: {
436
- /** Every semitone from C, over the two rows of a QWERTY keyboard. */HOME_ROW: {
665
+ /** Every semitone from C, over the two rows of a QWERTY keyboard. */
666
+ HOME_ROW: {
437
667
  keys: string[];
438
- }; /** The white keys only, on the home row. Black keys have no shortcut. */
668
+ };
669
+ /** The white keys only, on the home row. Black keys have no shortcut. */
439
670
  HOME_ROW_NATURAL: {
440
671
  keys: string[];
441
672
  };
442
673
  };
443
674
  //#endregion
444
675
  //#region src/components/Piano/index.d.ts
676
+ type KeyboardShortcutsScope = 'root' | 'window';
445
677
  /**
446
678
  * `style` that also takes CSS custom properties, which is how a key's colours
447
679
  * are set: see `index.css` for the ones each key type reads.
@@ -468,31 +700,62 @@ interface KeyState {
468
700
  disabled: boolean;
469
701
  }
470
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
+ */
471
716
  noteRange: NoteRange;
472
717
  /**
473
- * Let a pointer slide from one key to the next while it is down.
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.
474
720
  *
475
721
  * @default true
476
722
  */
477
723
  glissando?: boolean;
478
724
  /**
479
- * Highest note that can sound. Keys above it are drawn `aria-disabled`.
725
+ * Highest note that can sound. Keys above it carry `data-disabled`.
480
726
  *
481
727
  * @default 127
482
728
  */
483
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
+ */
484
735
  keyboardShortcuts?: KeyboardShortcuts;
485
736
  /**
486
- * Fill the parent element, deriving the width of a white key from it.
487
- * {@link PianoProps.whiteKeyWidth} is ignored.
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.
488
747
  *
489
748
  * @default false
490
749
  */
491
- fill?: boolean;
492
- /** @default 40 */
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
+ */
493
756
  whiteKeyWidth?: number;
494
757
  /**
495
- * Space between two white keys.
758
+ * Space between two white keys, in pixels.
496
759
  * @default 1
497
760
  */
498
761
  keyGap?: number;
@@ -506,7 +769,11 @@ interface PianoProps {
506
769
  * @default 0.6
507
770
  */
508
771
  blackKeyHeightRatio?: number;
509
- /** @default `fill ? '100%' : 160` */
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
+ */
510
777
  height?: number | string;
511
778
  style?: CSSProperties & CSSVariables;
512
779
  /**
@@ -525,11 +792,18 @@ interface PianoProps {
525
792
  *
526
793
  * @example highlight the notes of a scale
527
794
  * ```tsx
528
- * keyProps={(note) => ({ 'data-in-scale': inScale(note, root, 'major') })}
795
+ * keyProps={(note) => ({ 'data-in-scale': inScale(note, root, 'major') ? '' : undefined })}
529
796
  * ```
530
797
  */
531
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
+ */
532
805
  onPlayNote?: (note: number, velocity?: number) => void;
806
+ /** Called once everything holding a note has let go of it. */
533
807
  onStopNote?: (note: number) => void;
534
808
  }
535
809
  interface PianoMethods {
@@ -540,32 +814,21 @@ interface PianoMethods {
540
814
  * Customizable piano component.
541
815
  */
542
816
  declare const Piano: {
543
- Root: _$react.ForwardRefExoticComponent<PianoProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof PianoProps> & _$react.RefAttributes<PianoMethods>>;
817
+ Root: import("react").ForwardRefExoticComponent<PianoProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof PianoProps> & import("react").RefAttributes<PianoMethods>>;
544
818
  };
545
819
  //#endregion
546
- //#region src/components/PointsEditor/Background.d.ts
547
- interface PointsEditorBackgroundProps {
548
- /** Whatever the points are placed over: a graph, a canvas, an image. */
549
- children?: ReactNode;
550
- }
551
- declare function Background({
552
- className,
553
- children,
554
- ...props
555
- }: PointsEditorBackgroundProps & Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorBackgroundProps>): _$react.JSX.Element;
556
- //#endregion
557
820
  //#region src/components/PointsEditor/Container.d.ts
558
821
  interface PointsEditorContainerProps {
559
822
  /** `<PointsEditor.Point />` goes here. */
560
823
  children?: ReactNode;
561
- ref?: Ref<HTMLDivElement>;
562
824
  }
563
- declare function Container({
564
- children,
565
- className,
566
- ref,
567
- ...props
568
- }: PointsEditorContainerProps & Omit<ComponentPropsWithoutRef<'div'>, keyof PointsEditorContainerProps>): _$react.JSX.Element;
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;
569
832
  //#endregion
570
833
  //#region src/components/PointsEditor/Point.d.ts
571
834
  type PointBaseType = {
@@ -576,104 +839,252 @@ declare function clampPoint(point: PointBaseType, min?: Partial<PointBaseType>,
576
839
  x: number;
577
840
  y: number;
578
841
  };
579
- 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
+ */
580
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. */
581
857
  min?: Partial<PointBaseType>;
858
+ /** The highest position the point can take, per axis. An axis left out is 1. */
582
859
  max?: Partial<PointBaseType>;
860
+ /** Width and height at once. Sets both `--width` and `--height`. */
583
861
  size?: number | string;
862
+ /** Sets `--width`; the size the theme gives it stands when omitted. */
584
863
  width?: number | string;
864
+ /** Sets `--height`; the size the theme gives it stands when omitted. */
585
865
  height?: number | string;
866
+ /** Sets `--color`, for the theme to colour the point with. */
586
867
  color?: string;
587
868
  /** Overrides the `disabled` of `PointsEditor.Root`. */
588
869
  disabled?: boolean;
589
870
  /** Overrides the `readonly` of `PointsEditor.Root`. */
590
871
  readonly?: boolean;
591
872
  /** Overrides the `wheel` of `PointsEditor.Root`. */
592
- wheel?: InputEventOption | null;
873
+ wheel?: ModifierValue<InputEventOption> | null;
593
874
  /** Overrides the `keyboard` of `PointsEditor.Root`. */
594
- keyboard?: InputEventOption | null;
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
+ */
595
887
  onChange?: (value: PointBaseType) => void;
888
+ /** Called when a drag on this point starts, with where the point is. */
596
889
  onDragStart?: (value: PointBaseType) => void;
890
+ /** Called when that drag ends, with where the point is. */
597
891
  onDragEnd?: (value: PointBaseType) => void;
598
892
  }
599
- declare function Point<T extends PointBaseType>({
600
- value,
601
- min,
602
- max,
603
- size,
604
- width,
605
- height,
606
- color,
607
- disabled: _disabled,
608
- readonly: _readonly,
609
- wheel: _wheel,
610
- keyboard: _keyboard,
611
- onChange,
612
- onDragStart,
613
- onDragEnd,
614
- className,
615
- style,
616
- onPointerDown,
617
- onKeyDown,
618
- ...props
619
- }: 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;
620
907
  //#endregion
621
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
+ }
622
927
  type PointsEditorContextValue = {
623
928
  disabled: boolean;
624
- readonly: boolean; /** Inherited by every `Point`; `null` turns the wheel off. */
625
- wheel: InputEventOption | null; /** Inherited by every `Point`; `null` turns the keyboard off. */
626
- keyboard: InputEventOption | null;
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>;
627
936
  externalStyles: {
628
- userSelectNone?: boolean;
629
- cursor?: Cursor;
937
+ cursor?: CSSProperties['cursor'];
630
938
  };
631
939
  /**
632
940
  * `Container` registers its element here; `Point` normalizes the pointer
633
941
  * against it, so a point is placed by its position within the container.
634
942
  */
635
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;
636
979
  };
637
980
  /**
638
- * The settings `Root` was given, for the subcomponents to read. There is no
639
- * state to keep in sync: a point's value belongs to the `Point` that draws it.
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.
640
984
  */
641
985
  declare function usePointsEditorContext(): PointsEditorContextValue;
642
986
  declare function usePointsEditorContext<T>(selector: (state: PointsEditorContextValue) => T): T;
643
987
  //#endregion
644
988
  //#region src/components/PointsEditor/index.d.ts
645
989
  interface PointsEditorProps {
990
+ /** Sets `--width`; the size the theme gives it stands when omitted. */
646
991
  width?: number | string;
992
+ /** Sets `--height`; the size the theme gives it stands when omitted. */
647
993
  height?: number | string;
648
994
  /**
649
- * Only the appearance will change.
650
- * Please consider using with readonly.
651
- * aria-disabled property is also applied.
995
+ * Make the points unchangeable and remove them from the tab order.
996
+ * The parts carry `data-disabled` while it is set.
652
997
  */
653
998
  disabled?: boolean;
654
999
  /**
655
1000
  * Make the points unmovable.
656
- * aria-readonly property is also applied.
1001
+ * The parts carry `data-readonly` while it is set.
657
1002
  */
658
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
+ */
659
1010
  externalStyles?: {
660
- userSelectNone?: boolean;
661
- cursor?: Cursor;
1011
+ cursor?: CSSProperties['cursor'];
662
1012
  };
663
1013
  /**
664
- * wheel control option for every `Point`. Shift selects the x axis.
665
- * 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.
666
1055
  *
667
- * A `Point` can override it with a `wheel` of its own.
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
668
1074
  */
669
- wheel?: InputEventOption | null;
1075
+ selectable?: boolean;
670
1076
  /**
671
- * keyboard control option for every `Point`, on the arrow keys.
672
- * If null, no event will be triggered
1077
+ * Ids of the selected points, to hold the selection yourself. Leave it out
1078
+ * and the editor keeps its own.
673
1079
  *
674
- * A `Point` can override it with a `keyboard` of its own.
1080
+ * A `Point` takes its id from its `id` prop, or generates one that lasts as
1081
+ * long as it is mounted.
675
1082
  */
676
- keyboard?: 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;
677
1088
  /**
678
1089
  * The editor renders exactly what you compose here; there is no default
679
1090
  * markup to fall back to.
@@ -696,135 +1107,151 @@ interface PointsEditorProps {
696
1107
  * Multiple Point Controller
697
1108
  */
698
1109
  declare const PointsEditor: {
699
- 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>>;
700
1111
  Background: typeof Background;
701
- 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>>;
702
1113
  Point: typeof Point;
1114
+ SelectionBox: typeof SelectionBox;
703
1115
  };
704
1116
  //#endregion
705
- //#region src/components/Slider/type.d.ts
706
- type MarksType = 'mark' | 'mark-number' | 'number';
707
- type MarksOptions = ['step', MarksType] | [number, MarksType];
708
- //#endregion
709
- //#region src/components/Slider/Marks.d.ts
710
- interface MarksProps {
711
- gap?: number | string;
712
- options?: MarksOptions;
713
- children?: ReactNode;
714
- }
715
- declare function Marks({
716
- gap,
717
- options,
718
- children,
719
- className,
720
- style,
721
- ...props
722
- }: MarksProps & Omit<ComponentPropsWithoutRef<'div'>, keyof MarksProps>): _$react.JSX.Element;
723
- //#endregion
724
- //#region src/components/Slider/MarksOption.d.ts
725
- interface MarksOptionProps {
726
- value: number;
727
- type?: MarksType;
728
- /** Display text instead of value. */
729
- label?: string;
730
- /** mark thickness */
731
- thickness?: number | string;
732
- /** mark length */
733
- length?: number | string;
734
- /** Gap between mark and label. */
735
- gap?: number | string;
736
- labelWidth?: number | string;
737
- classes?: {
738
- mark?: string;
739
- label?: string;
740
- };
741
- styles?: {
742
- mark?: CSSProperties;
743
- label?: CSSProperties;
744
- };
745
- }
746
- declare function MarksOption({
747
- value,
748
- type,
749
- label,
750
- thickness,
751
- length,
752
- gap,
753
- labelWidth,
754
- classes,
755
- styles,
756
- className,
757
- style,
758
- ...props
759
- }: MarksOptionProps & Omit<ComponentPropsWithoutRef<'div'>, keyof MarksOptionProps>): _$react.JSX.Element;
760
- //#endregion
761
1117
  //#region src/components/Slider/Thumb.d.ts
762
1118
  interface SliderThumbProps {
763
1119
  /**
764
- * Size comes from the `--thumb-size` CSS variable on `Slider.Root`, so that
765
- * the root can reserve the matching amount of space around the track.
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.
766
1123
  */
767
1124
  color?: string;
768
1125
  className?: string;
769
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
+ */
770
1132
  children?: ReactNode;
771
- ref?: Ref<SliderThumbMethods>;
1133
+ 'aria-label'?: AriaAttributes['aria-label'];
1134
+ 'aria-labelledby'?: AriaAttributes['aria-labelledby'];
1135
+ 'aria-describedby'?: AriaAttributes['aria-describedby'];
1136
+ 'aria-valuetext'?: AriaAttributes['aria-valuetext'];
772
1137
  }
773
1138
  interface SliderThumbMethods {
774
1139
  focus: () => void;
775
1140
  blur: () => void;
776
1141
  }
777
- declare function Thumb$1({
778
- color,
779
- children,
780
- className,
781
- style,
782
- ref
783
- }: SliderThumbProps): _$react.JSX.Element;
784
1142
  //#endregion
785
1143
  //#region src/components/Slider/Track.d.ts
786
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
+ */
787
1149
  length?: number | string;
1150
+ /** How thick the track is across that axis. Sets `--thickness`. */
788
1151
  thickness?: number | string;
1152
+ /** Colour of the part from `min` to the value. Sets `--active`. */
789
1153
  active?: string;
1154
+ /** Colour of the rest of the track. Sets `--inactive`. */
790
1155
  inactive?: string;
791
- defaultStyle?: boolean;
792
1156
  className?: string;
793
1157
  style?: CSSProperties;
794
1158
  /** `<Slider.Thumb />` goes here. */
795
1159
  children?: ReactNode;
796
- ref?: Ref<HTMLDivElement>;
797
1160
  }
798
- declare function Track({
799
- length,
800
- thickness,
801
- active,
802
- inactive,
803
- children,
804
- className,
805
- style,
806
- defaultStyle,
807
- ref,
808
- ...props
809
- }: SliderTrackProps & Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>): _$react.JSX.Element;
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;
810
1233
  //#endregion
811
1234
  //#region src/components/Slider/context.d.ts
812
1235
  type SliderContextValue = {
813
1236
  value: number;
814
1237
  min: number;
815
1238
  max: number;
816
- step: number; /** How the value is distributed across the travel. */
1239
+ step: number;
1240
+ /** How the value is distributed across the travel. */
817
1241
  scale: Scale;
818
1242
  vertical: boolean;
819
1243
  reverse: boolean;
820
1244
  disabled: boolean;
821
1245
  readonly: boolean;
1246
+ onChange?: (value: number) => void;
822
1247
  /**
823
1248
  * Position of the thumb, 0-100, already accounting for the display direction
824
1249
  * implied by `vertical` and `reverse`.
825
1250
  */
826
- percent: number; /** `Track` registers its element here; `Root` normalizes the pointer against it. */
827
- trackRef: RefObject<HTMLDivElement | null>; /** `Thumb` registers itself here; `Root` focuses it when a drag starts. */
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. */
828
1255
  thumbRef: RefObject<SliderThumbMethods | null>;
829
1256
  };
830
1257
  /**
@@ -836,9 +1263,18 @@ declare function useSliderContext<T>(selector: (state: SliderContextValue) => T)
836
1263
  //#endregion
837
1264
  //#region src/components/Slider/index.d.ts
838
1265
  interface SliderProps {
1266
+ /** The current value. The slider shows only this, so update it from `onChange`. */
839
1267
  value: number;
1268
+ /** The value at the start of the travel. */
840
1269
  min: number;
1270
+ /** The value at the end of the travel. */
841
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
+ */
842
1278
  step?: number;
843
1279
  /**
844
1280
  * How the value is distributed across the travel.
@@ -851,41 +1287,90 @@ interface SliderProps {
851
1287
  */
852
1288
  scale?: Scale;
853
1289
  /**
854
- * slider orientation
855
- * aria-orientation property is also applied.
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
856
1294
  */
857
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
+ */
858
1302
  reverse?: boolean;
859
- /** 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
+ */
860
1309
  externalStyles?: {
861
- userSelectNone?: boolean;
862
- cursor?: Cursor;
1310
+ cursor?: CSSProperties['cursor'];
863
1311
  };
864
1312
  /**
865
- * wheel control option
866
- * 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 }
867
1340
  */
868
- wheel?: InputEventOption | null;
1341
+ dragSensitivity?: ModifierValue<number>;
869
1342
  /**
870
- * keyboard control option
871
- * 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] }
872
1355
  */
873
- keyboard?: InputEventOption | null;
1356
+ keyboard?: ModifierValue<InputEventOption> | null;
874
1357
  /**
875
- * Only the appearance will change.
876
- * Please consider using with readonly.
877
- * 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.
878
1360
  */
879
1361
  disabled?: boolean;
880
1362
  /**
881
1363
  * Make the value unchangeable.
882
- * aria-readonly property is also applied.
1364
+ * The parts carry `data-readonly` while it is set.
883
1365
  */
884
1366
  readonly?: boolean;
885
1367
  className?: string;
886
1368
  style?: CSSProperties;
1369
+ /** Called with the new value when a drag, the wheel or an arrow key moves it. */
887
1370
  onChange?: (value: number) => void;
1371
+ /** Called when a drag starts, with the value where the track was pressed. */
888
1372
  onDragStart?: (value: number) => void;
1373
+ /** Called when the drag ends, with the value it ended on. */
889
1374
  onDragEnd?: (value: number) => void;
890
1375
  /**
891
1376
  * The slider renders exactly what you compose here; there is no default
@@ -894,7 +1379,7 @@ interface SliderProps {
894
1379
  * @example
895
1380
  * <Slider.Root value={value} min={0} max={100} onChange={setValue}>
896
1381
  * <Slider.Track>
897
- * <Slider.Thumb />
1382
+ * <Slider.Thumb aria-label="Level" />
898
1383
  * </Slider.Track>
899
1384
  * </Slider.Root>
900
1385
  */
@@ -909,61 +1394,90 @@ interface SliderMethods {
909
1394
  */
910
1395
  declare const Slider: {
911
1396
  Root: React.ForwardRefExoticComponent<SliderProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof SliderProps> & React.RefAttributes<SliderMethods>>;
912
- Thumb: typeof Thumb$1;
913
- Track: typeof Track;
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>>;
914
1399
  Marks: typeof Marks;
915
1400
  MarksOption: typeof MarksOption;
916
1401
  };
917
1402
  //#endregion
918
- //#region src/components/XYPad/Area.d.ts
919
- interface XYPadAreaProps {
920
- width?: number | string;
921
- height?: number | string;
922
- color?: string;
923
- className?: string;
924
- style?: CSSProperties;
925
- /** `<XYPad.Thumb />` goes here. */
926
- children?: ReactNode;
927
- ref?: Ref<HTMLDivElement>;
928
- }
929
- declare function Area({
930
- width,
931
- height,
932
- color,
933
- children,
934
- className,
935
- style,
936
- ref,
937
- ...props
938
- }: 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;
939
1430
  //#endregion
940
1431
  //#region src/components/XYPad/Thumb.d.ts
941
1432
  interface XYPadThumbProps {
942
1433
  /**
943
- * Size comes from the `--thumb-size` CSS variable on `XYPad.Root`, so that
944
- * the root can reserve the matching amount of space around the area.
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.
945
1437
  */
946
1438
  color?: string;
947
- wrapperClassName?: string;
948
- 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
+ */
949
1446
  children?: ReactNode;
950
- ref?: Ref<XYPadThumbMethods>;
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']>;
951
1462
  }
952
1463
  interface XYPadThumbMethods {
953
1464
  focus: () => void;
954
1465
  blur: () => void;
955
1466
  }
956
- type Props = XYPadThumbProps & Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadThumbProps>;
957
- declare function Thumb({
958
- color,
959
- children,
960
- wrapperClassName,
961
- wrapperStyle,
962
- className,
963
- style,
964
- ref,
965
- ...props
966
- }: Props): _$react.JSX.Element;
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
+ }
967
1481
  //#endregion
968
1482
  //#region src/components/XYPad/index.d.ts
969
1483
  /**
@@ -973,12 +1487,22 @@ declare function Thumb({
973
1487
  * value applies to both axes.
974
1488
  */
975
1489
  interface XYPadProps {
1490
+ /** The current value as `[x, y]`. The pad shows only this, so update it from `onChange`. */
976
1491
  value: XY<number>;
1492
+ /** The value at the start of each axis, as `[x, y]` or one number for both. */
977
1493
  min: XYInput<number>;
1494
+ /** The value at the end of each axis, as `[x, y]` or one number for both. */
978
1495
  max: XYInput<number>;
1496
+ /**
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
1501
+ */
979
1502
  step?: XYInput<number>;
980
1503
  /**
981
- * How the value of each axis is distributed across the travel.
1504
+ * How the value of each axis is distributed across the travel, as `[x, y]`
1505
+ * or one scale for both.
982
1506
  *
983
1507
  * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,
984
1508
  * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or
@@ -987,25 +1511,83 @@ interface XYPadProps {
987
1511
  * @default linearScale
988
1512
  */
989
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
+ */
990
1520
  reverse?: XYInput<boolean>;
991
1521
  /**
992
- * wheel control option. Shift selects the x axis.
993
- * If null, no event will be triggered
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] }
994
1565
  */
995
- wheel?: InputEventOption | null;
1566
+ keyboard?: ModifierValue<InputEventOption> | null;
996
1567
  /**
997
- * keyboard control option
998
- * If null, no event will be triggered
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' }
999
1572
  */
1000
- keyboard?: InputEventOption | null;
1001
1573
  externalStyles?: {
1002
- userSelectNone?: boolean;
1003
- cursor?: Cursor;
1574
+ cursor?: CSSProperties['cursor'];
1004
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
+ */
1005
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
+ */
1006
1585
  readonly?: boolean;
1586
+ /** Called with the new value when a drag, the wheel or an arrow key moves it. */
1007
1587
  onChange?: (value: XY<number>) => void;
1588
+ /** Called when a drag starts, with the value where the area was pressed. */
1008
1589
  onDragStart?: (value: XY<number>) => void;
1590
+ /** Called when the drag ends, with the value it ended on. */
1009
1591
  onDragEnd?: (value: XY<number>) => void;
1010
1592
  /**
1011
1593
  * The pad renders exactly what you compose here; there is no default
@@ -1029,9 +1611,9 @@ interface XYPadMethods {
1029
1611
  * Simple XYPad
1030
1612
  */
1031
1613
  declare const XYPad: {
1032
- Root: _$react.ForwardRefExoticComponent<XYPadProps & Omit<Omit<_$react.DetailedHTMLProps<_$react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof XYPadProps> & _$react.RefAttributes<XYPadMethods>>;
1033
- Thumb: typeof Thumb;
1034
- 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>>;
1035
1617
  };
1036
1618
  //#endregion
1037
1619
  //#region src/hooks/useAnimationFrame.d.ts
@@ -1046,22 +1628,28 @@ interface UseDragProps {
1046
1628
  threshold?: number;
1047
1629
  /** CSS cursor to show while dragging. Applied to the element itself. */
1048
1630
  cursor?: string;
1049
- onDrag?: (x: number, y: number, deltaX: number, deltaY: number) => void;
1050
- onDragStart?: () => void;
1051
- 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;
1052
1646
  }
1053
1647
  /**
1054
1648
  * Track a pointer drag on an element.
1055
1649
  *
1056
1650
  * @returns a ref callback to attach to the element being dragged
1057
1651
  */
1058
- declare function useDrag<T extends Element>({
1059
- threshold,
1060
- cursor,
1061
- onDrag,
1062
- onDragStart,
1063
- onDragEnd
1064
- }: UseDragProps): (node: T | null) => void;
1652
+ declare function useDrag<T extends Element>({ threshold, cursor, pointerLock, onDrag, onDragStart, onDragEnd }: UseDragProps): (node: T | null) => void;
1065
1653
  //#endregion
1066
1654
  //#region src/hooks/useDragValue.d.ts
1067
1655
  interface UseDragValueOptions {
@@ -1086,15 +1674,48 @@ interface UseDragValueOptions {
1086
1674
  * @default 100
1087
1675
  */
1088
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
1688
+ */
1689
+ sensitivity?: (state: DragState) => number;
1089
1690
  /** @see DragValueOptions.updateOnPointerDown */
1090
1691
  updateOnPointerDown?: boolean;
1091
1692
  /** @default 0 */
1092
1693
  threshold?: number;
1093
1694
  /** CSS cursor to show while dragging. Applied to the element itself. */
1094
1695
  cursor?: string;
1095
- onChange?: (value: XY$1<number>) => void;
1096
- onDragStart?: (value: XY$1<number>) => void;
1097
- onDragEnd?: (value: XY$1<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;
1098
1719
  }
1099
1720
  /**
1100
1721
  * Drive a value with a pointer drag.
@@ -1117,27 +1738,53 @@ declare function useEventListener<K extends keyof GlobalEventHandlersEventMap>(t
1117
1738
  declare function useInterval(callback: () => void, delay: number | null): void;
1118
1739
  //#endregion
1119
1740
  //#region src/hooks/useLongPress.d.ts
1120
- 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;
1121
1742
  //#endregion
1122
1743
  //#region src/hooks/useMIDIAccess.d.ts
1123
1744
  /**
1124
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.
1125
1753
  */
1126
1754
  declare function useMIDIAccess(requestOnMount?: boolean): {
1127
- request: () => void;
1755
+ request: (options?: MIDIAccessOptions$1) => void;
1128
1756
  midiAccess: MIDIAccess | null;
1129
- error: _$_tremolo_ui_dom0.MIDIAccessError | null;
1757
+ error: import("@tremolo-ui/dom").MIDIAccessError | null;
1758
+ inputs: readonly MIDIInput[];
1130
1759
  };
1131
1760
  //#endregion
1132
1761
  //#region src/hooks/useMIDIInput.d.ts
1133
1762
  /**
1134
- * 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
+ * })
1135
1778
  */
1136
- 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;
1137
1780
  //#endregion
1138
1781
  //#region src/hooks/useMIDIMessage.d.ts
1139
1782
  /**
1140
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.
1141
1788
  */
1142
1789
  declare function useMIDIMessage(midiAccess: MIDIAccess | null, onMIDIMessage: (event: MIDIMessageEvent) => void): void;
1143
1790
  //#endregion
@@ -1164,10 +1811,31 @@ interface UseWheelOptions extends WheelOptions {
1164
1811
  *
1165
1812
  * @returns a ref callback to attach to the element, unused when `target` is given
1166
1813
  */
1167
- declare function useWheel<T extends Element>(onWheel: (event: WheelEvent) => void, {
1168
- requireFocus,
1169
- target
1170
- }?: UseWheelOptions): (node: T | null) => void;
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.
1837
+ */
1838
+ declare const DEFAULT_DRAG_SENSITIVITY: ModifierValue<number>;
1171
1839
  //#endregion
1172
- export { type AbsoluteSizingProps, AnimationCanvas, type AnimationCanvasProps, type CSSVariables, type CommonProps, type DecrementStepperProps, type DrawFunction, type IncrementStepperProps, type InitFunction, type KeyAttributes, type KeyState, type KeyboardShortcuts, Knob, type KnobMethods, type KnobProps, type MIDIAccessError, type MarksOptionProps, type MarksProps, NOT_SUPPORTED, NumberInput, type NumberInputFieldProps, type NumberInputMethods, type NumberInputProps, PERMISSION_DENIED, Piano, type PianoMethods, type PianoProps, type PointBaseType, type PointProps, PointsEditor, type PointsEditorBackgroundProps, type PointsEditorContainerProps, type PointsEditorContextValue, type PointsEditorProps, type RelativeSizingProps, SHORTCUTS, Slider, type SliderMethods, type SliderProps, type SliderThumbMethods, type SliderThumbProps, type SliderTrackProps, type StepperProps, type UseDragValueOptions, type UseWheelOptions, type XY, type XYInput, XYPad, type XYPadAreaProps, type XYPadMethods, type XYPadProps, type XYPadThumbMethods, type XYPadThumbProps, clampPoint, useAnimationFrame, useDrag, useDragValue, useEventListener, useInterval, useLongPress, useMIDIAccess, useMIDIInput, useMIDIMessage, useNumberInputContext, usePointsEditorContext, useSliderContext, useWheel };
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 };
1173
1841
  //# sourceMappingURL=index.d.cts.map