@tremolo-ui/react 0.6.0 → 0.7.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 +252 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +550 -133
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +550 -133
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +218 -39
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/components/DropZone/index.tsx +100 -0
- package/src/components/FileInput/Trigger.tsx +39 -0
- package/src/components/FileInput/context.tsx +24 -0
- package/src/components/FileInput/index.tsx +144 -0
- package/src/components/Knob/index.tsx +8 -5
- package/src/components/NumberInput/DecrementStepper.tsx +2 -13
- package/src/components/NumberInput/IncrementStepper.tsx +2 -14
- package/src/components/NumberInput/InputField.tsx +5 -11
- package/src/components/NumberInput/Stepper.tsx +4 -19
- package/src/components/NumberInput/StepperButton.tsx +13 -8
- package/src/components/NumberInput/context.tsx +6 -7
- package/src/components/NumberInput/index.tsx +4 -13
- package/src/components/Piano/index.tsx +10 -13
- package/src/components/PointsEditor/Background.tsx +2 -8
- package/src/components/PointsEditor/Container.tsx +2 -8
- package/src/components/PointsEditor/Point.tsx +7 -5
- package/src/components/PointsEditor/SelectionBox.tsx +2 -8
- package/src/components/PointsEditor/context.tsx +6 -6
- package/src/components/PointsEditor/index.tsx +9 -11
- package/src/components/Slider/Marks.tsx +6 -9
- package/src/components/Slider/MarksOption.tsx +7 -2
- package/src/components/Slider/Thumb.tsx +9 -15
- package/src/components/Slider/Track.tsx +6 -13
- package/src/components/Slider/index.tsx +5 -12
- package/src/components/Slider/type.ts +4 -2
- package/src/components/XYPad/Area.tsx +5 -10
- package/src/components/XYPad/Thumb.tsx +5 -11
- package/src/components/XYPad/index.tsx +3 -5
- package/src/components/_util/VisuallyHiddenRangeInput.tsx +3 -15
- package/src/components/_util/decimal-digits.ts +23 -0
- package/src/components/_util/visually-hidden.ts +25 -0
- package/src/css-variables.ts +23 -0
- package/src/hooks/_internal/useCheckSteps.ts +2 -3
- package/src/hooks/useDrag.ts +13 -4
- package/src/hooks/useDragValue.ts +17 -2
- package/src/hooks/useDropZone.ts +78 -0
- package/src/hooks/useWheel.ts +4 -1
- package/src/index.ts +12 -9
- package/src/input-event.ts +2 -2
package/dist/index.cjs
CHANGED
|
@@ -92,6 +92,203 @@ const AnimationCanvas = /* @__PURE__ */ (0, react.forwardRef)(function Animation
|
|
|
92
92
|
});
|
|
93
93
|
});
|
|
94
94
|
//#endregion
|
|
95
|
+
//#region src/hooks/_internal/useCallbackRef.ts
|
|
96
|
+
/**
|
|
97
|
+
* Internal
|
|
98
|
+
* @private
|
|
99
|
+
*/
|
|
100
|
+
function useCallbackRef(callback, deps = []) {
|
|
101
|
+
const callbackRef = (0, react.useRef)(() => {
|
|
102
|
+
throw new Error("Cannot call an event handler while rendering.");
|
|
103
|
+
});
|
|
104
|
+
(0, react.useInsertionEffect)(() => {
|
|
105
|
+
callbackRef.current = callback;
|
|
106
|
+
});
|
|
107
|
+
return (0, react.useCallback)((...args) => callbackRef.current?.(...args), deps);
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/hooks/useDropZone.ts
|
|
111
|
+
/**
|
|
112
|
+
* Take files dropped onto an element.
|
|
113
|
+
*
|
|
114
|
+
* Any element can become a drop target this way — a waveform display, a track
|
|
115
|
+
* lane, a canvas — without wrapping it in anything.
|
|
116
|
+
*
|
|
117
|
+
* @returns a ref callback for the element, and whether a drag is over it
|
|
118
|
+
*/
|
|
119
|
+
function useDropZone({ accept, multiple, disabled, onDrop, onReject } = {}) {
|
|
120
|
+
const dropHandler = useCallbackRef(onDrop);
|
|
121
|
+
const rejectHandler = useCallbackRef(onReject);
|
|
122
|
+
const [state, setState] = (0, react.useState)({
|
|
123
|
+
over: false,
|
|
124
|
+
invalid: false
|
|
125
|
+
});
|
|
126
|
+
const [node, setNode] = (0, react.useState)(null);
|
|
127
|
+
const instanceRef = (0, react.useRef)(null);
|
|
128
|
+
const latest = (0, react.useRef)({
|
|
129
|
+
accept,
|
|
130
|
+
multiple,
|
|
131
|
+
disabled
|
|
132
|
+
});
|
|
133
|
+
(0, react.useEffect)(() => {
|
|
134
|
+
if (!node) return;
|
|
135
|
+
const instance = (0, _tremolo_ui_dom.createDropZone)(node, {
|
|
136
|
+
...latest.current,
|
|
137
|
+
onDrop: (files, event) => dropHandler(files, event),
|
|
138
|
+
onReject: (files, event) => rejectHandler(files, event),
|
|
139
|
+
onStateChange: (next) => setState(next)
|
|
140
|
+
});
|
|
141
|
+
instanceRef.current = instance;
|
|
142
|
+
return () => {
|
|
143
|
+
instanceRef.current = null;
|
|
144
|
+
instance.destroy();
|
|
145
|
+
setState({
|
|
146
|
+
over: false,
|
|
147
|
+
invalid: false
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
}, [
|
|
151
|
+
node,
|
|
152
|
+
dropHandler,
|
|
153
|
+
rejectHandler
|
|
154
|
+
]);
|
|
155
|
+
(0, react.useEffect)(() => {
|
|
156
|
+
latest.current = {
|
|
157
|
+
accept,
|
|
158
|
+
multiple,
|
|
159
|
+
disabled
|
|
160
|
+
};
|
|
161
|
+
instanceRef.current?.update({
|
|
162
|
+
accept,
|
|
163
|
+
multiple,
|
|
164
|
+
disabled
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
refCallback: setNode,
|
|
169
|
+
over: state.over,
|
|
170
|
+
invalid: state.invalid
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* An area that takes files dropped onto it.
|
|
175
|
+
*/
|
|
176
|
+
const DropZone = { Root: /* @__PURE__ */ (0, react.forwardRef)(({ accept, multiple = false, disabled = false, onDrop, onReject, children, ...props }, ref) => {
|
|
177
|
+
const { refCallback, over, invalid } = useDropZone({
|
|
178
|
+
accept,
|
|
179
|
+
multiple,
|
|
180
|
+
disabled,
|
|
181
|
+
onDrop,
|
|
182
|
+
onReject
|
|
183
|
+
});
|
|
184
|
+
const composedRef = require_compose_refs.useComposedRefs(ref, refCallback);
|
|
185
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
186
|
+
ref: composedRef,
|
|
187
|
+
"data-dragover": over ? "" : void 0,
|
|
188
|
+
"data-invalid": invalid ? "" : void 0,
|
|
189
|
+
"data-disabled": disabled ? "" : void 0,
|
|
190
|
+
...props,
|
|
191
|
+
children
|
|
192
|
+
});
|
|
193
|
+
}) };
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/components/_util/visually-hidden.ts
|
|
196
|
+
/**
|
|
197
|
+
* Take an element out of sight while leaving it in the accessibility tree and
|
|
198
|
+
* in the tab order.
|
|
199
|
+
*
|
|
200
|
+
* `display: none` and `visibility: hidden` would remove it from both, and the
|
|
201
|
+
* native control underneath a headless component is what carries the ARIA and
|
|
202
|
+
* the keyboard behaviour. `pointer-events: none` is safe because nothing is
|
|
203
|
+
* ever clicked here directly: a `<label>` forwards its click, and a drag is
|
|
204
|
+
* handled by the part that is visible.
|
|
205
|
+
*/
|
|
206
|
+
const visuallyHiddenStyle = {
|
|
207
|
+
position: "absolute",
|
|
208
|
+
width: 1,
|
|
209
|
+
height: 1,
|
|
210
|
+
padding: 0,
|
|
211
|
+
margin: -1,
|
|
212
|
+
overflow: "hidden",
|
|
213
|
+
clip: "rect(0, 0, 0, 0)",
|
|
214
|
+
clipPath: "inset(50%)",
|
|
215
|
+
whiteSpace: "nowrap",
|
|
216
|
+
border: 0,
|
|
217
|
+
pointerEvents: "none"
|
|
218
|
+
};
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region src/components/FileInput/context.tsx
|
|
221
|
+
const FileInputContext = /* @__PURE__ */ (0, react.createContext)(null);
|
|
222
|
+
const FileInputProvider = FileInputContext.Provider;
|
|
223
|
+
function useFileInputContext(selector) {
|
|
224
|
+
const context = (0, react.useContext)(FileInputContext);
|
|
225
|
+
if (!context) throw new Error("Missing FileInputContext.Provider in the tree");
|
|
226
|
+
return selector ? selector(context) : context;
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/components/FileInput/Trigger.tsx
|
|
230
|
+
/**
|
|
231
|
+
* Opens the file picker, and names the input for a screen reader.
|
|
232
|
+
*
|
|
233
|
+
* A `<label>` rather than a `<button>`: the browser forwards the click to the
|
|
234
|
+
* input on its own, the input stays the one thing in the tab order, and its
|
|
235
|
+
* accessible name comes from this text without an `aria-label` to keep in
|
|
236
|
+
* step. A disabled input ignores the click, so nothing has to be intercepted.
|
|
237
|
+
*/
|
|
238
|
+
const Trigger = /* @__PURE__ */ (0, react.forwardRef)(({ children, ...props }, ref) => {
|
|
239
|
+
const { inputId, disabled } = useFileInputContext();
|
|
240
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
241
|
+
ref,
|
|
242
|
+
htmlFor: inputId,
|
|
243
|
+
"data-disabled": disabled ? "" : void 0,
|
|
244
|
+
...props,
|
|
245
|
+
children
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
/**
|
|
249
|
+
* Picks files, and hands them over as `File`s.
|
|
250
|
+
*/
|
|
251
|
+
const FileInput = {
|
|
252
|
+
Root: /* @__PURE__ */ (0, react.forwardRef)(({ accept, multiple = false, disabled = false, onChange, onReject, children, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, ...props }, ref) => {
|
|
253
|
+
const inputId = (0, react.useId)();
|
|
254
|
+
const context = (0, react.useMemo)(() => ({
|
|
255
|
+
inputId,
|
|
256
|
+
disabled
|
|
257
|
+
}), [inputId, disabled]);
|
|
258
|
+
function handleChange(event) {
|
|
259
|
+
const picked = Array.from(event.target.files ?? []);
|
|
260
|
+
event.target.value = "";
|
|
261
|
+
const accepted = [];
|
|
262
|
+
const rejected = [];
|
|
263
|
+
for (const file of picked) if ((0, _tremolo_ui_dom.matchesAccept)(file, accept)) accepted.push(file);
|
|
264
|
+
else rejected.push(file);
|
|
265
|
+
if (rejected.length > 0) onReject?.(rejected);
|
|
266
|
+
if (accepted.length > 0) onChange?.(accepted);
|
|
267
|
+
}
|
|
268
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileInputProvider, {
|
|
269
|
+
value: context,
|
|
270
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
271
|
+
ref,
|
|
272
|
+
"data-disabled": disabled ? "" : void 0,
|
|
273
|
+
...props,
|
|
274
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
275
|
+
id: inputId,
|
|
276
|
+
type: "file",
|
|
277
|
+
accept,
|
|
278
|
+
multiple,
|
|
279
|
+
disabled,
|
|
280
|
+
style: visuallyHiddenStyle,
|
|
281
|
+
"aria-label": ariaLabel,
|
|
282
|
+
"aria-labelledby": ariaLabelledby,
|
|
283
|
+
"aria-describedby": ariaDescribedby,
|
|
284
|
+
onChange: handleChange
|
|
285
|
+
}), children]
|
|
286
|
+
})
|
|
287
|
+
});
|
|
288
|
+
}),
|
|
289
|
+
Trigger
|
|
290
|
+
};
|
|
291
|
+
//#endregion
|
|
95
292
|
//#region src/hooks/_internal/useCheckSteps.ts
|
|
96
293
|
/** Positions probed across the travel. The ends are left out so that the
|
|
97
294
|
* clamp at `min` and `max` cannot be mistaken for a press that does nothing. */
|
|
@@ -157,8 +354,8 @@ function probe(options, range, format) {
|
|
|
157
354
|
};
|
|
158
355
|
for (const { modifiers } of entries(options)) for (const position of PROBES) {
|
|
159
356
|
const value = scale.denormalize(position, min, max);
|
|
160
|
-
const up = (0,
|
|
161
|
-
const down = (0,
|
|
357
|
+
const up = (0, _tremolo_ui_dom.applyDelta)(value, 1, options, range, modifiers);
|
|
358
|
+
const down = (0, _tremolo_ui_dom.applyDelta)(value, -1, options, range, modifiers);
|
|
162
359
|
if (up !== value || down !== value) outcome.moved = true;
|
|
163
360
|
if (!format) continue;
|
|
164
361
|
const shown = format(value);
|
|
@@ -209,21 +406,6 @@ function useCheckSteps({ component, axis, range, keyboard, wheel, format }) {
|
|
|
209
406
|
]);
|
|
210
407
|
}
|
|
211
408
|
//#endregion
|
|
212
|
-
//#region src/hooks/_internal/useCallbackRef.ts
|
|
213
|
-
/**
|
|
214
|
-
* Internal
|
|
215
|
-
* @private
|
|
216
|
-
*/
|
|
217
|
-
function useCallbackRef(callback, deps = []) {
|
|
218
|
-
const callbackRef = (0, react.useRef)(() => {
|
|
219
|
-
throw new Error("Cannot call an event handler while rendering.");
|
|
220
|
-
});
|
|
221
|
-
(0, react.useInsertionEffect)(() => {
|
|
222
|
-
callbackRef.current = callback;
|
|
223
|
-
});
|
|
224
|
-
return (0, react.useCallback)((...args) => callbackRef.current?.(...args), deps);
|
|
225
|
-
}
|
|
226
|
-
//#endregion
|
|
227
409
|
//#region src/hooks/useDragValue.ts
|
|
228
410
|
/**
|
|
229
411
|
* Drive a value with a pointer drag.
|
|
@@ -330,7 +512,7 @@ function useWheel(onWheel, { requireFocus = false, target } = {}) {
|
|
|
330
512
|
* `useCheckSteps` warns about.
|
|
331
513
|
*
|
|
332
514
|
* A modifier entry is not snapped to `step`, which is what lets the finer
|
|
333
|
-
* amount move at all — see `applyDelta` in `@tremolo-ui/
|
|
515
|
+
* amount move at all — see `applyDelta` in `@tremolo-ui/dom`.
|
|
334
516
|
*/
|
|
335
517
|
const DEFAULT_KEYBOARD_OPTIONS = {
|
|
336
518
|
default: ["raw", 1],
|
|
@@ -592,7 +774,7 @@ const Knob = {
|
|
|
592
774
|
"ArrowDown"
|
|
593
775
|
].includes(key)) {
|
|
594
776
|
event.preventDefault();
|
|
595
|
-
onChange((0,
|
|
777
|
+
onChange((0, _tremolo_ui_dom.applyDelta)(value, key === "ArrowRight" || key === "ArrowUp" ? 1 : -1, keyboard, range, event));
|
|
596
778
|
}
|
|
597
779
|
}, [
|
|
598
780
|
keyboard,
|
|
@@ -607,7 +789,7 @@ const Knob = {
|
|
|
607
789
|
reverse: true
|
|
608
790
|
}], [range]),
|
|
609
791
|
getValue: () => [value, value],
|
|
610
|
-
sensitivity: (state) => (0,
|
|
792
|
+
sensitivity: (state) => (0, _tremolo_ui_dom.selectModifier)(dragSensitivity, state.event).value,
|
|
611
793
|
threshold: 1,
|
|
612
794
|
cursor: inactive ? void 0 : externalStyles.cursor,
|
|
613
795
|
pointerLock: inactive ? false : pointerLock,
|
|
@@ -621,7 +803,7 @@ const Knob = {
|
|
|
621
803
|
if (!wheel || inactive) return;
|
|
622
804
|
event.preventDefault();
|
|
623
805
|
if (!onChange || event.deltaY === 0) return;
|
|
624
|
-
onChange((0,
|
|
806
|
+
onChange((0, _tremolo_ui_dom.applyDelta)(value, -Math.sign(event.deltaY), wheel, range, event));
|
|
625
807
|
}, WHEEL_OPTIONS$2);
|
|
626
808
|
const rootRefCallback = require_compose_refs.useComposedRefs(elmRef, dragRefCallback, wheelRefCallback);
|
|
627
809
|
const context = (0, react.useMemo)(() => {
|
|
@@ -1016,7 +1198,7 @@ const Stepper = /* @__PURE__ */ (0, react.forwardRef)(function Stepper({ classNa
|
|
|
1016
1198
|
* out of the pipeline for a modifier entry — naming one is a request to move
|
|
1017
1199
|
* off the grid.
|
|
1018
1200
|
*/
|
|
1019
|
-
const dragOptions = (0, react.useMemo)(() => (0,
|
|
1201
|
+
const dragOptions = (0, react.useMemo)(() => (0, _tremolo_ui_dom.mapModifier)(dragSensitivity, (factor) => ["raw", step * factor]), [dragSensitivity, step]);
|
|
1020
1202
|
/**
|
|
1021
1203
|
* Set once the drag has actually moved the value, so that the press-and-hold
|
|
1022
1204
|
* repeat of a stepper stands down and leaves the value to the drag. A ref,
|
|
@@ -1043,7 +1225,7 @@ const Stepper = /* @__PURE__ */ (0, react.forwardRef)(function Stepper({ classNa
|
|
|
1043
1225
|
onDragStart: (state) => {
|
|
1044
1226
|
originRef.current = null;
|
|
1045
1227
|
draggingRef.current = false;
|
|
1046
|
-
factorRef.current = (0,
|
|
1228
|
+
factorRef.current = (0, _tremolo_ui_dom.selectModifier)(dragSensitivity, state.event).value;
|
|
1047
1229
|
},
|
|
1048
1230
|
onDrag: (_x, y, _dx, _dy, state) => {
|
|
1049
1231
|
if (inactive || drag === null) return;
|
|
@@ -1055,7 +1237,7 @@ const Stepper = /* @__PURE__ */ (0, react.forwardRef)(function Stepper({ classNa
|
|
|
1055
1237
|
previousYRef.current = y;
|
|
1056
1238
|
return;
|
|
1057
1239
|
}
|
|
1058
|
-
const factor = (0,
|
|
1240
|
+
const factor = (0, _tremolo_ui_dom.selectModifier)(dragSensitivity, state.event).value;
|
|
1059
1241
|
if (factor !== factorRef.current) {
|
|
1060
1242
|
originRef.current = {
|
|
1061
1243
|
y: previousYRef.current,
|
|
@@ -1067,7 +1249,7 @@ const Stepper = /* @__PURE__ */ (0, react.forwardRef)(function Stepper({ classNa
|
|
|
1067
1249
|
const steps = Math.round(-(y - originRef.current.y) / drag);
|
|
1068
1250
|
if (steps === 0) return;
|
|
1069
1251
|
draggingRef.current = true;
|
|
1070
|
-
changeValue((0,
|
|
1252
|
+
changeValue((0, _tremolo_ui_dom.applyDelta)(originRef.current.value, steps, dragOptions, rawRange, state.event));
|
|
1071
1253
|
},
|
|
1072
1254
|
onDragEnd: () => {
|
|
1073
1255
|
draggingRef.current = false;
|
|
@@ -1197,8 +1379,8 @@ const NumberInput = {
|
|
|
1197
1379
|
changeValue
|
|
1198
1380
|
]);
|
|
1199
1381
|
const nudge = (0, react.useCallback)((direction, option, modifiers) => {
|
|
1200
|
-
const [mode] = (0,
|
|
1201
|
-
changeValue((0,
|
|
1382
|
+
const [mode] = (0, _tremolo_ui_dom.selectModifier)(option, modifiers).value;
|
|
1383
|
+
changeValue((0, _tremolo_ui_dom.applyDelta)(value, direction, option, mode === "raw" ? rawRange : range, modifiers));
|
|
1202
1384
|
}, [
|
|
1203
1385
|
changeValue,
|
|
1204
1386
|
value,
|
|
@@ -1368,7 +1550,7 @@ const Piano = { Root: /* @__PURE__ */ (0, react.forwardRef)(function Root({ note
|
|
|
1368
1550
|
const [activeNotes, setActiveNotes] = (0, react.useState)([]);
|
|
1369
1551
|
/** Set while `resizable` is on, measured from the parent. */
|
|
1370
1552
|
const [resizedKeyWidth, setResizedKeyWidth] = (0, react.useState)(whiteKeyWidth);
|
|
1371
|
-
const notes = (0, react.useMemo)(() => (0,
|
|
1553
|
+
const notes = (0, react.useMemo)(() => (0, _tremolo_ui_dom.getNoteRangeArray)(noteRange), [noteRange]);
|
|
1372
1554
|
const whiteKeyCount = (0, react.useMemo)(() => notes.filter(_tremolo_ui_functions.isWhiteKey).length, [notes]);
|
|
1373
1555
|
const layout = (0, react.useMemo)(() => ({
|
|
1374
1556
|
noteRange,
|
|
@@ -1513,7 +1695,7 @@ const Piano = { Root: /* @__PURE__ */ (0, react.forwardRef)(function Root({ note
|
|
|
1513
1695
|
role: "group",
|
|
1514
1696
|
tabIndex: 0,
|
|
1515
1697
|
style: {
|
|
1516
|
-
width: resizable ? "100%" : (0,
|
|
1698
|
+
width: resizable ? "100%" : (0, _tremolo_ui_dom.pianoWidth)(layout),
|
|
1517
1699
|
"--height": cssLength(height),
|
|
1518
1700
|
position: "relative",
|
|
1519
1701
|
...style
|
|
@@ -1540,8 +1722,8 @@ const Piano = { Root: /* @__PURE__ */ (0, react.forwardRef)(function Root({ note
|
|
|
1540
1722
|
position: "absolute",
|
|
1541
1723
|
zIndex: keyType === "white" ? 1 : 2,
|
|
1542
1724
|
...keyStyle,
|
|
1543
|
-
left: (0,
|
|
1544
|
-
width: keyType === "white" ? layout.whiteKeyWidth : (0,
|
|
1725
|
+
left: (0, _tremolo_ui_dom.notePosition)(note, layout),
|
|
1726
|
+
width: keyType === "white" ? layout.whiteKeyWidth : (0, _tremolo_ui_dom.blackKeyWidth)(layout),
|
|
1545
1727
|
height: keyType === "white" ? "100%" : `${blackKeyHeightRatio * 100}%`
|
|
1546
1728
|
},
|
|
1547
1729
|
children: content !== "" && content !== null && content !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -1581,24 +1763,11 @@ function usePointsEditorContext(selector) {
|
|
|
1581
1763
|
}
|
|
1582
1764
|
//#endregion
|
|
1583
1765
|
//#region src/components/_util/VisuallyHiddenRangeInput.tsx
|
|
1584
|
-
const hiddenStyle = {
|
|
1585
|
-
position: "absolute",
|
|
1586
|
-
width: 1,
|
|
1587
|
-
height: 1,
|
|
1588
|
-
padding: 0,
|
|
1589
|
-
margin: -1,
|
|
1590
|
-
overflow: "hidden",
|
|
1591
|
-
clip: "rect(0, 0, 0, 0)",
|
|
1592
|
-
clipPath: "inset(50%)",
|
|
1593
|
-
whiteSpace: "nowrap",
|
|
1594
|
-
border: 0,
|
|
1595
|
-
pointerEvents: "none"
|
|
1596
|
-
};
|
|
1597
1766
|
const VisuallyHiddenRangeInput = /* @__PURE__ */ (0, react.forwardRef)((props, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1598
1767
|
...props,
|
|
1599
1768
|
ref,
|
|
1600
1769
|
type: "range",
|
|
1601
|
-
style:
|
|
1770
|
+
style: visuallyHiddenStyle
|
|
1602
1771
|
}));
|
|
1603
1772
|
//#endregion
|
|
1604
1773
|
//#region src/components/PointsEditor/Point.tsx
|
|
@@ -1664,7 +1833,7 @@ function Point({ value, children, id: idProp, min, max, size, width, height, col
|
|
|
1664
1833
|
const { refCallback: dragRefCallback, dragging } = useDragValue({
|
|
1665
1834
|
axis: AXIS,
|
|
1666
1835
|
baseElementRef: containerRef,
|
|
1667
|
-
sensitivity: (state) => (0,
|
|
1836
|
+
sensitivity: (state) => (0, _tremolo_ui_dom.selectModifier)(dragSensitivity, state.event).value,
|
|
1668
1837
|
cursor: inactive ? void 0 : externalStyles.cursor,
|
|
1669
1838
|
shouldStart: () => !disabled,
|
|
1670
1839
|
onChange: ([x, y]) => {
|
|
@@ -1692,7 +1861,7 @@ function Point({ value, children, id: idProp, min, max, size, width, height, col
|
|
|
1692
1861
|
}
|
|
1693
1862
|
});
|
|
1694
1863
|
const nudge = (0, react.useCallback)((axis, direction, option, modifiers) => {
|
|
1695
|
-
const next = (0,
|
|
1864
|
+
const next = (0, _tremolo_ui_dom.applyDelta)(value[axis], direction, option, AXIS, modifiers);
|
|
1696
1865
|
nudgeSelection(id, {
|
|
1697
1866
|
x: axis === "x" ? next - value.x : 0,
|
|
1698
1867
|
y: axis === "y" ? next - value.y : 0
|
|
@@ -1981,7 +2150,7 @@ const PointsEditor = {
|
|
|
1981
2150
|
const { element, wheel, readonly, onChange, value } = entry.current;
|
|
1982
2151
|
if (!element?.contains(active)) continue;
|
|
1983
2152
|
if (!wheel || readonly || !onChange) return false;
|
|
1984
|
-
const next = (0,
|
|
2153
|
+
const next = (0, _tremolo_ui_dom.applyDelta)(value[axis], direction, wheel, AXIS, modifiers);
|
|
1985
2154
|
nudgeSelection(id, {
|
|
1986
2155
|
x: axis === "x" ? next - value.x : 0,
|
|
1987
2156
|
y: axis === "y" ? next - value.y : 0
|
|
@@ -2106,7 +2275,7 @@ function MarksOption({ value, mark = true, label, thickness, length, gap, labelW
|
|
|
2106
2275
|
const reverse = useSliderContext((s) => s.reverse);
|
|
2107
2276
|
const calcPercent = (0, react.useCallback)((value) => {
|
|
2108
2277
|
const percent = scale.normalize(value, min, max) * 100;
|
|
2109
|
-
return (0, _tremolo_ui_functions.toFixed)(
|
|
2278
|
+
return (0, _tremolo_ui_functions.toFixed)(vertical !== reverse ? 100 - percent : percent);
|
|
2110
2279
|
}, [
|
|
2111
2280
|
vertical,
|
|
2112
2281
|
reverse,
|
|
@@ -2142,6 +2311,27 @@ function MarksOption({ value, mark = true, label, thickness, length, gap, labelW
|
|
|
2142
2311
|
});
|
|
2143
2312
|
}
|
|
2144
2313
|
//#endregion
|
|
2314
|
+
//#region src/components/_util/decimal-digits.ts
|
|
2315
|
+
/**
|
|
2316
|
+
* How many digits a number carries after the decimal point.
|
|
2317
|
+
*
|
|
2318
|
+
* `Slider.Marks` builds its labels by multiplying an interval, which leaves
|
|
2319
|
+
* binary debris in the last digits (`0.1 * 3` is `0.30000000000000004`). The
|
|
2320
|
+
* count is what tells `toFixed` how far to round that back.
|
|
2321
|
+
*
|
|
2322
|
+
* The exponent form has to be handled separately: `String(1e-7)` is `'1e-7'`,
|
|
2323
|
+
* which has no decimal point at all, so reading the text after the point
|
|
2324
|
+
* reports no digits and the interval rounds away to whole numbers.
|
|
2325
|
+
*/
|
|
2326
|
+
function decimalDigits(x) {
|
|
2327
|
+
if (!Number.isFinite(x)) return 0;
|
|
2328
|
+
const text = String(x);
|
|
2329
|
+
const e = text.indexOf("e");
|
|
2330
|
+
if (e === -1) return text.split(".")[1]?.length ?? 0;
|
|
2331
|
+
const fraction = text.slice(0, e).split(".")[1]?.length ?? 0;
|
|
2332
|
+
return Math.max(0, fraction - Number(text.slice(e + 1)));
|
|
2333
|
+
}
|
|
2334
|
+
//#endregion
|
|
2145
2335
|
//#region src/components/Slider/type.ts
|
|
2146
2336
|
function generateOptionsList(options, min, max, step) {
|
|
2147
2337
|
const { per, mark = true, label = true } = typeof options === "object" ? options : { per: options };
|
|
@@ -2149,7 +2339,7 @@ function generateOptionsList(options, min, max, step) {
|
|
|
2149
2339
|
const interval = per === "step" ? step : per;
|
|
2150
2340
|
const count = Math.floor(max / interval) - Math.ceil(min / interval) + 1;
|
|
2151
2341
|
for (let i = 0; i < count; i++) {
|
|
2152
|
-
const value = (0, _tremolo_ui_functions.toFixed)(interval * (Math.ceil(min / interval) + i), (
|
|
2342
|
+
const value = (0, _tremolo_ui_functions.toFixed)(interval * (Math.ceil(min / interval) + i), decimalDigits(interval));
|
|
2153
2343
|
optionsList.push({
|
|
2154
2344
|
value,
|
|
2155
2345
|
mark,
|
|
@@ -2167,7 +2357,7 @@ function Marks({ gap, options, children, className, style, ...props }) {
|
|
|
2167
2357
|
const vertical = useSliderContext((s) => s.vertical);
|
|
2168
2358
|
const reverse = useSliderContext((s) => s.reverse);
|
|
2169
2359
|
const optionsList = options ? generateOptionsList(options, min, max, step) : [];
|
|
2170
|
-
if (
|
|
2360
|
+
if (vertical !== reverse) optionsList.reverse();
|
|
2171
2361
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2172
2362
|
className,
|
|
2173
2363
|
"data-orientation": vertical ? "vertical" : "horizontal",
|
|
@@ -2252,7 +2442,7 @@ const Track = /* @__PURE__ */ (0, react.forwardRef)(function Track({ length, thi
|
|
|
2252
2442
|
className,
|
|
2253
2443
|
"data-disabled": disabled ? "" : void 0,
|
|
2254
2444
|
"data-orientation": vertical ? "vertical" : "horizontal",
|
|
2255
|
-
"data-flipped":
|
|
2445
|
+
"data-flipped": vertical !== reverse ? "" : void 0,
|
|
2256
2446
|
style: {
|
|
2257
2447
|
"--active": active,
|
|
2258
2448
|
"--inactive": inactive,
|
|
@@ -2291,7 +2481,7 @@ const Slider = {
|
|
|
2291
2481
|
const inactive = disabled || readonly;
|
|
2292
2482
|
const p = (0, _tremolo_ui_functions.toFixed)(scale.normalize(value, min, max) * 100);
|
|
2293
2483
|
const rev = (0, _tremolo_ui_functions.toFixed)(100 - p);
|
|
2294
|
-
const displayReversed =
|
|
2484
|
+
const displayReversed = vertical !== reverse;
|
|
2295
2485
|
const percent = displayReversed ? rev : p;
|
|
2296
2486
|
const axis = (0, react.useMemo)(() => ({
|
|
2297
2487
|
min,
|
|
@@ -2324,7 +2514,7 @@ const Slider = {
|
|
|
2324
2514
|
if (!keyboard || !onChange || inactive) return;
|
|
2325
2515
|
let direction = key === "ArrowRight" || key === "ArrowUp" ? 1 : -1;
|
|
2326
2516
|
if (reverse) direction *= -1;
|
|
2327
|
-
onChange((0,
|
|
2517
|
+
onChange((0, _tremolo_ui_dom.applyDelta)(value, direction, keyboard, axis, event));
|
|
2328
2518
|
}, [
|
|
2329
2519
|
keyboard,
|
|
2330
2520
|
onChange,
|
|
@@ -2339,7 +2529,7 @@ const Slider = {
|
|
|
2339
2529
|
baseElementRef: trackRef,
|
|
2340
2530
|
cursor: inactive ? void 0 : externalStyles.cursor,
|
|
2341
2531
|
shouldStart: () => !inactive,
|
|
2342
|
-
sensitivity: (state) => (0,
|
|
2532
|
+
sensitivity: (state) => (0, _tremolo_ui_dom.selectModifier)(dragSensitivity, state.event).value,
|
|
2343
2533
|
updateOnPointerDown: true,
|
|
2344
2534
|
onChange: (v) => {
|
|
2345
2535
|
if (inactive) return;
|
|
@@ -2365,7 +2555,7 @@ const Slider = {
|
|
|
2365
2555
|
direction = event.deltaY > 0 ? -1 : 1;
|
|
2366
2556
|
}
|
|
2367
2557
|
if (reverse) direction *= -1;
|
|
2368
|
-
onChange((0,
|
|
2558
|
+
onChange((0, _tremolo_ui_dom.applyDelta)(value, direction, wheel, axis, event));
|
|
2369
2559
|
}, WHEEL_OPTIONS$1);
|
|
2370
2560
|
const rootRefCallback = require_compose_refs.useComposedRefs(dragRefCallback, wheelRefCallback);
|
|
2371
2561
|
const context = (0, react.useMemo)(() => ({
|
|
@@ -2597,7 +2787,7 @@ const XYPad = {
|
|
|
2597
2787
|
});
|
|
2598
2788
|
const withAxis = (0, react.useCallback)((axis, next) => axis === 0 ? [next, value[1]] : [value[0], next], [value]);
|
|
2599
2789
|
/** @param i 0 = x, 1 = y */
|
|
2600
|
-
const nudge = (0, react.useCallback)((i, direction, option, modifiers) => withAxis(i, (0,
|
|
2790
|
+
const nudge = (0, react.useCallback)((i, direction, option, modifiers) => withAxis(i, (0, _tremolo_ui_dom.applyDelta)(value[i], direction, option, axis[i], modifiers)), [
|
|
2601
2791
|
value,
|
|
2602
2792
|
axis,
|
|
2603
2793
|
withAxis
|
|
@@ -2627,7 +2817,7 @@ const XYPad = {
|
|
|
2627
2817
|
const { refCallback: dragRefCallback } = useDragValue({
|
|
2628
2818
|
axis,
|
|
2629
2819
|
baseElementRef: areaRef,
|
|
2630
|
-
sensitivity: (state) => (0,
|
|
2820
|
+
sensitivity: (state) => (0, _tremolo_ui_dom.selectModifier)(dragSensitivity, state.event).value,
|
|
2631
2821
|
updateOnPointerDown: true,
|
|
2632
2822
|
cursor: inactive ? void 0 : externalStyles.cursor,
|
|
2633
2823
|
shouldStart: () => !inactive,
|
|
@@ -2880,6 +3070,8 @@ exports.AnimationCanvas = AnimationCanvas;
|
|
|
2880
3070
|
exports.DEFAULT_DRAG_SENSITIVITY = DEFAULT_DRAG_SENSITIVITY;
|
|
2881
3071
|
exports.DEFAULT_KEYBOARD_OPTIONS = DEFAULT_KEYBOARD_OPTIONS;
|
|
2882
3072
|
exports.DEFAULT_WHEEL_OPTIONS = DEFAULT_WHEEL_OPTIONS;
|
|
3073
|
+
exports.DropZone = DropZone;
|
|
3074
|
+
exports.FileInput = FileInput;
|
|
2883
3075
|
exports.Knob = Knob;
|
|
2884
3076
|
Object.defineProperty(exports, "NOT_SUPPORTED", {
|
|
2885
3077
|
enumerable: true,
|
|
@@ -2910,7 +3102,9 @@ exports.clampPoint = clampPoint;
|
|
|
2910
3102
|
exports.useAnimationFrame = useAnimationFrame;
|
|
2911
3103
|
exports.useDrag = useDrag;
|
|
2912
3104
|
exports.useDragValue = useDragValue;
|
|
3105
|
+
exports.useDropZone = useDropZone;
|
|
2913
3106
|
exports.useEventListener = useEventListener;
|
|
3107
|
+
exports.useFileInputContext = useFileInputContext;
|
|
2914
3108
|
exports.useInterval = useInterval;
|
|
2915
3109
|
exports.useKnobContext = useKnobContext;
|
|
2916
3110
|
exports.useLongPress = useLongPress;
|