@tremolo-ui/react 0.1.3 → 0.1.5
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 +116 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +30 -19
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +26 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +102 -48
- package/dist/index.js.map +1 -1
- package/package.json +14 -9
- package/src/components/AnimationCanvas/canvas.ts +16 -0
- package/src/components/AnimationCanvas/index.tsx +54 -71
- package/src/components/NumberInput/InternalInput.tsx +1 -1
- package/src/components/NumberInput/context.tsx +2 -1
- package/src/components/NumberInput/index.css +1 -0
- package/src/components/NumberInput/index.tsx +2 -1
- package/src/components/NumberInput/type.ts +58 -0
- package/src/components/Piano/index.tsx +1 -2
- package/src/components/Slider/Scale.tsx +1 -1
- package/src/components/Slider/ScaleOption.tsx +1 -1
- package/src/components/Slider/index.tsx +1 -0
- package/src/components/Slider/type.ts +26 -0
- package/src/index.ts +2 -1
- /package/src/styles/{index.css → global.css} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -26,8 +26,6 @@ const react = __toESM(require("react"));
|
|
|
26
26
|
const react_jsx_runtime = __toESM(require("react/jsx-runtime"));
|
|
27
27
|
const __tremolo_ui_functions = __toESM(require("@tremolo-ui/functions"));
|
|
28
28
|
const zustand = __toESM(require("zustand"));
|
|
29
|
-
const __tremolo_ui_functions_NumberInput = __toESM(require("@tremolo-ui/functions/NumberInput"));
|
|
30
|
-
const __tremolo_ui_functions_Slider = __toESM(require("@tremolo-ui/functions/Slider"));
|
|
31
29
|
|
|
32
30
|
//#region src/components/AnimationCanvas/canvas.ts
|
|
33
31
|
const drawingState = [
|
|
@@ -50,9 +48,6 @@ const drawingState = [
|
|
|
50
48
|
"direction",
|
|
51
49
|
"imageSmoothingEnabled"
|
|
52
50
|
];
|
|
53
|
-
|
|
54
|
-
//#endregion
|
|
55
|
-
//#region src/components/AnimationCanvas/index.tsx
|
|
56
51
|
function setDprConfig(canvas, context, width, height, dpr) {
|
|
57
52
|
canvas.width = width * dpr;
|
|
58
53
|
canvas.height = height * dpr;
|
|
@@ -61,7 +56,10 @@ function setDprConfig(canvas, context, width, height, dpr) {
|
|
|
61
56
|
canvas.style.width = `${width}px`;
|
|
62
57
|
canvas.style.height = `${height}px`;
|
|
63
58
|
}
|
|
64
|
-
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/components/AnimationCanvas/index.tsx
|
|
62
|
+
function AnimationCanvas({ draw, init, animate = true, options, width: _width = 100, height: _height = 100, relativeSize, reduceFlickering = true, className, onContextMenu = (event) => event.preventDefault(),...props }) {
|
|
65
63
|
const canvasRef = (0, react.useRef)(null);
|
|
66
64
|
const memoCanvasRef = (0, react.useRef)(null);
|
|
67
65
|
const reqIdRef = (0, react.useRef)(-1);
|
|
@@ -69,27 +67,40 @@ function AnimationCanvas({ options, init, draw, width = 100, height = 100, relat
|
|
|
69
67
|
const heightRef = (0, react.useRef)(0);
|
|
70
68
|
const deltaMemoRef = (0, react.useRef)(-1);
|
|
71
69
|
const startTimeRef = (0, react.useRef)(-1);
|
|
72
|
-
const loop = (0, react.useCallback)((context, width
|
|
70
|
+
const loop = (0, react.useCallback)((context, width, height, count) => {
|
|
73
71
|
const now = performance.now();
|
|
74
72
|
const deltaTime = now - deltaMemoRef.current;
|
|
75
73
|
const elapsedTime = now - startTimeRef.current;
|
|
76
74
|
deltaMemoRef.current = now;
|
|
77
|
-
reqIdRef.current = requestAnimationFrame(() => loop(context, width
|
|
75
|
+
if (animate) reqIdRef.current = requestAnimationFrame(() => loop(context, width, height, count + 1));
|
|
78
76
|
draw(context, {
|
|
79
|
-
width: width
|
|
80
|
-
height: height
|
|
77
|
+
width: width.current,
|
|
78
|
+
height: height.current,
|
|
81
79
|
count: count + 1,
|
|
82
80
|
deltaTime,
|
|
83
81
|
elapsedTime,
|
|
84
82
|
fps: 1e3 / deltaTime
|
|
85
83
|
});
|
|
86
|
-
}, [draw]);
|
|
84
|
+
}, [draw, animate]);
|
|
87
85
|
(0, react.useEffect)(() => {
|
|
88
86
|
if (!canvasRef.current) return;
|
|
89
87
|
const canvas = canvasRef.current;
|
|
90
88
|
const context = canvas.getContext("2d", options);
|
|
91
89
|
if (!context) throw new Error("Cannot get canvas context.");
|
|
92
90
|
const dpr = globalThis.devicePixelRatio;
|
|
91
|
+
const firstRendering = (width, height) => {
|
|
92
|
+
setDprConfig(canvas, context, width, height, dpr);
|
|
93
|
+
widthRef.current = width;
|
|
94
|
+
heightRef.current = height;
|
|
95
|
+
if (init) init(context, {
|
|
96
|
+
width,
|
|
97
|
+
height
|
|
98
|
+
});
|
|
99
|
+
const now = performance.now();
|
|
100
|
+
deltaMemoRef.current = now;
|
|
101
|
+
startTimeRef.current = now;
|
|
102
|
+
loop(context, widthRef, heightRef, -1);
|
|
103
|
+
};
|
|
93
104
|
if (relativeSize) {
|
|
94
105
|
const parent = canvas.parentElement;
|
|
95
106
|
if (!parent) throw new Error("Canvas doesn't have a parent element.");
|
|
@@ -97,52 +108,37 @@ function AnimationCanvas({ options, init, draw, width = 100, height = 100, relat
|
|
|
97
108
|
const memoContext = memoCanvas?.getContext("2d", options);
|
|
98
109
|
const ro = new ResizeObserver((entries) => {
|
|
99
110
|
for (const entry of entries) {
|
|
111
|
+
const w = entry.contentRect.width;
|
|
112
|
+
const h = entry.contentRect.height;
|
|
100
113
|
const contextMemo = {};
|
|
101
|
-
for (const prop of drawingState) contextMemo[prop] = context[prop];
|
|
102
|
-
const w$1 = entry.contentRect.width;
|
|
103
|
-
const h$1 = entry.contentRect.height;
|
|
104
114
|
if (reduceFlickering && memoCanvas && memoContext) {
|
|
105
|
-
|
|
106
|
-
memoCanvas.
|
|
115
|
+
for (const prop of drawingState) contextMemo[prop] = context[prop];
|
|
116
|
+
memoCanvas.width = w * dpr;
|
|
117
|
+
memoCanvas.height = h * dpr;
|
|
107
118
|
memoContext.scale(1 / dpr, 1 / dpr);
|
|
108
119
|
if (canvas.width > 0 && canvas.height > 0) memoContext.drawImage(canvas, 0, 0);
|
|
109
120
|
}
|
|
110
|
-
setDprConfig(canvas, context, w
|
|
111
|
-
widthRef.current = w
|
|
112
|
-
heightRef.current = h
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
setDprConfig(canvas, context, w, h, dpr);
|
|
122
|
+
widthRef.current = w;
|
|
123
|
+
heightRef.current = h;
|
|
124
|
+
if (reduceFlickering && memoContext && memoCanvas && memoCanvas.width > 0 && memoCanvas.height > 0) {
|
|
125
|
+
for (const prop of drawingState) context[prop] = contextMemo[prop];
|
|
126
|
+
context.drawImage(memoContext.canvas, 0, 0);
|
|
127
|
+
}
|
|
128
|
+
if (!animate) loop(context, widthRef, heightRef, -1);
|
|
115
129
|
}
|
|
116
130
|
});
|
|
117
131
|
ro.observe(parent);
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
if (init) init(context, {
|
|
122
|
-
width: widthRef.current,
|
|
123
|
-
height: heightRef.current
|
|
124
|
-
});
|
|
125
|
-
const now = performance.now();
|
|
126
|
-
deltaMemoRef.current = now;
|
|
127
|
-
startTimeRef.current = now;
|
|
128
|
-
loop(context, widthRef, heightRef, -1);
|
|
132
|
+
const width = parent.clientWidth;
|
|
133
|
+
const height = parent.clientHeight;
|
|
134
|
+
firstRendering(width, height);
|
|
129
135
|
return () => {
|
|
130
136
|
if (reqIdRef.current) cancelAnimationFrame(reqIdRef.current);
|
|
131
137
|
ro.disconnect();
|
|
132
138
|
};
|
|
133
139
|
} else {
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
widthRef.current = rect.width;
|
|
137
|
-
heightRef.current = rect.height;
|
|
138
|
-
if (init) init(context, {
|
|
139
|
-
width: widthRef.current,
|
|
140
|
-
height: heightRef.current
|
|
141
|
-
});
|
|
142
|
-
const now = performance.now();
|
|
143
|
-
deltaMemoRef.current = now;
|
|
144
|
-
startTimeRef.current = now;
|
|
145
|
-
loop(context, widthRef, heightRef, -1);
|
|
140
|
+
const { width, height } = canvas.getBoundingClientRect();
|
|
141
|
+
firstRendering(width, height);
|
|
146
142
|
return () => {
|
|
147
143
|
if (reqIdRef.current) cancelAnimationFrame(reqIdRef.current);
|
|
148
144
|
};
|
|
@@ -152,12 +148,13 @@ function AnimationCanvas({ options, init, draw, width = 100, height = 100, relat
|
|
|
152
148
|
init,
|
|
153
149
|
options,
|
|
154
150
|
reduceFlickering,
|
|
155
|
-
relativeSize
|
|
151
|
+
relativeSize,
|
|
152
|
+
animate
|
|
156
153
|
]);
|
|
157
154
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("canvas", {
|
|
158
155
|
className: (0, clsx.default)("tremolo-animation-canvas", className),
|
|
159
|
-
width: relativeSize ? 0 :
|
|
160
|
-
height: relativeSize ? 0 :
|
|
156
|
+
width: relativeSize ? 0 : _width,
|
|
157
|
+
height: relativeSize ? 0 : _height,
|
|
161
158
|
ref: canvasRef,
|
|
162
159
|
onContextMenu,
|
|
163
160
|
...props
|
|
@@ -637,6 +634,47 @@ const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defau
|
|
|
637
634
|
});
|
|
638
635
|
});
|
|
639
636
|
|
|
637
|
+
//#endregion
|
|
638
|
+
//#region src/components/NumberInput/type.ts
|
|
639
|
+
function selectUnit(units, value) {
|
|
640
|
+
let i = 0;
|
|
641
|
+
for (; i < units.length; i++) if (Math.abs(units[i][1]) > Math.abs(value)) break;
|
|
642
|
+
return units[Math.max(0, i - 1)];
|
|
643
|
+
}
|
|
644
|
+
function parseValue(inputString, units, digit) {
|
|
645
|
+
const str = inputString.trim();
|
|
646
|
+
if (!units || typeof units == "string") {
|
|
647
|
+
const m$1 = str.match(/-?\d+(\.\d+)?/);
|
|
648
|
+
let v = Number(m$1?.[0] ?? "0");
|
|
649
|
+
v = isNaN(v) ? 0 : v;
|
|
650
|
+
return {
|
|
651
|
+
rawValue: v,
|
|
652
|
+
formatValue: (digit != void 0 ? v.toFixed(digit) : v) + (units ?? ""),
|
|
653
|
+
unit: units ?? ""
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
const unitList = units.map(([u, _s]) => u);
|
|
657
|
+
const scaleList = units.map(([_u, s]) => s);
|
|
658
|
+
let rawValue$5 = 0;
|
|
659
|
+
let unit = unitList[0];
|
|
660
|
+
let formatValue = (digit != void 0 ? rawValue$5.toFixed(digit) : rawValue$5) + unit;
|
|
661
|
+
const m = str.match(/^(-?\d+(\.\d+)?)\s*(\w*)$/);
|
|
662
|
+
if (m) {
|
|
663
|
+
rawValue$5 = Number(m[1]) || 0;
|
|
664
|
+
const uIndex = unitList.indexOf(m[3]);
|
|
665
|
+
rawValue$5 *= uIndex != -1 ? scaleList[uIndex] : 1;
|
|
666
|
+
const [u, scale] = selectUnit(units, rawValue$5);
|
|
667
|
+
const v = rawValue$5 / scale;
|
|
668
|
+
formatValue = (digit != void 0 ? v.toFixed(digit) : v) + u;
|
|
669
|
+
unit = u;
|
|
670
|
+
}
|
|
671
|
+
return {
|
|
672
|
+
rawValue: rawValue$5,
|
|
673
|
+
formatValue,
|
|
674
|
+
unit
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
640
678
|
//#endregion
|
|
641
679
|
//#region src/components/NumberInput/context.tsx
|
|
642
680
|
function safeClamp(value, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
|
|
@@ -656,14 +694,14 @@ const createNumberInputStore = (initProps) => {
|
|
|
656
694
|
return (0, zustand.createStore)()((set) => ({
|
|
657
695
|
...DEFAULT_PROPS,
|
|
658
696
|
...initProps,
|
|
659
|
-
value:
|
|
660
|
-
valueAsNumber:
|
|
697
|
+
value: parseValue(String(initProps?.value || ""), initProps?.units, initProps?.digit).formatValue,
|
|
698
|
+
valueAsNumber: parseValue(String(initProps?.value || ""), initProps?.units, initProps?.digit).rawValue,
|
|
661
699
|
increment: () => set((state) => {
|
|
662
700
|
if (state.readonly) return {};
|
|
663
|
-
const current =
|
|
701
|
+
const current = parseValue(state.value, state.units, state.digit).rawValue;
|
|
664
702
|
let next = current + (state.step ?? 1);
|
|
665
703
|
if (state.keepWithinRange) next = safeClamp(next, state.min, state.max);
|
|
666
|
-
const v =
|
|
704
|
+
const v = parseValue(String(next), state.units, state.digit).formatValue;
|
|
667
705
|
state?.onChange?.(next, v);
|
|
668
706
|
return {
|
|
669
707
|
value: v,
|
|
@@ -672,10 +710,10 @@ const createNumberInputStore = (initProps) => {
|
|
|
672
710
|
}),
|
|
673
711
|
decrement: () => set((state) => {
|
|
674
712
|
if (state.readonly) return {};
|
|
675
|
-
const current =
|
|
713
|
+
const current = parseValue(state.value, state.units, state.digit).rawValue;
|
|
676
714
|
let next = current - (state.step ?? 1);
|
|
677
715
|
if (state.keepWithinRange) next = safeClamp(next, state.min, state.max);
|
|
678
|
-
const v =
|
|
716
|
+
const v = parseValue(String(next), state.units, state.digit).formatValue;
|
|
679
717
|
state?.onChange?.(next, v);
|
|
680
718
|
return {
|
|
681
719
|
value: v,
|
|
@@ -687,7 +725,7 @@ const createNumberInputStore = (initProps) => {
|
|
|
687
725
|
const v = value ?? "";
|
|
688
726
|
return {
|
|
689
727
|
value: v,
|
|
690
|
-
valueAsNumber:
|
|
728
|
+
valueAsNumber: parseValue(v, state.units, state.digit).rawValue
|
|
691
729
|
};
|
|
692
730
|
})
|
|
693
731
|
}));
|
|
@@ -698,7 +736,7 @@ function NumberInputProvider({ children,...props }) {
|
|
|
698
736
|
if (!storeRef.current) storeRef.current = createNumberInputStore(props);
|
|
699
737
|
(0, react.useEffect)(() => {
|
|
700
738
|
if (storeRef.current) {
|
|
701
|
-
const parsed =
|
|
739
|
+
const parsed = parseValue(String(props?.value || ""), props?.units, props?.digit);
|
|
702
740
|
storeRef.current.setState({
|
|
703
741
|
...props,
|
|
704
742
|
value: parsed.formatValue,
|
|
@@ -764,7 +802,7 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
764
802
|
if (!["ArrowUp", "ArrowDown"].includes(key)) return;
|
|
765
803
|
event.preventDefault();
|
|
766
804
|
const x = key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
767
|
-
const parsed =
|
|
805
|
+
const parsed = parseValue(String(updateValueByEvent(keyboard[0], x)));
|
|
768
806
|
onChange?.(parsed.rawValue, parsed.formatValue);
|
|
769
807
|
}, [
|
|
770
808
|
keyboard,
|
|
@@ -777,7 +815,7 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
777
815
|
event.preventDefault();
|
|
778
816
|
if (!onChange || event.deltaY == 0) return;
|
|
779
817
|
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
780
|
-
const parsed =
|
|
818
|
+
const parsed = parseValue(String(updateValueByEvent(wheel[0], x)));
|
|
781
819
|
onChange?.(parsed.rawValue, parsed.formatValue);
|
|
782
820
|
}, { passive: false }, [
|
|
783
821
|
wheel,
|
|
@@ -815,7 +853,7 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
815
853
|
const v = event.currentTarget.value;
|
|
816
854
|
const cursorPos = event.target.selectionStart;
|
|
817
855
|
change(v);
|
|
818
|
-
const valueAsNumber$1 =
|
|
856
|
+
const valueAsNumber$1 = parseValue(v, units, digit).rawValue;
|
|
819
857
|
onChange?.(valueAsNumber$1, v);
|
|
820
858
|
if (cursorPos) {
|
|
821
859
|
event.target.selectionStart = cursorPos;
|
|
@@ -825,16 +863,16 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
825
863
|
onFocus: (event) => {
|
|
826
864
|
setEditing(true);
|
|
827
865
|
const v = event.currentTarget.value;
|
|
828
|
-
const parsed =
|
|
866
|
+
const parsed = parseValue(v, units, digit);
|
|
829
867
|
if (selectWithFocus == "all") event.currentTarget.setSelectionRange(0, v.length);
|
|
830
868
|
else if (selectWithFocus == "number") event.currentTarget.setSelectionRange(0, parsed.formatValue.length - parsed.unit.length);
|
|
831
869
|
onFocus?.(parsed.rawValue, parsed.formatValue, event);
|
|
832
870
|
},
|
|
833
871
|
onBlur: (event) => {
|
|
834
872
|
setEditing(false);
|
|
835
|
-
let v =
|
|
873
|
+
let v = parseValue(event.currentTarget.value, units, digit).rawValue;
|
|
836
874
|
if (clampValueOnBlur) v = safeClamp(v, min, max);
|
|
837
|
-
const parsed =
|
|
875
|
+
const parsed = parseValue(String(v), units, digit);
|
|
838
876
|
change(parsed.formatValue);
|
|
839
877
|
if (clampValueOnBlur) onChange?.(parsed.rawValue, parsed.formatValue);
|
|
840
878
|
onBlur?.(parsed.rawValue, parsed.formatValue, event);
|
|
@@ -1484,6 +1522,22 @@ function ScaleOption({ value, type = "mark-number", label, thickness = 1, length
|
|
|
1484
1522
|
});
|
|
1485
1523
|
}
|
|
1486
1524
|
|
|
1525
|
+
//#endregion
|
|
1526
|
+
//#region src/components/Slider/type.ts
|
|
1527
|
+
function generateOptionsList(options, min, max, step) {
|
|
1528
|
+
const optionsList = [];
|
|
1529
|
+
const per = options[0] == "step" ? step : options[0];
|
|
1530
|
+
const count = Math.floor(max / per) - Math.ceil(min / per) + 1;
|
|
1531
|
+
for (let i = 0; i < count; i++) {
|
|
1532
|
+
const value = (0, __tremolo_ui_functions.toFixed)(per * (Math.ceil(min / per) + i), (0, __tremolo_ui_functions.decimalPart)(per)?.length);
|
|
1533
|
+
optionsList.push({
|
|
1534
|
+
value,
|
|
1535
|
+
type: options[1]
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
return optionsList;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1487
1541
|
//#endregion
|
|
1488
1542
|
//#region src/components/Slider/Scale.tsx
|
|
1489
1543
|
/** @category Slider */
|
|
@@ -1493,7 +1547,7 @@ function Scale({ gap = 6, options, children, className, style,...props }) {
|
|
|
1493
1547
|
const step = useSliderContext((s) => s.step);
|
|
1494
1548
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1495
1549
|
const reverse = useSliderContext((s) => s.reverse);
|
|
1496
|
-
const optionsList = options ?
|
|
1550
|
+
const optionsList = options ? generateOptionsList(options, min, max, step) : [];
|
|
1497
1551
|
if ((0, __tremolo_ui_functions.xor)(vertical, reverse)) optionsList.reverse();
|
|
1498
1552
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1499
1553
|
className: (0, clsx.default)("tremolo-slider-scale", className),
|