@tremolo-ui/functions 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.
- package/dist/index.cjs +330 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +163 -42
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +163 -42
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +325 -94
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +20 -3
- package/src/math.ts +53 -5
- package/src/midi.ts +35 -15
- package/src/modifiers.ts +141 -0
- package/src/piano.ts +35 -21
- package/src/scales.ts +79 -25
- package/src/unit.ts +182 -51
- package/src/util.ts +1 -36
- package/src/types.ts +0 -4
package/dist/index.d.ts
CHANGED
|
@@ -16,8 +16,45 @@ declare function normalizeValue(value: number, min: number, max: number): number
|
|
|
16
16
|
* The inverse of {@link normalizeValue}.
|
|
17
17
|
*/
|
|
18
18
|
declare function rawValue(normalizedValue: number, min: number, max: number): number;
|
|
19
|
+
/**
|
|
20
|
+
* Put a value on the grid the caller asked for, rounding a half step upwards.
|
|
21
|
+
*
|
|
22
|
+
* The rounding is done on the quotient rather than by comparing the distance
|
|
23
|
+
* to the two neighbours, because both of those carry error of their own. The
|
|
24
|
+
* quotient is cleared of its artefact first: `0.15 / 0.1` is 1.4999999999999998,
|
|
25
|
+
* and a value sitting exactly on a half step would otherwise fall to whichever
|
|
26
|
+
* side the last bit happened to land on — 0.25 rounded up while 0.15 and 0.35
|
|
27
|
+
* rounded down.
|
|
28
|
+
*/
|
|
19
29
|
declare function stepValue(value: number, step: number): number;
|
|
20
30
|
declare function toFixed(x: number, fractionDigits?: number): number;
|
|
31
|
+
/**
|
|
32
|
+
* The significant decimal digits a double actually carries. A double holds a
|
|
33
|
+
* little under 16, so anything past this is the binary representation showing
|
|
34
|
+
* through rather than information.
|
|
35
|
+
*/
|
|
36
|
+
declare const SIGNIFICANT_DIGITS = 15;
|
|
37
|
+
/**
|
|
38
|
+
* Drop the binary artefact from a computed value.
|
|
39
|
+
*
|
|
40
|
+
* Arithmetic on doubles leaves debris in the last couple of digits, and it
|
|
41
|
+
* accumulates: adding 0.1 to 5 twelve times gives 5.699999999999998 rather
|
|
42
|
+
* than 5.7, and the display of a control shows exactly that. Rounding to the
|
|
43
|
+
* digits a double can carry removes it, and adds nothing back — the value was
|
|
44
|
+
* already the result of a calculation whose own error is that size or larger.
|
|
45
|
+
*
|
|
46
|
+
* This is not the same as rounding to a `step`. {@link stepValue} puts a value
|
|
47
|
+
* on a grid the caller asked for and is a decision about the value; this only
|
|
48
|
+
* removes what was never in the value to begin with.
|
|
49
|
+
*
|
|
50
|
+
* @param significantDigits how many digits to keep. The default is the only
|
|
51
|
+
* one that is purely artefact removal; a smaller number starts discarding real
|
|
52
|
+
* precision.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* toPrecision(5.1 + 0.1) // 5.2, rather than 5.199999999999999
|
|
56
|
+
*/
|
|
57
|
+
declare function toPrecision(x: number, significantDigits?: number): number;
|
|
21
58
|
declare function integerPart(x: number | string): string | undefined;
|
|
22
59
|
declare function decimalPart(x: number | string): string | undefined;
|
|
23
60
|
declare function radian(degree: number): number;
|
|
@@ -26,11 +63,76 @@ declare function mapValue(value: number, inMin: number, inMax: number, outMin: n
|
|
|
26
63
|
declare function dbToGain(db: number): number;
|
|
27
64
|
declare function gainToDb(gain: number): number;
|
|
28
65
|
//#endregion
|
|
29
|
-
//#region src/
|
|
66
|
+
//#region src/modifiers.d.ts
|
|
30
67
|
/**
|
|
31
68
|
* Options for setting the amount of keyboard and mouse wheel changes.
|
|
32
69
|
*/
|
|
33
|
-
type InputEventOption = ['normalized' | 'raw', number];
|
|
70
|
+
type InputEventOption = readonly ['normalized' | 'raw', number];
|
|
71
|
+
/**
|
|
72
|
+
* A modifier key that can carry an amount of its own.
|
|
73
|
+
*
|
|
74
|
+
* `ctrl` and `meta` are kept apart rather than folded into one "command" key:
|
|
75
|
+
* a plugin UI that mirrors a desktop host usually wants the same physical key
|
|
76
|
+
* on every platform, not the platform's own convention.
|
|
77
|
+
*/
|
|
78
|
+
type Modifier = 'shift' | 'alt' | 'ctrl' | 'meta';
|
|
79
|
+
/** The modifier flags of a `WheelEvent` or a `KeyboardEvent`. */
|
|
80
|
+
interface ModifierState {
|
|
81
|
+
shiftKey: boolean;
|
|
82
|
+
altKey: boolean;
|
|
83
|
+
ctrlKey: boolean;
|
|
84
|
+
metaKey: boolean;
|
|
85
|
+
}
|
|
86
|
+
/** One setting per modifier key, with `default` for none of them. */
|
|
87
|
+
type ModifierSetting = number | InputEventOption;
|
|
88
|
+
type ModifierMap<T extends ModifierSetting> = {
|
|
89
|
+
default: T;
|
|
90
|
+
} & Partial<Record<Modifier, T>>;
|
|
91
|
+
/**
|
|
92
|
+
* A single setting, or one per modifier key.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ['raw', 1]
|
|
96
|
+
* { default: ['raw', 1], shift: ['raw', 0.1] }
|
|
97
|
+
*/
|
|
98
|
+
type ModifierValue<T extends ModifierSetting> = T | ModifierMap<T>;
|
|
99
|
+
interface SelectedInputEvent {
|
|
100
|
+
option: InputEventOption;
|
|
101
|
+
/** Which modifier entry was chosen, or `null` for `default`. */
|
|
102
|
+
modifier: Modifier | null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Pick the setting that applies, given the modifier keys being held.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* selectModifier({ default: 1, shift: 0.1 }, event)
|
|
109
|
+
*/
|
|
110
|
+
declare function selectModifier<T extends ModifierSetting>(options: ModifierValue<T>, modifiers?: ModifierState): {
|
|
111
|
+
value: T;
|
|
112
|
+
modifier: Modifier | null;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Turn every entry of a setting into another kind of setting, keeping which
|
|
116
|
+
* modifier each belongs to.
|
|
117
|
+
*
|
|
118
|
+
* A drag sensitivity is a number and a keyboard amount is a tuple, but the two
|
|
119
|
+
* describe the same thing from the caller's side. This carries one over to the
|
|
120
|
+
* other so that a component can hand a sensitivity to {@link applyDelta}
|
|
121
|
+
* without unpicking the modifier map itself — which matters, since naming a
|
|
122
|
+
* modifier is also what takes `step` out of the pipeline.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* mapModifier({ default: 1, shift: 0.1 }, (f) => ['raw', step * f])
|
|
126
|
+
* // { default: ['raw', 1], shift: ['raw', 0.1] }
|
|
127
|
+
*/
|
|
128
|
+
declare function mapModifier<T extends ModifierSetting, U extends ModifierSetting>(options: ModifierValue<T>, fn: (value: T) => U): ModifierValue<U>;
|
|
129
|
+
/**
|
|
130
|
+
* Pick the amount that applies, given the modifier keys being held.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* selectInputEvent({ default: ['raw', 1], shift: ['raw', 0.1] }, event)
|
|
134
|
+
*/
|
|
135
|
+
declare function selectInputEvent(options: ModifierValue<InputEventOption>, modifiers?: ModifierState): SelectedInputEvent;
|
|
34
136
|
//#endregion
|
|
35
137
|
//#region src/scales.d.ts
|
|
36
138
|
/**
|
|
@@ -166,16 +268,18 @@ interface ValueRange {
|
|
|
166
268
|
* @param direction which way, and how many times, to apply the option. The
|
|
167
269
|
* size of one step is `option[1]`, so this is normally `1` or `-1`.
|
|
168
270
|
*
|
|
271
|
+
* @param modifiers the event, for `options` that name a modifier key. See
|
|
272
|
+
* {@link selectInputEvent}.
|
|
273
|
+
*
|
|
169
274
|
* @example
|
|
170
275
|
* // ArrowDown on a slider whose keyboard option is ['raw', 1]
|
|
171
276
|
* applyDelta(value, -1, keyboard, { min, max, step, scale })
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* // Shift+ArrowDown, where `keyboard` is { default: …, shift: ['raw', 0.1] }
|
|
280
|
+
* applyDelta(value, -1, keyboard, range, event)
|
|
172
281
|
*/
|
|
173
|
-
declare function applyDelta(value: number, direction: number,
|
|
174
|
-
min,
|
|
175
|
-
max,
|
|
176
|
-
step,
|
|
177
|
-
scale
|
|
178
|
-
}: ValueRange): number;
|
|
282
|
+
declare function applyDelta(value: number, direction: number, options: ModifierValue<InputEventOption>, { min, max, step, scale }: ValueRange, modifiers?: ModifierState): number;
|
|
179
283
|
//#endregion
|
|
180
284
|
//#region src/midi.d.ts
|
|
181
285
|
declare const whiteKeys: readonly ["A", "B", "C", "D", "E", "F", "G"];
|
|
@@ -341,52 +445,69 @@ declare function noteAt(x: number, y: number, height: number, layout: PianoLayou
|
|
|
341
445
|
//#endregion
|
|
342
446
|
//#region src/unit.d.ts
|
|
343
447
|
/**
|
|
344
|
-
*
|
|
345
|
-
* the smallest scale up. A value is shown in the largest unit that does not
|
|
346
|
-
* exceed it.
|
|
448
|
+
* The SI prefixes {@link unitFormat} chooses between.
|
|
347
449
|
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*/
|
|
352
|
-
type Units = [string, number][];
|
|
353
|
-
/**
|
|
354
|
-
* Pick the unit a value is displayed in: the largest one whose scale does not
|
|
355
|
-
* exceed the magnitude of the value.
|
|
450
|
+
* Deliberately narrower than the full SI set: yocto through yotta are of no
|
|
451
|
+
* use to an audio control, and every extra prefix is one more symbol `parse`
|
|
452
|
+
* has to tell apart from a unit.
|
|
356
453
|
*/
|
|
357
|
-
|
|
454
|
+
type SIPrefix = 'p' | 'n' | 'µ' | 'm' | '' | 'k' | 'M' | 'G';
|
|
455
|
+
interface UnitFormatOptions {
|
|
456
|
+
/**
|
|
457
|
+
* The prefix the stored value is already in.
|
|
458
|
+
*
|
|
459
|
+
* A control that keeps milliseconds in `value` is `{ base: 'm' }` with a
|
|
460
|
+
* unit of `'s'`: 1500 then displays as `1.5s`, and `parse` gives 1500 back.
|
|
461
|
+
*
|
|
462
|
+
* @default ''
|
|
463
|
+
*/
|
|
464
|
+
base?: SIPrefix;
|
|
465
|
+
/**
|
|
466
|
+
* Whether to scale the number and pick a prefix at all.
|
|
467
|
+
*
|
|
468
|
+
* Turn it off for anything that is not an SI quantity. dB, %, cents and
|
|
469
|
+
* semitones do not take prefixes, and `-6dB` read as "-6 deci-B" is wrong
|
|
470
|
+
* rather than merely unusual.
|
|
471
|
+
*
|
|
472
|
+
* @default true
|
|
473
|
+
*/
|
|
474
|
+
prefixes?: boolean;
|
|
475
|
+
/**
|
|
476
|
+
* Digits after the decimal point. The number is left as-is when omitted.
|
|
477
|
+
*/
|
|
478
|
+
digits?: number;
|
|
479
|
+
/**
|
|
480
|
+
* Text placed between the number and the unit.
|
|
481
|
+
* @default ''
|
|
482
|
+
*/
|
|
483
|
+
separator?: string;
|
|
484
|
+
}
|
|
485
|
+
/** The `format` / `parse` pair a `NumberInput` takes. */
|
|
486
|
+
interface UnitFormatter {
|
|
487
|
+
format: (value: number) => string;
|
|
488
|
+
parse: (text: string) => number;
|
|
489
|
+
}
|
|
358
490
|
/**
|
|
359
|
-
*
|
|
491
|
+
* Build the `format` and `parse` of a unit, as one pair.
|
|
360
492
|
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
493
|
+
* They are returned together because they have to agree: a `format` that
|
|
494
|
+
* writes `1.23kHz` is only useful next to a `parse` that reads it back as
|
|
495
|
+
* 1230. Spread the result into the input.
|
|
363
496
|
*
|
|
364
497
|
* @example
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Read a value back out of text, undoing the scaling of {@link formatValue}.
|
|
371
|
-
*
|
|
372
|
-
* With a list of units the text has to be a number followed by an optional
|
|
373
|
-
* unit and nothing else, since the unit decides the scale; anything else reads
|
|
374
|
-
* as 0. With a single unit, or none, the first number found anywhere in the
|
|
375
|
-
* text is taken, so a half-typed entry still yields something.
|
|
498
|
+
* unitFormat('Hz') // 1234 -> '1.23kHz'
|
|
499
|
+
* unitFormat('s', { base: 'm' }) // value in ms. 1500 -> '1.5s'
|
|
500
|
+
* unitFormat('s', { base: 'm', digits: 2 }) // 1500 -> '1.50s'
|
|
501
|
+
* unitFormat('dB', { prefixes: false, digits: 1 }) // -6.25 -> '-6.3dB'
|
|
376
502
|
*
|
|
377
503
|
* @example
|
|
378
|
-
*
|
|
379
|
-
* parseValue('4abc') // 4
|
|
504
|
+
* <NumberInput.Root {...unitFormat('Hz', { digits: 2 })} value={v} onChange={setV}>
|
|
380
505
|
*/
|
|
381
|
-
declare function
|
|
506
|
+
declare function unitFormat(unit: string, options?: UnitFormatOptions): UnitFormatter;
|
|
382
507
|
//#endregion
|
|
383
508
|
//#region src/util.d.ts
|
|
384
|
-
type Operator = '+' | '-' | '*' | '/';
|
|
385
|
-
declare function styleHelper(value: string | number): string;
|
|
386
|
-
declare function styleHelper(value: string | number, op: Operator, influencer?: number): string;
|
|
387
|
-
declare function isEmpty(obj: object): boolean;
|
|
388
509
|
declare function mod(n: number, m: number): number;
|
|
389
510
|
declare function xor(a?: boolean, b?: boolean): boolean;
|
|
390
511
|
//#endregion
|
|
391
|
-
export { type InputEventOption, type NoteKey, type NoteRange, type PianoLayout, type Scale, type ScaleName, type
|
|
512
|
+
export { type InputEventOption, type Modifier, type ModifierMap, type ModifierState, type ModifierValue, type NoteKey, type NoteRange, type PianoLayout, SIGNIFICANT_DIGITS, type SIPrefix, type Scale, type ScaleName, type SelectedInputEvent, type UnitFormatOptions, type UnitFormatter, type ValueRange, type WhiteKey, applyDelta, blackKeyWidth, clamp, curveScale, curveWithCenterValue, dbToGain, decimalPart, degree, exponentialScale, gainToDb, getNoteRangeArray, inScale, integerPart, isBlackKey, isWhiteKey, linearScale, mapModifier, mapValue, mod, normalizeValue, noteAt, noteKey, noteKeys, noteName, noteNumber, notePosition, noteToFrequency, parseNoteName, pianoWidth, radian, rawValue, scaleIntervals, scaleNotes, selectInputEvent, selectModifier, skewScale, skewWithCenterValue, stepValue, symmetricSkewScale, toFixed, toPrecision, unitFormat, whiteKeys, xor };
|
|
392
513
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/math.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/math.ts","../src/modifiers.ts","../src/scales.ts","../src/midi.ts","../src/piano.ts","../src/unit.ts","../src/util.ts"],"mappings":";;;;iBAGgB,MAAM,eAAe,aAAa;;;;;;;iBAUlC,eAAe,eAAe,aAAa;;;;;;iBAU3C,SAAS,yBAAyB,aAAa;;;;;;;;;;;iBAe/C,UAAU,eAAe;iBAUzB,QAAQ,WAAW;;;;;;cAStB;;;;;;;;;;;;;;;;;;;;;iBAsBG,YAAY,WAAW;iBAUvB,YAAY;iBAOZ,YAAY;iBAIZ,OAAO;iBAIP,OAAO;iBAIP,SACd,eACA,eACA,eACA,gBACA;iBAKc,SAAS;iBAIT,SAAS;;;;;;KCvHb;;;;;;;;KASA;;UAGK;EACf;EACA;EACA;EACA;;;KAIG,2BAA2B;KAEpB,YAAY,UAAU;EAAqB,SAAS;IAAM,QACpE,OAAO,UAAU;;;;;;;;KAUP,cAAc,UAAU,mBAAmB,IAAI,YAAY;UAEtD;EACf,QAAQ;;EAER,UAAU;;;;;;;;iBAsCI,eAAe,UAAU,iBACvC,SAAS,cAAc,IACvB,YAAY;EACT,OAAO;EAAG,UAAU;;;;;;;;;;;;;;;;iBAiCT,YACd,UAAU,iBACV,UAAU,iBACV,SAAS,cAAc,IAAI,KAAK,OAAO,MAAM,IAAI,cAAc;;;;;;;iBAgBjD,iBACd,SAAS,cAAc,mBACvB,YAAY,gBACX;;;;;;;;;;;;;;;;;;UClHc;;EAEf,YAAY,eAAe,aAAa;;EAExC,cAAc,kBAAkB,aAAa;;;;;;;;cAmBlC,aAAa;;;;;;;;;;;;;;;;;iBAqBV,UAAU,eAAe;;;;;iBAsBzB,oBACd,qBACA,aACA;;;;;;;;;;;;cAmBW,kBAAkB;;;;;;;;;;;;;;;;;;;;;;iBAqDf,WAAW,gBAAgB;;;;;;;;;;;;;iBAmD3B,mBAAmB,eAAe;;;;;iBAiClC,qBACd,qBACA,aACA;;;;;;;;UAkBe;EACf;EACA;;;;EAIA;;;;;;EAMA,QAAQ;;;;;;;;;;;;;;;;;;;;;;;iBAwBM,WACd,eACA,mBACA,SAAS,cAAc,qBACrB,KAAK,KAAK,MAAM,SAAuB,YACzC,YAAY;;;cCpTD;KAED,mBAAmB;cAElB;KAeD,kBAAkB;iBAQd,cAAc;EAOY,QAAA;EACZ;;;;;;iBAQd,WAAW;;;;;;;iBAeX,SAAS,wBAAwB;;;;iBAUjC,QAAQ,qBAAqB;;;;iBAQ7B,WAAW;;;;iBAiBX,WAAW;;;;;;;iBAUX,gBAAgB,uBAAuB,iBAAY;;;;;;;;;;;;cAiBtD;;;;;;;;;;;;;;;;;;KAsBD,yBAAyB;;;;;;;;;;;;iBAarB,QACd,uBACA,uBACA,MAAM;;;;;;;;;;;;;;;iBAuBQ,WACd,uBACA,MAAM,WACN;;;KCxLU;EACV;EACA;;;;;iBAMc,kBAAkB,WAAW;;;;;;;UAa5B;EACf,WAAW;;EAGX;;;;;;;EAQA;;;;;;EAOA;;;;;;EAOA;;;iBA8Bc,cAAc,QAAQ;;iBA0CtB,WAAW,QAAQ;;;;;;;;iBAYnB,aAAa,cAAc,QAAQ;;;;;;;;;;;;iBAenC,OACd,WACA,WACA,gBACA,QAAQ;;;;;;;;;;KC/IE;UAwBK;;;;;;;;;EASf,OAAO;;;;;;;;;;EAUP;;;;EAIA;;;;;EAKA;;;UAIe;EACf,SAAS;EACT,QAAQ;;;;;;;;;;;;;;;;;;iBAiCM,WACd,cACA,UAAS,oBACR;;;iBCvGa,IAAI,WAAW;iBAIf,IAAI,aAAW"}
|