@tremolo-ui/react 0.0.1 → 0.0.2

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 (64) hide show
  1. package/dist/cjs/index.d.ts +43 -47
  2. package/dist/cjs/index.js +411 -303
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/types/src/components/AnimationCanvas/canvas.d.ts +6 -0
  5. package/dist/cjs/types/src/components/AnimationCanvas/canvas.d.ts.map +1 -0
  6. package/dist/cjs/types/src/components/AnimationCanvas/index.d.ts +20 -0
  7. package/dist/cjs/types/src/components/AnimationCanvas/index.d.ts.map +1 -0
  8. package/dist/cjs/types/src/components/Knob/index.d.ts +23 -10
  9. package/dist/cjs/types/src/components/Knob/index.d.ts.map +1 -1
  10. package/dist/cjs/types/src/components/NumberInput/index.d.ts +47 -0
  11. package/dist/cjs/types/src/components/NumberInput/index.d.ts.map +1 -0
  12. package/dist/cjs/types/src/components/Slider/Thumb.d.ts +11 -0
  13. package/dist/cjs/types/src/components/Slider/Thumb.d.ts.map +1 -0
  14. package/dist/cjs/types/src/components/Slider/Track.d.ts +10 -0
  15. package/dist/cjs/types/src/components/Slider/Track.d.ts.map +1 -0
  16. package/dist/cjs/types/src/components/Slider/index.d.ts +17 -10
  17. package/dist/cjs/types/src/components/Slider/index.d.ts.map +1 -1
  18. package/dist/cjs/types/src/components/Slider/type.d.ts +4 -24
  19. package/dist/cjs/types/src/components/Slider/type.d.ts.map +1 -1
  20. package/dist/cjs/types/src/hooks/useCallbackRef.d.ts.map +1 -1
  21. package/dist/cjs/types/src/hooks/useEventListener.d.ts.map +1 -1
  22. package/dist/cjs/types/src/hooks/useRefCallbackEvent.d.ts +5 -0
  23. package/dist/cjs/types/src/hooks/useRefCallbackEvent.d.ts.map +1 -0
  24. package/dist/cjs/types/src/index.d.ts +2 -2
  25. package/dist/cjs/types/src/index.d.ts.map +1 -1
  26. package/dist/cjs/types/src/math.d.ts.map +1 -1
  27. package/dist/cjs/types/src/system/pseudo.d.ts +14 -0
  28. package/dist/cjs/types/src/system/pseudo.d.ts.map +1 -0
  29. package/dist/cjs/types/src/types.d.ts.map +1 -1
  30. package/dist/cjs/types/src/util.d.ts.map +1 -1
  31. package/dist/esm/index.d.ts +43 -47
  32. package/dist/esm/index.js +378 -268
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist/esm/types/src/components/AnimationCanvas/canvas.d.ts +6 -0
  35. package/dist/esm/types/src/components/AnimationCanvas/canvas.d.ts.map +1 -0
  36. package/dist/esm/types/src/components/AnimationCanvas/index.d.ts +20 -0
  37. package/dist/esm/types/src/components/AnimationCanvas/index.d.ts.map +1 -0
  38. package/dist/esm/types/src/components/Knob/index.d.ts +23 -10
  39. package/dist/esm/types/src/components/Knob/index.d.ts.map +1 -1
  40. package/dist/esm/types/src/components/NumberInput/index.d.ts +47 -0
  41. package/dist/esm/types/src/components/NumberInput/index.d.ts.map +1 -0
  42. package/dist/esm/types/src/components/Slider/Thumb.d.ts +11 -0
  43. package/dist/esm/types/src/components/Slider/Thumb.d.ts.map +1 -0
  44. package/dist/esm/types/src/components/Slider/Track.d.ts +10 -0
  45. package/dist/esm/types/src/components/Slider/Track.d.ts.map +1 -0
  46. package/dist/esm/types/src/components/Slider/index.d.ts +17 -10
  47. package/dist/esm/types/src/components/Slider/index.d.ts.map +1 -1
  48. package/dist/esm/types/src/components/Slider/type.d.ts +4 -24
  49. package/dist/esm/types/src/components/Slider/type.d.ts.map +1 -1
  50. package/dist/esm/types/src/hooks/useCallbackRef.d.ts.map +1 -1
  51. package/dist/esm/types/src/hooks/useEventListener.d.ts.map +1 -1
  52. package/dist/esm/types/src/hooks/useRefCallbackEvent.d.ts +5 -0
  53. package/dist/esm/types/src/hooks/useRefCallbackEvent.d.ts.map +1 -0
  54. package/dist/esm/types/src/index.d.ts +2 -2
  55. package/dist/esm/types/src/index.d.ts.map +1 -1
  56. package/dist/esm/types/src/math.d.ts.map +1 -1
  57. package/dist/esm/types/src/system/pseudo.d.ts +14 -0
  58. package/dist/esm/types/src/system/pseudo.d.ts.map +1 -0
  59. package/dist/esm/types/src/types.d.ts.map +1 -1
  60. package/dist/esm/types/src/util.d.ts.map +1 -1
  61. package/package.json +19 -36
  62. package/LICENSE +0 -21
  63. package/README.md +0 -59
  64. package/dist/tsconfig.tsbuildinfo +0 -1
package/dist/esm/index.js CHANGED
@@ -1,45 +1,44 @@
1
- import { jsx, jsxs } from '@emotion/react/jsx-runtime';
1
+ import { normalizeValue, radian, rawValue, clamp, stepValue } from 'common/math';
2
+ export * from 'common/math';
3
+ import { jsxs, Fragment, jsx } from '@emotion/react/jsx-runtime';
2
4
  import { css, Global } from '@emotion/react';
3
- import { useRef, useInsertionEffect, useCallback, useEffect, useMemo, Children, isValidElement } from 'react';
5
+ import React, { useRef, useInsertionEffect, useCallback, useEffect, useMemo } from 'react';
6
+ import { isReversed, parseScaleOrderList, isHorizontal, gradientDirection, isVertical } from 'common/components/Slider/type';
7
+ import { styleHelper } from 'common/util';
4
8
 
5
- function clamp(value, min, max) {
6
- return Math.max(min, Math.min(value, max));
7
- }
8
- function normalizeValue(rawValue, min, max, skew = 1) {
9
- if (min >= max)
10
- throw new RangeError('requirements: min < max');
11
- const v = clamp((rawValue - min) / (max - min), 0, 1);
12
- return Math.pow(v, skew);
13
- }
14
- function rawValue(normalizedValue, min, max, skew = 1) {
15
- if (min >= max)
16
- throw new RangeError('requirements: min < max');
17
- const v = skew == 1
18
- ? clamp(normalizedValue, 0, 1)
19
- : Math.exp(Math.log(clamp(normalizedValue, 0, 1)) / skew);
20
- return min + v * (max - min);
21
- }
22
- function skewWithCenterValue(centerValue, min, max) {
23
- if (!(min <= centerValue && centerValue <= max))
24
- throw new RangeError('requirements: min <= centerValue <= max');
25
- return Math.log(0.5) / Math.log((centerValue - min) / (max - min));
26
- }
27
- function stepValue(value, step) {
28
- var _a;
29
- if (step <= 0)
30
- throw new RangeError('requirements: step > 0');
31
- const quotient = Math.floor(value / step);
32
- const decimalDigits = (_a = decimalPart(step)) === null || _a === void 0 ? void 0 : _a.length;
33
- const v = toFixed(quotient * step, decimalDigits);
34
- const next = toFixed((quotient + 1) * step, decimalDigits);
35
- return Math.abs(value - v) < Math.abs(value - next) ? v : next;
36
- }
37
- function toFixed(x, fractionDigits) {
38
- return Number(x.toFixed(fractionDigits));
39
- }
40
- function decimalPart(x) {
41
- return String(x).split('.')[1];
42
- }
9
+ /******************************************************************************
10
+ Copyright (c) Microsoft Corporation.
11
+
12
+ Permission to use, copy, modify, and/or distribute this software for any
13
+ purpose with or without fee is hereby granted.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
16
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
17
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
18
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
19
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
20
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21
+ PERFORMANCE OF THIS SOFTWARE.
22
+ ***************************************************************************** */
23
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
24
+
25
+
26
+ function __rest(s, e) {
27
+ var t = {};
28
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
29
+ t[p] = s[p];
30
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
31
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
32
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
33
+ t[p[i]] = s[p[i]];
34
+ }
35
+ return t;
36
+ }
37
+
38
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
39
+ var e = new Error(message);
40
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
41
+ };
43
42
 
44
43
  /**
45
44
  * This hook is user-land implementation of the experimental `useEffectEvent` hook.
@@ -72,27 +71,174 @@ function useEventListener(target, event, handler, options) {
72
71
  };
73
72
  }
74
73
 
75
- function styleHelper(property) {
76
- if (typeof property == 'number') {
77
- return `${property}px`;
78
- }
79
- else {
80
- return property;
81
- }
74
+ function useRefCallbackEvent(event, handler, options, deps = []) {
75
+ return useMemo(() => {
76
+ let cleanup;
77
+ return (node) => {
78
+ if (!node) {
79
+ cleanup === null || cleanup === void 0 ? void 0 : cleanup();
80
+ return;
81
+ }
82
+ node.addEventListener(event, handler, options);
83
+ cleanup = () => {
84
+ node.removeEventListener(event, handler, options);
85
+ };
86
+ };
87
+ }, deps);
82
88
  }
83
89
 
84
- function Knob({ value, min, max, step = 1, skew = 1, size = 50, active = '#7998ec', inactive = '#eee', bg = '#ccc', thumb = '#4e76e6', lineWeight = 12, thumbWeight = 4, cursor = 'pointer', style, bodyNoSelect = true, enableWheel, onChange, }) {
90
+ // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
91
+ const drawingState = [
92
+ 'strokeStyle',
93
+ 'fillStyle',
94
+ 'globalAlpha',
95
+ 'lineWidth',
96
+ 'lineCap',
97
+ 'lineJoin',
98
+ 'miterLimit',
99
+ 'lineDashOffset',
100
+ 'shadowOffsetX',
101
+ 'shadowOffsetY',
102
+ 'shadowBlur',
103
+ 'shadowColor',
104
+ 'globalCompositeOperation',
105
+ 'font',
106
+ 'textAlign',
107
+ 'textBaseline',
108
+ 'direction',
109
+ 'imageSmoothingEnabled',
110
+ ];
111
+
112
+ function setDprConfig(canvas, context, width, height, dpr) {
113
+ canvas.width = width * dpr;
114
+ canvas.height = height * dpr;
115
+ context.scale(dpr, dpr);
116
+ canvas.style.width = `${width}px`;
117
+ canvas.style.height = `${height}px`;
118
+ }
119
+ /**
120
+ * A simple animatable canvas with requestAnimationFrame()
121
+ */
122
+ function AnimationCanvas(_a) {
123
+ var { width = 100, height = 100, style, relativeSize = false, reduceFlickering = true, options, init, draw } = _a, props = __rest(_a, ["width", "height", "style", "relativeSize", "reduceFlickering", "options", "init", "draw"]);
124
+ const canvasRef = useRef(null);
125
+ const memoCanvasRef = useRef(null);
126
+ const reqIdRef = useRef();
127
+ const widthRef = useRef(0);
128
+ const heightRef = useRef(0);
129
+ const loop = (context, width, height, count) => {
130
+ reqIdRef.current = requestAnimationFrame(() => loop(context, width, height, count + 1));
131
+ draw(context, width, height, count + 1);
132
+ };
133
+ useEffect(() => {
134
+ if (!canvasRef.current)
135
+ return;
136
+ const canvas = canvasRef.current;
137
+ const context = canvas.getContext('2d', options);
138
+ if (!context)
139
+ throw new Error('Cannot get canvas context.');
140
+ const dpr = window.devicePixelRatio;
141
+ if (relativeSize) {
142
+ const parent = canvas.parentElement;
143
+ if (!parent)
144
+ throw new Error("Canvas doesn't have a parent element.");
145
+ const memoCanvas = memoCanvasRef.current;
146
+ const memoContext = memoCanvas === null || memoCanvas === void 0 ? void 0 : memoCanvas.getContext('2d', options);
147
+ const resizeObserver = new ResizeObserver(() => {
148
+ console.log('resize');
149
+ // Prevents loss of some context when the canvas is resized
150
+ const contextMemo = {};
151
+ for (const prop of drawingState) {
152
+ contextMemo[prop] = context[prop];
153
+ }
154
+ const w = Math.floor(parent.clientWidth);
155
+ const h = Math.floor(parent.clientHeight);
156
+ if (reduceFlickering && memoCanvas && memoContext) {
157
+ memoCanvas.width = w * dpr;
158
+ memoCanvas.height = h * dpr;
159
+ memoContext.scale(1 / dpr, 1 / dpr);
160
+ memoContext.drawImage(context.canvas, 0, 0);
161
+ }
162
+ setDprConfig(canvas, context, w, h, dpr);
163
+ widthRef.current = w;
164
+ heightRef.current = h;
165
+ for (const prop of drawingState) {
166
+ context[prop] = contextMemo[prop];
167
+ }
168
+ if (reduceFlickering && memoContext) {
169
+ context.drawImage(memoContext.canvas, 0, 0);
170
+ }
171
+ });
172
+ resizeObserver.observe(parent);
173
+ const w = Math.floor(parent.clientWidth);
174
+ const h = Math.floor(parent.clientHeight);
175
+ setDprConfig(canvas, context, w, h, dpr);
176
+ if (init)
177
+ init(context, widthRef, heightRef, 0);
178
+ loop(context, widthRef, heightRef, 0);
179
+ return () => {
180
+ if (reqIdRef.current)
181
+ cancelAnimationFrame(reqIdRef.current);
182
+ resizeObserver.unobserve(parent);
183
+ };
184
+ }
185
+ else {
186
+ const rect = canvas.getBoundingClientRect();
187
+ setDprConfig(canvas, context, rect.width, rect.height, dpr);
188
+ widthRef.current = rect.width;
189
+ heightRef.current = rect.height;
190
+ if (init)
191
+ init(context, widthRef, heightRef, 0);
192
+ loop(context, widthRef, heightRef, 0);
193
+ return () => {
194
+ if (reqIdRef.current)
195
+ cancelAnimationFrame(reqIdRef.current);
196
+ };
197
+ }
198
+ }, [loop]);
199
+ return (jsxs(Fragment, { children: [jsx("canvas", Object.assign({ width: relativeSize ? 0 : width, height: relativeSize ? 0 : height, css: css(style), ref: canvasRef }, props)), relativeSize && reduceFlickering && (jsx("canvas", { style: { display: 'none' }, width: relativeSize ? 0 : width, height: relativeSize ? 0 : height, ref: memoCanvasRef }))] }));
200
+ }
201
+
202
+ const defaultOptions = {
203
+ active: '#7998ec',
204
+ inactive: '#eee',
205
+ bg: '#ccc',
206
+ thumb: '#4e76e6',
207
+ lineWeight: 6,
208
+ thumbWeight: 4,
209
+ };
210
+ /**
211
+ * simple rotary Knob.
212
+ */
213
+ function Knob(_a) {
214
+ var { value, min, max, defaultValue = min, startValue = min, step = 1, skew = 1, size, width = 50, height = 50, options = defaultOptions,
215
+ // draw,
216
+ cursor = 'pointer', style, bodyNoSelect = true, enableWheel, enableDoubleClickDefault = true, onChange, _active, _focus, _hover } = _a, props = __rest(_a, ["value", "min", "max", "defaultValue", "startValue", "step", "skew", "size", "width", "height", "options", "cursor", "style", "bodyNoSelect", "enableWheel", "enableDoubleClickDefault", "onChange", "_active", "_focus", "_hover"]);
85
217
  // -- state and ref ---
86
218
  // const [privateValue, setPrivateValue] = useState(value);
87
219
  const dragOffsetY = useRef(undefined);
88
220
  // --- interpret props ---
89
- const percent = normalizeValue(value, min, max, skew);
90
- const handleValue = (event) => {
221
+ const opts = Object.assign(Object.assign({}, defaultOptions), options);
222
+ // const percent = normalizeValue(value, min, max, skew)
223
+ const isRelativeSize = typeof (size !== null && size !== void 0 ? size : width) == 'string';
224
+ const adaptSize = (size) => {
225
+ if (typeof size == 'string') {
226
+ return Number(size.slice(0, -1));
227
+ }
228
+ else {
229
+ return size;
230
+ }
231
+ };
232
+ const handleEvent = (event) => {
233
+ const isTouch = event instanceof TouchEvent;
234
+ if (isTouch && event.cancelable)
235
+ event.preventDefault();
91
236
  if (dragOffsetY.current) {
92
237
  if (bodyNoSelect)
93
238
  document.body.classList.add('no-select');
94
- const delta = dragOffsetY.current - event.screenY;
95
- dragOffsetY.current = event.screenY;
239
+ const screenY = isTouch ? event.touches[0].screenY : event.screenY;
240
+ const delta = dragOffsetY.current - screenY;
241
+ dragOffsetY.current = screenY;
96
242
  const n = normalizeValue(value, min, max, skew);
97
243
  const v = rawValue(n + delta / 100, min, max, skew);
98
244
  const v2 = clamp(stepValue(v, step), min, max);
@@ -101,209 +247,175 @@ function Knob({ value, min, max, step = 1, skew = 1, size = 50, active = '#7998e
101
247
  }
102
248
  };
103
249
  // --- hooks ---
104
- const wrapperRef = useMemo(() => {
105
- let cleanup;
106
- return (div) => {
107
- if (!div) {
108
- cleanup === null || cleanup === void 0 ? void 0 : cleanup();
109
- return;
110
- }
111
- const controller = new AbortController();
112
- div.addEventListener('wheel', (event) => {
113
- if (!enableWheel)
114
- return;
115
- event.preventDefault();
116
- const x = event.deltaY > 0 ? -enableWheel[1] : enableWheel[1];
117
- let v;
118
- if (enableWheel[0] == 'normalized') {
119
- const n = normalizeValue(value, min, max, skew);
120
- v = rawValue(n + x, min, max, skew);
121
- }
122
- else {
123
- v = value + x;
124
- }
125
- if (onChange)
126
- onChange(clamp(stepValue(v, step), min, max));
127
- }, {
128
- signal: controller.signal,
129
- passive: false,
130
- });
131
- cleanup = () => {
132
- // console.log("cleanup")
133
- controller.abort();
134
- };
135
- };
136
- }, [value]);
137
- useEventListener(window, 'mousemove', (event) => {
138
- handleValue(event);
139
- });
140
- useEventListener(window, 'mouseup', () => {
250
+ const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
251
+ if (!enableWheel)
252
+ return;
253
+ event.preventDefault();
254
+ const x = event.deltaY > 0 ? -enableWheel[1] : enableWheel[1];
255
+ let v;
256
+ if (enableWheel[0] == 'normalized') {
257
+ const n = normalizeValue(value, min, max, skew);
258
+ v = rawValue(n + x, min, max, skew);
259
+ }
260
+ else {
261
+ v = value + x;
262
+ }
263
+ if (onChange)
264
+ onChange(clamp(stepValue(v, step), min, max));
265
+ }, { passive: false }, [enableWheel, value, min, max, skew, step, onChange]);
266
+ const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleEvent, { passive: false }, [value, min, max, skew, step, onChange, bodyNoSelect]);
267
+ useEventListener(window, 'mousemove', handleEvent);
268
+ useEventListener(window, 'pointerup', () => {
141
269
  dragOffsetY.current = undefined;
142
270
  if (bodyNoSelect)
143
271
  document.body.classList.remove('no-select');
144
272
  });
145
- return (jsx("div", { className: "tremolo-knob", ref: wrapperRef, onMouseDown: (event) => {
273
+ return (jsxs("div", Object.assign({ className: "tremolo-knob", ref: (div) => {
274
+ wheelRefCallback(div);
275
+ touchMoveRefCallback(div);
276
+ }, role: "slider", tabIndex: 0, "aria-valuenow": value, "aria-valuemax": max, "aria-valuemin": min, css: css(Object.assign(Object.assign({ display: 'inline-block', width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height, cursor: cursor }, style), { ':active': Object.assign({}, _active), ':focus': Object.assign({}, _focus), ':hover': Object.assign({}, _hover) })), onPointerDown: (event) => {
146
277
  dragOffsetY.current = event.screenY;
147
- handleValue(event);
148
- }, style: Object.assign({ display: 'inline-block', width: size, height: size }, style), children: jsx("div", { css: css({
149
- display: 'inline-block',
150
- width: size,
151
- height: size,
152
- background: `conic-gradient(${active} ${percent * 270}deg, ${inactive} ${percent * 270}deg, ${inactive} ${270}deg, ${bg} 90deg)`,
153
- borderRadius: '50%',
154
- position: 'relative',
155
- cursor: cursor,
156
- rotate: '-135deg',
157
- '&::before': {
158
- display: 'block',
159
- position: 'absolute',
160
- right: 0,
161
- left: 0,
162
- top: 0,
163
- bottom: 0,
164
- margin: 'auto',
165
- content: '""',
166
- width: `calc(${styleHelper(size)} - ${styleHelper(lineWeight)})`,
167
- height: `calc(${styleHelper(size)} - ${styleHelper(lineWeight)})`,
168
- backgroundColor: bg,
169
- borderRadius: '50%',
170
- },
171
- '&::after': {
172
- display: 'block',
173
- position: 'absolute',
174
- top: 0,
175
- bottom: 0,
176
- // left: lineWeight,
177
- margin: 'auto 0',
178
- content: '""',
179
- width: `calc(0.5 * ${styleHelper(size)})`,
180
- height: thumbWeight,
181
- borderRadius: thumbWeight,
182
- backgroundColor: thumb,
183
- rotate: `${90 + percent * 270}deg`,
184
- transformOrigin: '100% 50%',
185
- },
186
- }), children: jsx(Global, { styles: {
278
+ handleEvent(event);
279
+ }, onDoubleClick: () => {
280
+ if (enableDoubleClickDefault && onChange) {
281
+ onChange(defaultValue);
282
+ }
283
+ } }, props, { children: [jsx(Global, { styles: {
187
284
  '.no-select': {
188
285
  userSelect: 'none',
189
286
  },
190
- } }) }) }));
287
+ } }), jsx(AnimationCanvas, { width: adaptSize(size !== null && size !== void 0 ? size : width), height: adaptSize(size !== null && size !== void 0 ? size : height), relativeSize: isRelativeSize,
288
+ // TODO
289
+ // reduceFlickering={false}
290
+ draw: (ctx, _w, _h) => {
291
+ const p = normalizeValue(value, min, max, skew);
292
+ const startP = normalizeValue(startValue, min, max, skew);
293
+ const w = _w.current, cx = w / 2;
294
+ const h = _h.current, cy = h / 2;
295
+ const r = Math.min(cx, cy);
296
+ // reset
297
+ ctx.clearRect(0, 0, w, h);
298
+ ctx.lineCap = 'butt';
299
+ // bg
300
+ ctx.beginPath();
301
+ ctx.fillStyle = opts.bg;
302
+ ctx.arc(cx, cy, r - 0.5, 0, 2 * Math.PI);
303
+ ctx.fill();
304
+ ctx.lineCap = 'round';
305
+ ctx.lineWidth = opts.lineWeight;
306
+ // inactive rotary
307
+ ctx.beginPath();
308
+ ctx.strokeStyle = opts.inactive;
309
+ ctx.arc(cx, cy, r - 0.5 - opts.lineWeight / 2, radian(135), radian(45));
310
+ ctx.stroke();
311
+ // active rotary
312
+ ctx.beginPath();
313
+ ctx.strokeStyle = opts.active;
314
+ ctx.arc(cx, cy, r - 0.5 - opts.lineWeight / 2, radian(135 + 270 * startP), radian(135 + 270 * p), p < startP);
315
+ ctx.stroke();
316
+ // thumb
317
+ ctx.beginPath();
318
+ ctx.moveTo(cx, cy);
319
+ ctx.lineTo(cx +
320
+ Math.cos(radian(135 + 270 * p)) *
321
+ (r - 0.5 - opts.thumbWeight / 2), cy +
322
+ Math.sin(radian(135 + 270 * p)) *
323
+ (r - 0.5 - opts.thumbWeight / 2));
324
+ ctx.strokeStyle = opts.thumb;
325
+ ctx.lineWidth = opts.thumbWeight;
326
+ ctx.stroke();
327
+ } })] })));
191
328
  }
192
329
 
193
- function isHorizontal(d) {
194
- return d == 'right' || d == 'left';
195
- }
196
- function isVertical(d) {
197
- return d == 'up' || d == 'down';
198
- }
199
- function isReversed(d) {
200
- return d == 'left' || d == 'up';
201
- }
202
- function gradientDirection(d) {
203
- if (d == 'left' || d == 'right')
204
- return d;
205
- if (d == 'up')
206
- return 'top';
207
- if (d == 'down')
208
- return 'bottom';
209
- }
210
- function parseScaleOrderList(scale, min, max, step) {
211
- var _a;
212
- let scalesList = [];
213
- if (!scale) ;
214
- else if (typeof scale[0] == 'object') {
215
- scalesList = scale;
216
- }
217
- else {
218
- const per = scale[0] == 'step' ? step : scale[0];
219
- const count = Math.floor(max / per) - Math.ceil(min / per) + 1;
220
- for (let i = 0; i < count; i++) {
221
- const at = toFixed(per * (Math.ceil(min / per) + i), (_a = decimalPart(per)) === null || _a === void 0 ? void 0 : _a.length);
222
- scalesList.push({ at: at, type: scale[1] });
223
- }
224
- }
225
- return scalesList;
330
+ function SliderThumb({ color, children, style, __css, }) {
331
+ return (jsx("div", { className: "tremolo-slider-thumb-wrapper", css: css(Object.assign({ width: 'fit-content', height: 'fit-content', position: 'absolute', translate: '-50% -50%', zIndex: 100 }, __css)), children: children ? (children) : (
332
+ // default slider thumb
333
+ jsx("div", { className: "tremolo-slider-thumb",
334
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
335
+ tabIndex: 0, css: css(Object.assign({ background: color, width: '1.4rem', height: '1.4rem', borderRadius: '50%' }, style)) })) }));
226
336
  }
227
337
 
228
- // interface SliderThumbProps {}
229
- function SliderThumb( /* {}: SliderThumbProps */) {
230
- return jsx("div", {});
338
+ function SliderTrack({ children, style, __css, }) {
339
+ return (jsx("div", { className: "tremolo-slider-track", css: css(Object.assign(Object.assign({}, __css), style)), children: children }));
231
340
  }
232
- function Slider({ value, min, max, step = 1, length = 140, thickness = 10, direction = 'right', skew = 1, scale, scaleOption, color = '#4e76e6', bg = '#eee', cursor = 'pointer', style, bodyNoSelect = true, enableWheel, onChange, children, }) {
233
- var _a, _b;
341
+
342
+ /**
343
+ * Customizable slider
344
+ */
345
+ function Slider(_a) {
346
+ var _b, _c;
347
+ var { value, min, max, step = 1, skew = 1, length = 140, thickness = 10, direction = 'right', scale, scaleOption, color = '#4e76e6', bg = '#eee', className, style, bodyNoSelect = true, enableWheel, onChange, children } = _a; __rest(_a, ["value", "min", "max", "step", "skew", "length", "thickness", "direction", "scale", "scaleOption", "color", "bg", "className", "style", "bodyNoSelect", "enableWheel", "onChange", "children"]);
234
348
  // -- state and ref ---
235
- const baseWrapperElement = useRef(null);
236
- // --- interpret props ---
349
+ const trackElementRef = useRef(null);
237
350
  const percent = normalizeValue(value, min, max, skew) * 100;
238
351
  const percentRev = isReversed(direction) ? 100 - percent : percent;
239
352
  const scalesList = parseScaleOrderList(scale, min, max, step);
240
353
  if (isReversed(direction))
241
354
  scalesList.reverse();
242
- // let defaultThumb = true
355
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
356
+ let trackProps;
357
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
358
+ let thumbProps;
243
359
  if (children != undefined) {
244
- const childElements = Children.toArray(children);
245
- const lastElement = childElements[childElements.length - 1];
246
- if (isValidElement(lastElement)) {
247
- // defaultThumb = false
248
- if (lastElement.type == SliderThumb) ;
249
- }
250
- else {
251
- throw new Error('children is an invalid element.');
252
- }
360
+ React.Children.forEach(children, (child) => {
361
+ if (React.isValidElement(child)) {
362
+ if (child.type == SliderThumb) {
363
+ thumbProps = child.props;
364
+ }
365
+ else if (child.type == SliderTrack) {
366
+ trackProps = child.props;
367
+ }
368
+ else {
369
+ throw new Error('only <SliderThumb> or <SliderTrack>');
370
+ }
371
+ }
372
+ else {
373
+ throw new Error('children is an invalid element.');
374
+ }
375
+ });
253
376
  }
254
377
  const thumbDragged = useRef(false);
255
378
  const handleValue = (event) => {
256
- if (baseWrapperElement.current && thumbDragged.current) {
257
- if (bodyNoSelect)
258
- document.body.classList.add('no-select');
259
- const { left: x1, top: y1, right: x2, bottom: y2, } = baseWrapperElement.current.getBoundingClientRect();
260
- const mouseX = event.clientX;
261
- const mouseY = event.clientY;
262
- const n = isHorizontal(direction)
263
- ? normalizeValue(mouseX, x1, x2)
264
- : normalizeValue(mouseY, y1, y2);
265
- const v = rawValue(isReversed(direction) ? 1 - n : n, min, max, skew);
266
- const v2 = clamp(stepValue(v, step), min, max);
267
- if (onChange)
268
- onChange(v2);
269
- }
379
+ if (!trackElementRef.current || !thumbDragged.current)
380
+ return;
381
+ const isTouch = event instanceof TouchEvent;
382
+ if (isTouch && event.cancelable)
383
+ event.preventDefault();
384
+ if (bodyNoSelect)
385
+ document.body.classList.add('no-select');
386
+ const { left: x1, top: y1, right: x2, bottom: y2, } = trackElementRef.current.getBoundingClientRect();
387
+ const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
388
+ const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
389
+ const n = isHorizontal(direction)
390
+ ? normalizeValue(mouseX, x1, x2)
391
+ : normalizeValue(mouseY, y1, y2);
392
+ const v = rawValue(isReversed(direction) ? 1 - n : n, min, max, skew);
393
+ const v2 = clamp(stepValue(v, step), min, max);
394
+ if (onChange)
395
+ onChange(v2);
270
396
  };
271
397
  // --- hooks ---
272
- const wrapperRef = useMemo(() => {
273
- let cleanup;
274
- return (div) => {
275
- if (!div) {
276
- cleanup === null || cleanup === void 0 ? void 0 : cleanup();
277
- return;
278
- }
279
- const controller = new AbortController();
280
- div.addEventListener('wheel', (event) => {
281
- if (!enableWheel)
282
- return;
283
- event.preventDefault();
284
- let x = event.deltaY > 0 ? enableWheel[1] : -enableWheel[1];
285
- if (isReversed(direction))
286
- x *= -1;
287
- let v;
288
- if (enableWheel[0] == 'normalized') {
289
- const n = normalizeValue(value, min, max, skew);
290
- v = rawValue(n + x, min, max, skew);
291
- }
292
- else {
293
- v = value + x;
294
- }
295
- if (onChange)
296
- onChange(clamp(stepValue(v, step), min, max));
297
- }, {
298
- signal: controller.signal,
299
- passive: false,
300
- });
301
- cleanup = () => {
302
- // console.log("cleanup")
303
- controller.abort();
304
- };
305
- };
306
- }, [value]);
398
+ const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [min, max, skew, step, direction, onChange]);
399
+ const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
400
+ if (!enableWheel)
401
+ return;
402
+ event.preventDefault();
403
+ let x = event.deltaY > 0 ? enableWheel[1] : -enableWheel[1];
404
+ if (isReversed(direction) || isHorizontal(direction))
405
+ x *= -1;
406
+ let v;
407
+ if (enableWheel[0] == 'normalized') {
408
+ const n = normalizeValue(value, min, max, skew);
409
+ v = rawValue(n + x, min, max, skew);
410
+ }
411
+ else {
412
+ v = value + x;
413
+ }
414
+ if (onChange)
415
+ onChange(clamp(stepValue(v, step), min, max));
416
+ }, {
417
+ passive: false,
418
+ }, [enableWheel, value, min, max, skew, step]);
307
419
  useEventListener(window, 'mousemove', (event) => {
308
420
  handleValue(event);
309
421
  });
@@ -312,44 +424,38 @@ function Slider({ value, min, max, step = 1, length = 140, thickness = 10, direc
312
424
  if (bodyNoSelect)
313
425
  document.body.classList.remove('no-select');
314
426
  });
315
- return (jsxs("div", { ref: wrapperRef, className: "tremolo-slider", style: {
316
- display: 'inline-block',
317
- boxSizing: 'border-box',
318
- margin: '0.7rem',
319
- padding: 0,
320
- cursor: cursor,
321
- }, onMouseDown: (event) => {
427
+ return (jsxs("div", { className: 'tremolo-slider' + (className ? ` ${className}` : ''), ref: (div) => {
428
+ wheelRefCallback(div);
429
+ touchMoveRefCallback(div);
430
+ }, tabIndex: -1, role: "slider", "aria-valuenow": value, "aria-valuemax": max, "aria-valuemin": min, "aria-orientation": isHorizontal(direction) ? 'horizontal' : 'vertical', css: css(Object.assign({ display: 'inline-block', boxSizing: 'border-box', margin: '0.7rem', padding: 0, cursor: 'pointer' }, style)), onPointerDown: (event) => {
322
431
  thumbDragged.current = true;
323
432
  handleValue(event);
324
433
  }, children: [jsx(Global, { styles: {
325
434
  '.no-select': {
326
435
  userSelect: 'none',
327
436
  },
328
- } }), jsxs("div", { style: {
437
+ } }), jsxs("div", { css: css({
329
438
  display: 'flex',
330
439
  flexDirection: isHorizontal(direction) ? 'column' : 'row',
331
- }, children: [jsx("div", { ref: baseWrapperElement, css: css(Object.assign({ background: `linear-gradient(to ${gradientDirection(direction)}, ${color} ${percent}%, ${bg} ${percent}%)`, borderRadius: typeof thickness == 'number'
332
- ? thickness / 2
333
- : `calc(${thickness} / 2)`, borderStyle: 'solid', borderColor: '#ccc', borderWidth: 2, width: isHorizontal(direction) ? length : thickness, height: isHorizontal(direction) ? thickness : length, position: 'relative', zIndex: 1 }, style)), children: jsx("div", { className: "tremolo-slider-thumb-wrapper", style: {
334
- width: 'fix-content',
335
- height: 'fix-content',
336
- position: 'absolute',
337
- top: isHorizontal(direction) ? '50%' : `${percentRev}%`,
338
- left: isHorizontal(direction) ? `${percentRev}%` : '50%',
339
- translate: '-50% -50%',
340
- zIndex: 100,
341
- }, children: children ? (children) : (jsx("div", { className: "tremolo-slider-thumb", style: {
342
- background: color,
343
- width: '1.4rem',
344
- height: '1.4rem',
345
- borderRadius: '50%',
346
- } })) }) }), scale && (jsx("div", { className: "tremolo-slider-scale-wrapper", style: Object.assign({ position: 'relative', width: isHorizontal(direction) ? length : undefined, height: isVertical(direction) ? length : undefined, marginLeft: isHorizontal(direction)
440
+ }), children: [jsxs("div", { className: "tremolo-slider-track-wrapper", ref: trackElementRef, css: css({ position: 'relative' }), children: [jsx(SliderTrack, Object.assign({ __css: {
441
+ background: `linear-gradient(to ${gradientDirection(direction)}, ${color} ${percent}%, ${bg} ${percent}%)`,
442
+ borderRadius: styleHelper(thickness, '/', 2),
443
+ borderStyle: 'solid',
444
+ borderColor: '#ccc',
445
+ borderWidth: 2,
446
+ width: isHorizontal(direction) ? length : thickness,
447
+ height: isHorizontal(direction) ? thickness : length,
448
+ zIndex: 1,
449
+ } }, trackProps)), jsx(SliderThumb, Object.assign({ color: color, __css: {
450
+ top: isHorizontal(direction) ? '50%' : `${percentRev}%`,
451
+ left: isHorizontal(direction) ? `${percentRev}%` : '50%',
452
+ } }, thumbProps))] }), scale && (jsx("div", { className: "tremolo-slider-scale-wrapper", css: css(Object.assign({ position: 'relative', width: isHorizontal(direction) ? length : undefined, height: isVertical(direction) ? length : undefined, marginLeft: isHorizontal(direction)
347
453
  ? 2
348
- : ((_a = scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.gap) !== null && _a !== void 0 ? _a : `calc((1.4rem - ${styleHelper(thickness)}) / 2)`), marginTop: isVertical(direction)
454
+ : ((_b = scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.gap) !== null && _b !== void 0 ? _b : `calc((1.4rem - ${styleHelper(thickness)}) / 2)`), marginTop: isVertical(direction)
349
455
  ? 2
350
- : ((_b = scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.gap) !== null && _b !== void 0 ? _b : `calc((1.4rem - ${styleHelper(thickness)}) / 2)`) }, scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.style), children: scalesList.map((item, index) => {
351
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
352
- return (jsxs("div", { style: {
456
+ : ((_c = scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.gap) !== null && _c !== void 0 ? _c : `calc((1.4rem - ${styleHelper(thickness)}) / 2)`) }, scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.style)), children: scalesList.map((item, index) => {
457
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
458
+ return (jsxs("div", { css: css({
353
459
  display: 'flex',
354
460
  flexDirection: isHorizontal(direction) ? 'column' : 'row',
355
461
  justifyContent: 'center',
@@ -367,21 +473,25 @@ function Slider({ value, min, max, step = 1, length = 140, thickness = 10, direc
367
473
  : undefined,
368
474
  translate: isHorizontal(direction) ? '-50% 0' : '0 -50%',
369
475
  zIndex: 10,
370
- }, children: [item.type != 'number' && (jsx("div", { className: "tremolo-slider-scale-mark", style: {
476
+ }), children: [((_b = (_a = item.type) !== null && _a !== void 0 ? _a : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.defaultType) !== null && _b !== void 0 ? _b : 'mark-number') !=
477
+ 'number' && (jsx("div", { className: "tremolo-slider-scale-mark", css: css({
371
478
  width: isHorizontal(direction)
372
- ? ((_b = (_a = item.style) === null || _a === void 0 ? void 0 : _a.thickness) !== null && _b !== void 0 ? _b : 1)
373
- : ((_d = (_c = item.style) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : '0.5rem'),
479
+ ? ((_d = (_c = item.style) === null || _c === void 0 ? void 0 : _c.thickness) !== null && _d !== void 0 ? _d : 1)
480
+ : ((_f = (_e = item.style) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : '0.5rem'),
374
481
  height: isVertical(direction)
375
- ? ((_f = (_e = item.style) === null || _e === void 0 ? void 0 : _e.thickness) !== null && _f !== void 0 ? _f : 1)
376
- : ((_h = (_g = item.style) === null || _g === void 0 ? void 0 : _g.length) !== null && _h !== void 0 ? _h : '0.5rem'),
377
- backgroundColor: (_l = (_k = (_j = item.style) === null || _j === void 0 ? void 0 : _j.markColor) !== null && _k !== void 0 ? _k : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.markColor) !== null && _l !== void 0 ? _l : '#222',
482
+ ? ((_h = (_g = item.style) === null || _g === void 0 ? void 0 : _g.thickness) !== null && _h !== void 0 ? _h : 1)
483
+ : ((_k = (_j = item.style) === null || _j === void 0 ? void 0 : _j.length) !== null && _k !== void 0 ? _k : '0.5rem'),
484
+ backgroundColor: (_o = (_m = (_l = item.style) === null || _l === void 0 ? void 0 : _l.markColor) !== null && _m !== void 0 ? _m : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.markColor) !== null && _o !== void 0 ? _o : '#222',
378
485
  marginBottom: isHorizontal(direction) ? 2 : undefined,
379
486
  marginRight: isVertical(direction) ? 2 : undefined,
380
- } })), item.type != 'mark' && (jsx("div", { className: "tremolo-slider-scale-number", style: {
381
- color: (_o = (_m = item.style) === null || _m === void 0 ? void 0 : _m.labelColor) !== null && _o !== void 0 ? _o : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.labelColor,
382
- }, children: item.at }))] }, index));
487
+ }) })), ((_q = (_p = item.type) !== null && _p !== void 0 ? _p : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.defaultType) !== null && _q !== void 0 ? _q : 'mark-number') !=
488
+ 'mark' && (jsx("div", { className: "tremolo-slider-scale-number", css: css({
489
+ color: (_s = (_r = item.style) === null || _r === void 0 ? void 0 : _r.labelColor) !== null && _s !== void 0 ? _s : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.labelColor,
490
+ width: scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.labelWidth,
491
+ textAlign: 'right',
492
+ }), children: (_t = item.text) !== null && _t !== void 0 ? _t : item.at }))] }, index));
383
493
  }) }))] })] }));
384
494
  }
385
495
 
386
- export { Knob, Slider, clamp, decimalPart, normalizeValue, rawValue, skewWithCenterValue, stepValue, toFixed };
496
+ export { Knob, Slider };
387
497
  //# sourceMappingURL=index.js.map