sliftutils 1.9.1 → 1.9.3
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/index.d.ts +1 -0
- package/package.json +1 -1
- package/render-utils/Input.d.ts +1 -0
- package/render-utils/Input.tsx +22 -7
- package/render-utils/InputLabel.tsx +1 -5
- package/render-utils/drag.ts +81 -16
package/index.d.ts
CHANGED
|
@@ -554,6 +554,7 @@ declare module "sliftutils/render-utils/GenericFormat" {
|
|
|
554
554
|
|
|
555
555
|
declare module "sliftutils/render-utils/Input" {
|
|
556
556
|
import preact from "preact";
|
|
557
|
+
export declare function roundToDecimals(value: number, decimals: number): number;
|
|
557
558
|
export type InputProps = (preact.JSX.HTMLAttributes<HTMLInputElement> & {
|
|
558
559
|
/** ONLY throttles onChangeValue */
|
|
559
560
|
throttle?: number;
|
package/package.json
CHANGED
package/render-utils/Input.d.ts
CHANGED
package/render-utils/Input.tsx
CHANGED
|
@@ -3,6 +3,22 @@ import { css } from "typesafecss";
|
|
|
3
3
|
import { observer } from "./observer";
|
|
4
4
|
import { throttleFunction } from "socket-function/src/misc";
|
|
5
5
|
|
|
6
|
+
const ARROW_KEY_SHIFT_FACTOR = 10;
|
|
7
|
+
|
|
8
|
+
export function roundToDecimals(value: number, decimals: number) {
|
|
9
|
+
return Math.round(value * 10 ** decimals) / 10 ** decimals;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function countDecimals(value: number) {
|
|
13
|
+
let text = String(value);
|
|
14
|
+
if (text.includes("e-")) {
|
|
15
|
+
return Number(text.split("e-")[1]);
|
|
16
|
+
}
|
|
17
|
+
let dot = text.indexOf(".");
|
|
18
|
+
if (dot < 0) return 0;
|
|
19
|
+
return text.length - dot - 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
6
22
|
// TODO: Autogrow mode while typing
|
|
7
23
|
|
|
8
24
|
// NOTE: "value" is optional. If you don't pass "value", we will preserve the value.
|
|
@@ -201,13 +217,11 @@ export class Input extends preact.Component<InputProps> {
|
|
|
201
217
|
}
|
|
202
218
|
if (this.elem && props.type === "number") {
|
|
203
219
|
let delta = 0;
|
|
204
|
-
let magnitude = 1;
|
|
220
|
+
let magnitude = Number(props.step) || 1;
|
|
205
221
|
if (e.shiftKey) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
magnitude = 0.1;
|
|
210
|
-
}
|
|
222
|
+
magnitude *= ARROW_KEY_SHIFT_FACTOR;
|
|
223
|
+
} else if (e.ctrlKey && !props.integer) {
|
|
224
|
+
magnitude /= ARROW_KEY_SHIFT_FACTOR;
|
|
211
225
|
}
|
|
212
226
|
if (e.code === "ArrowUp") {
|
|
213
227
|
delta = magnitude;
|
|
@@ -219,7 +233,8 @@ export class Input extends preact.Component<InputProps> {
|
|
|
219
233
|
}
|
|
220
234
|
if (delta !== 0) {
|
|
221
235
|
e.preventDefault();
|
|
222
|
-
let
|
|
236
|
+
let decimals = Math.max(countDecimals(magnitude), countDecimals(+this.elem.value || 0));
|
|
237
|
+
let newValue = roundToDecimals((+this.elem.value || 0) + delta, decimals);
|
|
223
238
|
e.currentTarget.value = newValue.toString();
|
|
224
239
|
callback?.(e as unknown as preact.JSX.TargetedInputEvent<HTMLInputElement>);
|
|
225
240
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import preact from "preact";
|
|
2
|
-
import { Input } from "./Input";
|
|
2
|
+
import { Input, roundToDecimals } from "./Input";
|
|
3
3
|
import { css } from "typesafecss";
|
|
4
4
|
import { lazy } from "socket-function/src/caching";
|
|
5
5
|
import { observer } from "./observer";
|
|
@@ -68,10 +68,6 @@ export type InputLabelProps = Omit<InputProps, "label" | "title"> & {
|
|
|
68
68
|
useDateUI?: boolean;
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
function roundToDecimals(value: number, decimals: number) {
|
|
72
|
-
return Math.round(value * 10 ** decimals) / 10 ** decimals;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
71
|
export const startGuessDateRange = +new Date(2010, 0, 1).getTime();
|
|
76
72
|
export const endGuessDateRange = +new Date(2050, 0, 1).getTime();
|
|
77
73
|
|
package/render-utils/drag.ts
CHANGED
|
@@ -3,6 +3,16 @@ import { runInAction } from "./mobxTyped";
|
|
|
3
3
|
|
|
4
4
|
export let dragCount = 0;
|
|
5
5
|
|
|
6
|
+
const ARROW_STEP_PER_FRAME = 1;
|
|
7
|
+
const ARROW_STEP_PER_FRAME_SHIFT = 10;
|
|
8
|
+
const ARROW_STEP_PER_FRAME_CTRL = 0.1;
|
|
9
|
+
const ARROW_DIRECTIONS: { [code: string]: DragOffset } = {
|
|
10
|
+
ArrowUp: { x: 0, y: -1 },
|
|
11
|
+
ArrowDown: { x: 0, y: 1 },
|
|
12
|
+
ArrowLeft: { x: -1, y: 0 },
|
|
13
|
+
ArrowRight: { x: 1, y: 0 },
|
|
14
|
+
};
|
|
15
|
+
|
|
6
16
|
type DragOffset = { x: number; y: number };
|
|
7
17
|
|
|
8
18
|
function throttleFunctionFast<Args extends unknown[]>(
|
|
@@ -60,12 +70,30 @@ export function performDrag2(
|
|
|
60
70
|
let startMouseY = e.clientY;
|
|
61
71
|
let lastMouseX = e.clientX;
|
|
62
72
|
let lastMouseY = e.clientY;
|
|
73
|
+
let keyX = 0;
|
|
74
|
+
let keyY = 0;
|
|
75
|
+
let heldArrows = new Set<string>();
|
|
76
|
+
let shiftHeld = e.shiftKey;
|
|
77
|
+
let ctrlHeld = e.ctrlKey;
|
|
78
|
+
let arrowRepeatRunning = false;
|
|
63
79
|
|
|
64
80
|
let passedSlop = !slop;
|
|
65
81
|
|
|
82
|
+
function getDelta(): DragOffset {
|
|
83
|
+
let deltaX = lastMouseX - startMouseX + keyX;
|
|
84
|
+
let deltaY = lastMouseY - startMouseY + keyY;
|
|
85
|
+
if (slop) {
|
|
86
|
+
let dist = Math.sqrt(deltaX ** 2 + deltaY ** 2);
|
|
87
|
+
if (dist > slop) passedSlop = true;
|
|
88
|
+
}
|
|
89
|
+
return { x: deltaX, y: deltaY };
|
|
90
|
+
}
|
|
91
|
+
|
|
66
92
|
function cancel() {
|
|
67
93
|
lastMouseX = startMouseX;
|
|
68
94
|
lastMouseY = startMouseY;
|
|
95
|
+
keyX = 0;
|
|
96
|
+
keyY = 0;
|
|
69
97
|
finish();
|
|
70
98
|
}
|
|
71
99
|
|
|
@@ -74,17 +102,41 @@ export function performDrag2(
|
|
|
74
102
|
|
|
75
103
|
const triggerMove = throttleFunctionFast(async () => {
|
|
76
104
|
if (disposed) return;
|
|
77
|
-
let
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
let dist = Math.sqrt(deltaX ** 2 + deltaY ** 2);
|
|
81
|
-
if (dist > slop) passedSlop = true;
|
|
82
|
-
if (!passedSlop) return;
|
|
83
|
-
}
|
|
84
|
-
onMove({ x: deltaX, y: deltaY });
|
|
105
|
+
let delta = getDelta();
|
|
106
|
+
if (!passedSlop) return;
|
|
107
|
+
onMove(delta);
|
|
85
108
|
await new Promise(r => requestAnimationFrame(r));
|
|
86
109
|
});
|
|
87
110
|
|
|
111
|
+
function applyArrowStep() {
|
|
112
|
+
let step = ARROW_STEP_PER_FRAME;
|
|
113
|
+
if (shiftHeld) {
|
|
114
|
+
step = ARROW_STEP_PER_FRAME_SHIFT;
|
|
115
|
+
} else if (ctrlHeld) {
|
|
116
|
+
step = ARROW_STEP_PER_FRAME_CTRL;
|
|
117
|
+
}
|
|
118
|
+
for (let code of heldArrows) {
|
|
119
|
+
let direction = ARROW_DIRECTIONS[code];
|
|
120
|
+
keyX += direction.x * step;
|
|
121
|
+
keyY += direction.y * step;
|
|
122
|
+
}
|
|
123
|
+
triggerMove();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function runArrowRepeat() {
|
|
127
|
+
if (arrowRepeatRunning) return;
|
|
128
|
+
arrowRepeatRunning = true;
|
|
129
|
+
const frame = () => {
|
|
130
|
+
if (finished || !heldArrows.size) {
|
|
131
|
+
arrowRepeatRunning = false;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
applyArrowStep();
|
|
135
|
+
requestAnimationFrame(frame);
|
|
136
|
+
};
|
|
137
|
+
requestAnimationFrame(frame);
|
|
138
|
+
}
|
|
139
|
+
|
|
88
140
|
const onMouseMove = (e: MouseEvent) => {
|
|
89
141
|
if (finished) return;
|
|
90
142
|
if (e.buttons === 0) {
|
|
@@ -106,16 +158,12 @@ export function performDrag2(
|
|
|
106
158
|
window.removeEventListener("mouseup", finish, { capture: true });
|
|
107
159
|
window.removeEventListener("blur", onBlur);
|
|
108
160
|
document.removeEventListener("keydown", keyDown);
|
|
161
|
+
document.removeEventListener("keyup", keyUp);
|
|
109
162
|
triggerMove();
|
|
110
163
|
if (onDone) {
|
|
111
|
-
let
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
let dist = Math.sqrt(deltaX ** 2 + deltaY ** 2);
|
|
115
|
-
if (dist > slop) passedSlop = true;
|
|
116
|
-
if (!passedSlop) return;
|
|
117
|
-
}
|
|
118
|
-
onDone({ x: deltaX, y: deltaY });
|
|
164
|
+
let delta = getDelta();
|
|
165
|
+
if (!passedSlop) return;
|
|
166
|
+
onDone(delta);
|
|
119
167
|
}
|
|
120
168
|
} finally {
|
|
121
169
|
setTimeout(() => {
|
|
@@ -127,9 +175,25 @@ export function performDrag2(
|
|
|
127
175
|
|
|
128
176
|
const keyDown = (e: KeyboardEvent) => {
|
|
129
177
|
if (finished) return;
|
|
178
|
+
shiftHeld = e.shiftKey;
|
|
179
|
+
ctrlHeld = e.ctrlKey;
|
|
130
180
|
if (e.code === "Escape") {
|
|
131
181
|
cancel();
|
|
182
|
+
return;
|
|
132
183
|
}
|
|
184
|
+
if (!ARROW_DIRECTIONS[e.code]) return;
|
|
185
|
+
e.preventDefault();
|
|
186
|
+
if (e.repeat || heldArrows.has(e.code)) return;
|
|
187
|
+
heldArrows.add(e.code);
|
|
188
|
+
applyArrowStep();
|
|
189
|
+
runArrowRepeat();
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const keyUp = (e: KeyboardEvent) => {
|
|
193
|
+
if (finished) return;
|
|
194
|
+
shiftHeld = e.shiftKey;
|
|
195
|
+
ctrlHeld = e.ctrlKey;
|
|
196
|
+
heldArrows.delete(e.code);
|
|
133
197
|
};
|
|
134
198
|
|
|
135
199
|
const onBlur = () => {
|
|
@@ -141,6 +205,7 @@ export function performDrag2(
|
|
|
141
205
|
window.addEventListener("mouseup", finish, { capture: true });
|
|
142
206
|
window.addEventListener("blur", onBlur);
|
|
143
207
|
document.addEventListener("keydown", keyDown);
|
|
208
|
+
document.addEventListener("keyup", keyUp);
|
|
144
209
|
selfCancelCallback.add(cancel);
|
|
145
210
|
|
|
146
211
|
if (!slop) {
|