@dcg-overseas/number-line 0.1.30 → 1.0.0-beta.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/README.md +10 -12
- package/dist/index.cjs +157 -53
- package/dist/index.d.ts +6 -0
- package/dist/index.js +157 -53
- package/package.json +47 -47
package/README.md
CHANGED
|
@@ -210,11 +210,12 @@ function App() {
|
|
|
210
210
|
| `canDelete` | `boolean` | 是否有可删除的选中项(笔迹或弧线) |
|
|
211
211
|
| `canUndo` | `boolean` | 是否有可撤销的操作 |
|
|
212
212
|
| `onTogglePen` | `() => void` | 切换画笔工具 |
|
|
213
|
-
| `onToggleEraser` | `() => void` | 切换橡皮擦 |
|
|
214
|
-
| `onDelete` | `() => void` | 删除当前选中 |
|
|
215
|
-
| `
|
|
213
|
+
| `onToggleEraser` | `() => void` | 切换橡皮擦 |
|
|
214
|
+
| `onDelete` | `() => void` | 删除当前选中 |
|
|
215
|
+
| `clearAll` | `() => void` | 可撤销地清空全部笔迹、弧线、选择和分组边界粗刻度;连续清空不会增加空历史项 |
|
|
216
|
+
| `onReset` | `() => void` | `clearAll` 的兼容别名,已弃用;新代码请使用 `clearAll` |
|
|
216
217
|
| `onUndo` | `() => void` | 撤销上一步操作 |
|
|
217
|
-
| `showAllArcs` | `() => void` | 重新显示全部跳数弧线**及分组边界粗刻度**(清空删除记录)。即使参数未变、或刚 `
|
|
218
|
+
| `showAllArcs` | `() => void` | 重新显示全部跳数弧线**及分组边界粗刻度**(清空删除记录)。即使参数未变、或刚 `clearAll` 隐藏后,也能强制把跳数显示出来,且不影响笔迹 |
|
|
218
219
|
|
|
219
220
|
#### 缩放 API(仅 `enableZoom=true` 时有效)
|
|
220
221
|
| 字段 | 类型 | 说明 |
|
|
@@ -234,7 +235,7 @@ function App() {
|
|
|
234
235
|
import { NumberLineProvider, NumberLine, useNumberLineContext } from '@dcg-overseas/number-line'
|
|
235
236
|
|
|
236
237
|
function Toolbar() {
|
|
237
|
-
const { tool, canDelete, canUndo, onTogglePen, onToggleEraser, onDelete,
|
|
238
|
+
const { tool, canDelete, canUndo, onTogglePen, onToggleEraser, onDelete, clearAll, onUndo } =
|
|
238
239
|
useNumberLineContext()
|
|
239
240
|
|
|
240
241
|
return (
|
|
@@ -242,7 +243,7 @@ function Toolbar() {
|
|
|
242
243
|
<button data-active={tool === 'pen'} onClick={onTogglePen}>✏️ 画笔</button>
|
|
243
244
|
<button data-active={tool === 'eraser'} onClick={onToggleEraser}>🩹 擦除</button>
|
|
244
245
|
<button disabled={!canDelete} onClick={onDelete}>🗑 删除</button>
|
|
245
|
-
<button onClick={
|
|
246
|
+
<button onClick={clearAll}>♻️ 全部清除</button>
|
|
246
247
|
<button disabled={!canUndo} onClick={onUndo}>↶ 撤销</button>
|
|
247
248
|
</div>
|
|
248
249
|
)
|
|
@@ -304,12 +305,9 @@ import type {
|
|
|
304
305
|
|
|
305
306
|
### 操作级撤销
|
|
306
307
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
- `delete` — 删除笔迹/弧线,撤销 = 恢复删除前完整快照 + 还原弧线 group index
|
|
311
|
-
|
|
312
|
-
这样即使一次 `delete` 同时删了多条笔迹和多条弧线,撤销也能精确还原。
|
|
308
|
+
撤销栈记录每次操作发生前的完整状态快照,包括笔迹、已删除弧线、已选弧线和粗分组刻度状态。
|
|
309
|
+
画笔、橡皮擦、删除选中及 `clearAll` 都复用同一恢复流程,因此一次清空或批量删除可以完整还原。
|
|
310
|
+
`min`、`max`、`groupSize`、`groupCount` 或 `tickStep` 改变时会执行不可撤销初始化并清空历史,防止把上一道题的数据恢复到新坐标范围。
|
|
313
311
|
|
|
314
312
|
---
|
|
315
313
|
|
package/dist/index.cjs
CHANGED
|
@@ -5,27 +5,16 @@ const React = require("react");
|
|
|
5
5
|
function useHistory() {
|
|
6
6
|
const stackRef = React.useRef([]);
|
|
7
7
|
const [stackLen, setStackLen] = React.useState(0);
|
|
8
|
-
const push = React.useCallback((
|
|
9
|
-
stackRef.current = [...stackRef.current,
|
|
8
|
+
const push = React.useCallback((snapshot) => {
|
|
9
|
+
stackRef.current = [...stackRef.current, snapshot];
|
|
10
10
|
setStackLen(stackRef.current.length);
|
|
11
11
|
}, []);
|
|
12
|
-
const undo = React.useCallback((
|
|
13
|
-
if (stackRef.current.length === 0) return
|
|
14
|
-
const
|
|
12
|
+
const undo = React.useCallback(() => {
|
|
13
|
+
if (stackRef.current.length === 0) return null;
|
|
14
|
+
const snapshot = stackRef.current[stackRef.current.length - 1];
|
|
15
15
|
stackRef.current = stackRef.current.slice(0, -1);
|
|
16
16
|
setStackLen(stackRef.current.length);
|
|
17
|
-
|
|
18
|
-
case "add":
|
|
19
|
-
return {
|
|
20
|
-
strokes: strokes.filter((s) => !op.strokes.some((os) => os.id === s.id)),
|
|
21
|
-
arcIndices: []
|
|
22
|
-
};
|
|
23
|
-
case "delete":
|
|
24
|
-
return {
|
|
25
|
-
strokes: op.strokesBefore,
|
|
26
|
-
arcIndices: op.arcIndices
|
|
27
|
-
};
|
|
28
|
-
}
|
|
17
|
+
return snapshot;
|
|
29
18
|
}, []);
|
|
30
19
|
const clear = React.useCallback(() => {
|
|
31
20
|
stackRef.current = [];
|
|
@@ -247,6 +236,19 @@ function normalizeDomain(lo, hi) {
|
|
|
247
236
|
}
|
|
248
237
|
return { min: x, max: y };
|
|
249
238
|
}
|
|
239
|
+
function eraseRadius(pointerType) {
|
|
240
|
+
return pointerType === "touch" ? 28 : 16;
|
|
241
|
+
}
|
|
242
|
+
function strokeWithinRadius(d, px, py, radius, w, h) {
|
|
243
|
+
const nums = d.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi);
|
|
244
|
+
if (!nums) return false;
|
|
245
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
246
|
+
const x = parseFloat(nums[i]) * w;
|
|
247
|
+
const y = parseFloat(nums[i + 1]) * h;
|
|
248
|
+
if (Math.hypot(x - px, y - py) < radius) return true;
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
250
252
|
function NumberLineProvider({
|
|
251
253
|
min,
|
|
252
254
|
max,
|
|
@@ -285,11 +287,35 @@ function NumberLineProvider({
|
|
|
285
287
|
});
|
|
286
288
|
const [liveStroke, setLiveStroke] = React.useState(null);
|
|
287
289
|
const svgPoint = React.useRef(null);
|
|
290
|
+
const [eraseHoverIds, setEraseHoverIds] = React.useState(/* @__PURE__ */ new Set());
|
|
291
|
+
const eraseGesture = React.useRef(null);
|
|
292
|
+
const createHistorySnapshot = React.useCallback(
|
|
293
|
+
(strokesOverride) => ({
|
|
294
|
+
strokes: (strokesOverride ?? drawing.strokes).map((stroke) => ({ ...stroke })),
|
|
295
|
+
deletedArcIndices: [...deletedArcIndices],
|
|
296
|
+
selectedArcIndices: [...selectedArcIndices],
|
|
297
|
+
groupTicksHidden
|
|
298
|
+
}),
|
|
299
|
+
[drawing.strokes, deletedArcIndices, selectedArcIndices, groupTicksHidden]
|
|
300
|
+
);
|
|
288
301
|
React.useEffect(() => {
|
|
302
|
+
drawing.resetStrokes();
|
|
303
|
+
history.clear();
|
|
304
|
+
setLiveStroke(null);
|
|
305
|
+
eraseGesture.current = null;
|
|
306
|
+
setEraseHoverIds(/* @__PURE__ */ new Set());
|
|
289
307
|
setDeletedArcIndices(/* @__PURE__ */ new Set());
|
|
290
308
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
291
309
|
setGroupTicksHidden(false);
|
|
292
|
-
}, [
|
|
310
|
+
}, [
|
|
311
|
+
domainMin,
|
|
312
|
+
domainMax,
|
|
313
|
+
groupSize,
|
|
314
|
+
groupCount,
|
|
315
|
+
tickStep,
|
|
316
|
+
drawing.resetStrokes,
|
|
317
|
+
history.clear
|
|
318
|
+
]);
|
|
293
319
|
const getLocalCoords = React.useCallback(
|
|
294
320
|
(e) => {
|
|
295
321
|
const svg = svgRef.current;
|
|
@@ -304,51 +330,107 @@ function NumberLineProvider({
|
|
|
304
330
|
},
|
|
305
331
|
[svgRef]
|
|
306
332
|
);
|
|
333
|
+
const strokeIdsNear = React.useCallback(
|
|
334
|
+
(x, y, radius) => {
|
|
335
|
+
const ids = [];
|
|
336
|
+
for (const s of drawing.strokes) {
|
|
337
|
+
if (strokeWithinRadius(s.d, x, y, radius, containerWidth, containerHeight)) {
|
|
338
|
+
ids.push(s.id);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return ids;
|
|
342
|
+
},
|
|
343
|
+
[drawing.strokes, containerWidth, containerHeight]
|
|
344
|
+
);
|
|
345
|
+
const eraseAt = React.useCallback(
|
|
346
|
+
(x, y, radius) => {
|
|
347
|
+
const gesture = eraseGesture.current;
|
|
348
|
+
if (!gesture) return;
|
|
349
|
+
for (const id of strokeIdsNear(x, y, radius)) {
|
|
350
|
+
if (!gesture.removed.has(id)) {
|
|
351
|
+
gesture.removed.add(id);
|
|
352
|
+
drawing.eraseStroke(id);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
[strokeIdsNear, drawing]
|
|
357
|
+
);
|
|
307
358
|
const onPointerDown = React.useCallback(
|
|
308
359
|
(e) => {
|
|
309
360
|
e.currentTarget.focus();
|
|
361
|
+
if (drawing.tool === "eraser") {
|
|
362
|
+
if (eraseGesture.current) return;
|
|
363
|
+
const coords2 = getLocalCoords(e);
|
|
364
|
+
if (!coords2) return;
|
|
365
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
366
|
+
eraseGesture.current = { before: drawing.strokes, removed: /* @__PURE__ */ new Set() };
|
|
367
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
368
|
+
eraseAt(coords2[0], coords2[1], eraseRadius(e.pointerType));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
310
371
|
if (drawing.tool !== "pen") return;
|
|
311
372
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
312
373
|
const coords = getLocalCoords(e);
|
|
313
374
|
if (!coords) return;
|
|
314
375
|
drawing.startStroke(coords[0], coords[1], containerWidth, containerHeight);
|
|
315
376
|
},
|
|
316
|
-
[drawing, getLocalCoords, containerWidth, containerHeight]
|
|
377
|
+
[drawing, getLocalCoords, containerWidth, containerHeight, eraseAt]
|
|
317
378
|
);
|
|
318
379
|
const onPointerMove = React.useCallback(
|
|
319
380
|
(e) => {
|
|
381
|
+
if (drawing.tool === "eraser") {
|
|
382
|
+
const coords2 = getLocalCoords(e);
|
|
383
|
+
if (!coords2) return;
|
|
384
|
+
const radius = eraseRadius(e.pointerType);
|
|
385
|
+
if (eraseGesture.current) {
|
|
386
|
+
eraseAt(coords2[0], coords2[1], radius);
|
|
387
|
+
} else {
|
|
388
|
+
const nextIds = strokeIdsNear(coords2[0], coords2[1], radius);
|
|
389
|
+
setEraseHoverIds((prev) => {
|
|
390
|
+
if (nextIds.length === prev.size && nextIds.every((id) => prev.has(id)))
|
|
391
|
+
return prev;
|
|
392
|
+
return new Set(nextIds);
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
320
397
|
if (drawing.tool !== "pen") return;
|
|
321
398
|
const coords = getLocalCoords(e);
|
|
322
399
|
if (!coords) return;
|
|
323
400
|
const live = drawing.continueStroke(coords[0], coords[1], containerWidth, containerHeight);
|
|
324
401
|
if (live) setLiveStroke(live);
|
|
325
402
|
},
|
|
326
|
-
[drawing, getLocalCoords, containerWidth, containerHeight]
|
|
403
|
+
[drawing, getLocalCoords, containerWidth, containerHeight, eraseAt, strokeIdsNear]
|
|
327
404
|
);
|
|
328
405
|
const onPointerUp = React.useCallback(() => {
|
|
406
|
+
const gesture = eraseGesture.current;
|
|
407
|
+
if (gesture) {
|
|
408
|
+
eraseGesture.current = null;
|
|
409
|
+
if (gesture.removed.size > 0) {
|
|
410
|
+
history.push(createHistorySnapshot(gesture.before));
|
|
411
|
+
}
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
329
414
|
if (drawing.tool !== "pen") return;
|
|
330
415
|
const stroke = drawing.endStroke();
|
|
331
416
|
setLiveStroke(null);
|
|
332
417
|
if (stroke) {
|
|
418
|
+
history.push(createHistorySnapshot());
|
|
333
419
|
drawing.addStroke(stroke);
|
|
334
|
-
history.push({ type: "add", strokes: [stroke] });
|
|
335
420
|
}
|
|
336
|
-
}, [drawing, history]);
|
|
421
|
+
}, [drawing, history, createHistorySnapshot]);
|
|
422
|
+
const onPointerLeave = React.useCallback(() => {
|
|
423
|
+
if (eraseGesture.current) return;
|
|
424
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
425
|
+
}, []);
|
|
337
426
|
const onStrokeClick = React.useCallback(
|
|
338
427
|
(id, e) => {
|
|
339
428
|
e.stopPropagation();
|
|
340
|
-
if (drawing.tool === "
|
|
341
|
-
const target = drawing.strokes.find((s) => s.id === id);
|
|
342
|
-
if (target) {
|
|
343
|
-
const strokesBefore = drawing.strokes;
|
|
344
|
-
drawing.eraseStroke(id);
|
|
345
|
-
history.push({ type: "delete", strokesBefore, arcIndices: [] });
|
|
346
|
-
}
|
|
347
|
-
} else if (drawing.tool === "none") {
|
|
429
|
+
if (drawing.tool === "none") {
|
|
348
430
|
drawing.toggleSelect(id);
|
|
349
431
|
}
|
|
350
432
|
},
|
|
351
|
-
[drawing
|
|
433
|
+
[drawing]
|
|
352
434
|
);
|
|
353
435
|
const onArcClick = React.useCallback(
|
|
354
436
|
(groupIndex, e) => {
|
|
@@ -366,39 +448,55 @@ function NumberLineProvider({
|
|
|
366
448
|
);
|
|
367
449
|
const clearArcSelection = React.useCallback(() => setSelectedArcIndices(/* @__PURE__ */ new Set()), []);
|
|
368
450
|
const onDelete = React.useCallback(() => {
|
|
369
|
-
const strokesBefore = drawing.strokes;
|
|
370
451
|
const deletedStrokes = drawing.strokes.filter((s) => s.selected);
|
|
371
452
|
const deletedArcs = [...selectedArcIndices];
|
|
372
453
|
if (deletedStrokes.length === 0 && deletedArcs.length === 0) return;
|
|
454
|
+
history.push(createHistorySnapshot());
|
|
373
455
|
if (deletedStrokes.length > 0) drawing.deleteSelected();
|
|
374
456
|
if (deletedArcs.length > 0) {
|
|
375
457
|
setDeletedArcIndices((prev) => /* @__PURE__ */ new Set([...prev, ...deletedArcs]));
|
|
376
458
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
377
459
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const onReset = React.useCallback(() => {
|
|
381
|
-
drawing.resetStrokes();
|
|
382
|
-
history.clear();
|
|
460
|
+
}, [drawing, history, selectedArcIndices, createHistorySnapshot]);
|
|
461
|
+
const clearAll = React.useCallback(() => {
|
|
383
462
|
const arcCount = Math.max(0, Math.ceil(Number(groupCount) || 0));
|
|
463
|
+
const allArcsDeleted = deletedArcIndices.size >= arcCount && Array.from({ length: arcCount }, (_, index) => index).every(
|
|
464
|
+
(index) => deletedArcIndices.has(index)
|
|
465
|
+
);
|
|
466
|
+
const alreadyClear = drawing.strokes.length === 0 && selectedArcIndices.size === 0 && allArcsDeleted && groupTicksHidden;
|
|
467
|
+
if (alreadyClear) return;
|
|
468
|
+
history.push(createHistorySnapshot());
|
|
469
|
+
drawing.resetStrokes();
|
|
470
|
+
setLiveStroke(null);
|
|
471
|
+
eraseGesture.current = null;
|
|
472
|
+
setEraseHoverIds(/* @__PURE__ */ new Set());
|
|
384
473
|
setDeletedArcIndices(new Set(Array.from({ length: arcCount }, (_, i) => i)));
|
|
385
474
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
386
475
|
setGroupTicksHidden(true);
|
|
387
|
-
}, [
|
|
476
|
+
}, [
|
|
477
|
+
drawing,
|
|
478
|
+
history,
|
|
479
|
+
groupCount,
|
|
480
|
+
deletedArcIndices,
|
|
481
|
+
selectedArcIndices,
|
|
482
|
+
groupTicksHidden,
|
|
483
|
+
createHistorySnapshot
|
|
484
|
+
]);
|
|
485
|
+
const onReset = clearAll;
|
|
388
486
|
const showAllArcs = React.useCallback(() => {
|
|
389
487
|
setDeletedArcIndices((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
390
488
|
setGroupTicksHidden(false);
|
|
391
489
|
}, []);
|
|
392
490
|
const onUndo = React.useCallback(() => {
|
|
393
|
-
const
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
491
|
+
const snapshot = history.undo();
|
|
492
|
+
if (!snapshot) return;
|
|
493
|
+
drawing.restoreStrokes(snapshot.strokes);
|
|
494
|
+
setDeletedArcIndices(new Set(snapshot.deletedArcIndices));
|
|
495
|
+
setSelectedArcIndices(new Set(snapshot.selectedArcIndices));
|
|
496
|
+
setGroupTicksHidden(snapshot.groupTicksHidden);
|
|
497
|
+
setLiveStroke(null);
|
|
498
|
+
eraseGesture.current = null;
|
|
499
|
+
setEraseHoverIds(/* @__PURE__ */ new Set());
|
|
402
500
|
}, [history, drawing]);
|
|
403
501
|
const onTogglePen = React.useCallback(
|
|
404
502
|
() => {
|
|
@@ -417,6 +515,7 @@ function NumberLineProvider({
|
|
|
417
515
|
const canDelete = drawing.strokes.some((s) => s.selected) || selectedArcIndices.size > 0;
|
|
418
516
|
React.useEffect(() => {
|
|
419
517
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
518
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
420
519
|
}, [drawing.tool]);
|
|
421
520
|
const keyHandlers = React.useRef({ toggleTool: drawing.toggleTool, clearArcSelection, canDelete, onDelete, onUndo });
|
|
422
521
|
keyHandlers.current = { toggleTool: drawing.toggleTool, clearArcSelection, canDelete, onDelete, onUndo };
|
|
@@ -468,11 +567,13 @@ function NumberLineProvider({
|
|
|
468
567
|
tool: drawing.tool,
|
|
469
568
|
strokes: drawing.strokes,
|
|
470
569
|
liveStroke,
|
|
570
|
+
eraseHoverIds,
|
|
471
571
|
selectedArcIndices,
|
|
472
572
|
deletedArcIndices,
|
|
473
573
|
onPointerDown,
|
|
474
574
|
onPointerMove,
|
|
475
575
|
onPointerUp,
|
|
576
|
+
onPointerLeave,
|
|
476
577
|
onStrokeClick,
|
|
477
578
|
onArcClick,
|
|
478
579
|
canDelete,
|
|
@@ -480,6 +581,7 @@ function NumberLineProvider({
|
|
|
480
581
|
onTogglePen,
|
|
481
582
|
onToggleEraser,
|
|
482
583
|
onDelete,
|
|
584
|
+
clearAll,
|
|
483
585
|
onReset,
|
|
484
586
|
onUndo,
|
|
485
587
|
showAllArcs
|
|
@@ -916,11 +1018,10 @@ const GroupArcsLayer = React.memo(function GroupArcsLayer2({
|
|
|
916
1018
|
}
|
|
917
1019
|
return /* @__PURE__ */ jsxRuntime.jsx("g", { className: "nl-arcs-layer", children: arcs });
|
|
918
1020
|
});
|
|
919
|
-
function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick }) {
|
|
920
|
-
const [hoveredId, setHoveredId] = React.useState(null);
|
|
1021
|
+
function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds, onStrokeClick }) {
|
|
921
1022
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { className: "nl-drawing-layer", transform: `scale(${width} ${height})`, children: [
|
|
922
1023
|
strokes.map((s) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
923
|
-
(s.selected || tool === "eraser" && s.id
|
|
1024
|
+
(s.selected || tool === "eraser" && eraseHoverIds.has(s.id)) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
924
1025
|
"path",
|
|
925
1026
|
{
|
|
926
1027
|
d: s.d,
|
|
@@ -945,10 +1046,8 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
|
|
|
945
1046
|
vectorEffect: "non-scaling-stroke",
|
|
946
1047
|
pointerEvents: "stroke",
|
|
947
1048
|
style: { cursor: tool === "eraser" ? "cell" : tool === "none" ? "pointer" : "default" },
|
|
948
|
-
onPointerEnter: () => setHoveredId(s.id),
|
|
949
|
-
onPointerLeave: () => setHoveredId((prev) => prev === s.id ? null : prev),
|
|
950
1049
|
onClick: (e) => {
|
|
951
|
-
if (tool === "
|
|
1050
|
+
if (tool === "none") {
|
|
952
1051
|
e.stopPropagation();
|
|
953
1052
|
onStrokeClick(s.id, e);
|
|
954
1053
|
}
|
|
@@ -1015,11 +1114,13 @@ function NumberLine({ className }) {
|
|
|
1015
1114
|
tool,
|
|
1016
1115
|
strokes,
|
|
1017
1116
|
liveStroke,
|
|
1117
|
+
eraseHoverIds,
|
|
1018
1118
|
selectedArcIndices,
|
|
1019
1119
|
deletedArcIndices,
|
|
1020
1120
|
onPointerDown,
|
|
1021
1121
|
onPointerMove,
|
|
1022
1122
|
onPointerUp,
|
|
1123
|
+
onPointerLeave,
|
|
1023
1124
|
onStrokeClick,
|
|
1024
1125
|
onArcClick
|
|
1025
1126
|
} = useNumberLineContext();
|
|
@@ -1186,6 +1287,8 @@ function NumberLine({ className }) {
|
|
|
1186
1287
|
onPointerDown: handlePointerDown,
|
|
1187
1288
|
onPointerMove: handlePointerMove,
|
|
1188
1289
|
onPointerUp: handlePointerUp,
|
|
1290
|
+
onPointerCancel: handlePointerUp,
|
|
1291
|
+
onPointerLeave,
|
|
1189
1292
|
onWheel: handleWheel,
|
|
1190
1293
|
onDoubleClick: handleDoubleClick,
|
|
1191
1294
|
onMouseEnter: () => enableZoom && setIsZoomActive(true),
|
|
@@ -1244,6 +1347,7 @@ function NumberLine({ className }) {
|
|
|
1244
1347
|
tool,
|
|
1245
1348
|
width: w,
|
|
1246
1349
|
height: h,
|
|
1350
|
+
eraseHoverIds,
|
|
1247
1351
|
onStrokeClick
|
|
1248
1352
|
}
|
|
1249
1353
|
)
|
package/dist/index.d.ts
CHANGED
|
@@ -29,11 +29,14 @@ export declare interface NumberLineContextValue {
|
|
|
29
29
|
tool: Tool;
|
|
30
30
|
strokes: Stroke[];
|
|
31
31
|
liveStroke: Stroke | null;
|
|
32
|
+
/** Stroke ids the eraser brush is hovering over (preview of what a press would remove). */
|
|
33
|
+
eraseHoverIds: Set<string>;
|
|
32
34
|
selectedArcIndices: Set<number>;
|
|
33
35
|
deletedArcIndices: Set<number>;
|
|
34
36
|
onPointerDown: (e: default_2.PointerEvent<SVGSVGElement>) => void;
|
|
35
37
|
onPointerMove: (e: default_2.PointerEvent<SVGSVGElement>) => void;
|
|
36
38
|
onPointerUp: () => void;
|
|
39
|
+
onPointerLeave: () => void;
|
|
37
40
|
onStrokeClick: (id: string, e: default_2.PointerEvent) => void;
|
|
38
41
|
onArcClick: (groupIndex: number, e: default_2.PointerEvent) => void;
|
|
39
42
|
canDelete: boolean;
|
|
@@ -41,6 +44,9 @@ export declare interface NumberLineContextValue {
|
|
|
41
44
|
onTogglePen: () => void;
|
|
42
45
|
onToggleEraser: () => void;
|
|
43
46
|
onDelete: () => void;
|
|
47
|
+
/** 可撤销地清空笔迹、弧线、选择和粗分组刻度;已为空时不创建历史项。 */
|
|
48
|
+
clearAll: () => void;
|
|
49
|
+
/** @deprecated 请使用语义明确且行为相同的 `clearAll`。 */
|
|
44
50
|
onReset: () => void;
|
|
45
51
|
onUndo: () => void;
|
|
46
52
|
/** restore (re-show) every arc by clearing the deleted-arc record */
|
package/dist/index.js
CHANGED
|
@@ -3,27 +3,16 @@ import React, { useRef, useState, useCallback, useLayoutEffect, useEffect, creat
|
|
|
3
3
|
function useHistory() {
|
|
4
4
|
const stackRef = useRef([]);
|
|
5
5
|
const [stackLen, setStackLen] = useState(0);
|
|
6
|
-
const push = useCallback((
|
|
7
|
-
stackRef.current = [...stackRef.current,
|
|
6
|
+
const push = useCallback((snapshot) => {
|
|
7
|
+
stackRef.current = [...stackRef.current, snapshot];
|
|
8
8
|
setStackLen(stackRef.current.length);
|
|
9
9
|
}, []);
|
|
10
|
-
const undo = useCallback((
|
|
11
|
-
if (stackRef.current.length === 0) return
|
|
12
|
-
const
|
|
10
|
+
const undo = useCallback(() => {
|
|
11
|
+
if (stackRef.current.length === 0) return null;
|
|
12
|
+
const snapshot = stackRef.current[stackRef.current.length - 1];
|
|
13
13
|
stackRef.current = stackRef.current.slice(0, -1);
|
|
14
14
|
setStackLen(stackRef.current.length);
|
|
15
|
-
|
|
16
|
-
case "add":
|
|
17
|
-
return {
|
|
18
|
-
strokes: strokes.filter((s) => !op.strokes.some((os) => os.id === s.id)),
|
|
19
|
-
arcIndices: []
|
|
20
|
-
};
|
|
21
|
-
case "delete":
|
|
22
|
-
return {
|
|
23
|
-
strokes: op.strokesBefore,
|
|
24
|
-
arcIndices: op.arcIndices
|
|
25
|
-
};
|
|
26
|
-
}
|
|
15
|
+
return snapshot;
|
|
27
16
|
}, []);
|
|
28
17
|
const clear = useCallback(() => {
|
|
29
18
|
stackRef.current = [];
|
|
@@ -245,6 +234,19 @@ function normalizeDomain(lo, hi) {
|
|
|
245
234
|
}
|
|
246
235
|
return { min: x, max: y };
|
|
247
236
|
}
|
|
237
|
+
function eraseRadius(pointerType) {
|
|
238
|
+
return pointerType === "touch" ? 28 : 16;
|
|
239
|
+
}
|
|
240
|
+
function strokeWithinRadius(d, px, py, radius, w, h) {
|
|
241
|
+
const nums = d.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi);
|
|
242
|
+
if (!nums) return false;
|
|
243
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
244
|
+
const x = parseFloat(nums[i]) * w;
|
|
245
|
+
const y = parseFloat(nums[i + 1]) * h;
|
|
246
|
+
if (Math.hypot(x - px, y - py) < radius) return true;
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
248
250
|
function NumberLineProvider({
|
|
249
251
|
min,
|
|
250
252
|
max,
|
|
@@ -283,11 +285,35 @@ function NumberLineProvider({
|
|
|
283
285
|
});
|
|
284
286
|
const [liveStroke, setLiveStroke] = useState(null);
|
|
285
287
|
const svgPoint = useRef(null);
|
|
288
|
+
const [eraseHoverIds, setEraseHoverIds] = useState(/* @__PURE__ */ new Set());
|
|
289
|
+
const eraseGesture = useRef(null);
|
|
290
|
+
const createHistorySnapshot = useCallback(
|
|
291
|
+
(strokesOverride) => ({
|
|
292
|
+
strokes: (strokesOverride ?? drawing.strokes).map((stroke) => ({ ...stroke })),
|
|
293
|
+
deletedArcIndices: [...deletedArcIndices],
|
|
294
|
+
selectedArcIndices: [...selectedArcIndices],
|
|
295
|
+
groupTicksHidden
|
|
296
|
+
}),
|
|
297
|
+
[drawing.strokes, deletedArcIndices, selectedArcIndices, groupTicksHidden]
|
|
298
|
+
);
|
|
286
299
|
useEffect(() => {
|
|
300
|
+
drawing.resetStrokes();
|
|
301
|
+
history.clear();
|
|
302
|
+
setLiveStroke(null);
|
|
303
|
+
eraseGesture.current = null;
|
|
304
|
+
setEraseHoverIds(/* @__PURE__ */ new Set());
|
|
287
305
|
setDeletedArcIndices(/* @__PURE__ */ new Set());
|
|
288
306
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
289
307
|
setGroupTicksHidden(false);
|
|
290
|
-
}, [
|
|
308
|
+
}, [
|
|
309
|
+
domainMin,
|
|
310
|
+
domainMax,
|
|
311
|
+
groupSize,
|
|
312
|
+
groupCount,
|
|
313
|
+
tickStep,
|
|
314
|
+
drawing.resetStrokes,
|
|
315
|
+
history.clear
|
|
316
|
+
]);
|
|
291
317
|
const getLocalCoords = useCallback(
|
|
292
318
|
(e) => {
|
|
293
319
|
const svg = svgRef.current;
|
|
@@ -302,51 +328,107 @@ function NumberLineProvider({
|
|
|
302
328
|
},
|
|
303
329
|
[svgRef]
|
|
304
330
|
);
|
|
331
|
+
const strokeIdsNear = useCallback(
|
|
332
|
+
(x, y, radius) => {
|
|
333
|
+
const ids = [];
|
|
334
|
+
for (const s of drawing.strokes) {
|
|
335
|
+
if (strokeWithinRadius(s.d, x, y, radius, containerWidth, containerHeight)) {
|
|
336
|
+
ids.push(s.id);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return ids;
|
|
340
|
+
},
|
|
341
|
+
[drawing.strokes, containerWidth, containerHeight]
|
|
342
|
+
);
|
|
343
|
+
const eraseAt = useCallback(
|
|
344
|
+
(x, y, radius) => {
|
|
345
|
+
const gesture = eraseGesture.current;
|
|
346
|
+
if (!gesture) return;
|
|
347
|
+
for (const id of strokeIdsNear(x, y, radius)) {
|
|
348
|
+
if (!gesture.removed.has(id)) {
|
|
349
|
+
gesture.removed.add(id);
|
|
350
|
+
drawing.eraseStroke(id);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
[strokeIdsNear, drawing]
|
|
355
|
+
);
|
|
305
356
|
const onPointerDown = useCallback(
|
|
306
357
|
(e) => {
|
|
307
358
|
e.currentTarget.focus();
|
|
359
|
+
if (drawing.tool === "eraser") {
|
|
360
|
+
if (eraseGesture.current) return;
|
|
361
|
+
const coords2 = getLocalCoords(e);
|
|
362
|
+
if (!coords2) return;
|
|
363
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
364
|
+
eraseGesture.current = { before: drawing.strokes, removed: /* @__PURE__ */ new Set() };
|
|
365
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
366
|
+
eraseAt(coords2[0], coords2[1], eraseRadius(e.pointerType));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
308
369
|
if (drawing.tool !== "pen") return;
|
|
309
370
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
310
371
|
const coords = getLocalCoords(e);
|
|
311
372
|
if (!coords) return;
|
|
312
373
|
drawing.startStroke(coords[0], coords[1], containerWidth, containerHeight);
|
|
313
374
|
},
|
|
314
|
-
[drawing, getLocalCoords, containerWidth, containerHeight]
|
|
375
|
+
[drawing, getLocalCoords, containerWidth, containerHeight, eraseAt]
|
|
315
376
|
);
|
|
316
377
|
const onPointerMove = useCallback(
|
|
317
378
|
(e) => {
|
|
379
|
+
if (drawing.tool === "eraser") {
|
|
380
|
+
const coords2 = getLocalCoords(e);
|
|
381
|
+
if (!coords2) return;
|
|
382
|
+
const radius = eraseRadius(e.pointerType);
|
|
383
|
+
if (eraseGesture.current) {
|
|
384
|
+
eraseAt(coords2[0], coords2[1], radius);
|
|
385
|
+
} else {
|
|
386
|
+
const nextIds = strokeIdsNear(coords2[0], coords2[1], radius);
|
|
387
|
+
setEraseHoverIds((prev) => {
|
|
388
|
+
if (nextIds.length === prev.size && nextIds.every((id) => prev.has(id)))
|
|
389
|
+
return prev;
|
|
390
|
+
return new Set(nextIds);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
318
395
|
if (drawing.tool !== "pen") return;
|
|
319
396
|
const coords = getLocalCoords(e);
|
|
320
397
|
if (!coords) return;
|
|
321
398
|
const live = drawing.continueStroke(coords[0], coords[1], containerWidth, containerHeight);
|
|
322
399
|
if (live) setLiveStroke(live);
|
|
323
400
|
},
|
|
324
|
-
[drawing, getLocalCoords, containerWidth, containerHeight]
|
|
401
|
+
[drawing, getLocalCoords, containerWidth, containerHeight, eraseAt, strokeIdsNear]
|
|
325
402
|
);
|
|
326
403
|
const onPointerUp = useCallback(() => {
|
|
404
|
+
const gesture = eraseGesture.current;
|
|
405
|
+
if (gesture) {
|
|
406
|
+
eraseGesture.current = null;
|
|
407
|
+
if (gesture.removed.size > 0) {
|
|
408
|
+
history.push(createHistorySnapshot(gesture.before));
|
|
409
|
+
}
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
327
412
|
if (drawing.tool !== "pen") return;
|
|
328
413
|
const stroke = drawing.endStroke();
|
|
329
414
|
setLiveStroke(null);
|
|
330
415
|
if (stroke) {
|
|
416
|
+
history.push(createHistorySnapshot());
|
|
331
417
|
drawing.addStroke(stroke);
|
|
332
|
-
history.push({ type: "add", strokes: [stroke] });
|
|
333
418
|
}
|
|
334
|
-
}, [drawing, history]);
|
|
419
|
+
}, [drawing, history, createHistorySnapshot]);
|
|
420
|
+
const onPointerLeave = useCallback(() => {
|
|
421
|
+
if (eraseGesture.current) return;
|
|
422
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
423
|
+
}, []);
|
|
335
424
|
const onStrokeClick = useCallback(
|
|
336
425
|
(id, e) => {
|
|
337
426
|
e.stopPropagation();
|
|
338
|
-
if (drawing.tool === "
|
|
339
|
-
const target = drawing.strokes.find((s) => s.id === id);
|
|
340
|
-
if (target) {
|
|
341
|
-
const strokesBefore = drawing.strokes;
|
|
342
|
-
drawing.eraseStroke(id);
|
|
343
|
-
history.push({ type: "delete", strokesBefore, arcIndices: [] });
|
|
344
|
-
}
|
|
345
|
-
} else if (drawing.tool === "none") {
|
|
427
|
+
if (drawing.tool === "none") {
|
|
346
428
|
drawing.toggleSelect(id);
|
|
347
429
|
}
|
|
348
430
|
},
|
|
349
|
-
[drawing
|
|
431
|
+
[drawing]
|
|
350
432
|
);
|
|
351
433
|
const onArcClick = useCallback(
|
|
352
434
|
(groupIndex, e) => {
|
|
@@ -364,39 +446,55 @@ function NumberLineProvider({
|
|
|
364
446
|
);
|
|
365
447
|
const clearArcSelection = useCallback(() => setSelectedArcIndices(/* @__PURE__ */ new Set()), []);
|
|
366
448
|
const onDelete = useCallback(() => {
|
|
367
|
-
const strokesBefore = drawing.strokes;
|
|
368
449
|
const deletedStrokes = drawing.strokes.filter((s) => s.selected);
|
|
369
450
|
const deletedArcs = [...selectedArcIndices];
|
|
370
451
|
if (deletedStrokes.length === 0 && deletedArcs.length === 0) return;
|
|
452
|
+
history.push(createHistorySnapshot());
|
|
371
453
|
if (deletedStrokes.length > 0) drawing.deleteSelected();
|
|
372
454
|
if (deletedArcs.length > 0) {
|
|
373
455
|
setDeletedArcIndices((prev) => /* @__PURE__ */ new Set([...prev, ...deletedArcs]));
|
|
374
456
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
375
457
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const onReset = useCallback(() => {
|
|
379
|
-
drawing.resetStrokes();
|
|
380
|
-
history.clear();
|
|
458
|
+
}, [drawing, history, selectedArcIndices, createHistorySnapshot]);
|
|
459
|
+
const clearAll = useCallback(() => {
|
|
381
460
|
const arcCount = Math.max(0, Math.ceil(Number(groupCount) || 0));
|
|
461
|
+
const allArcsDeleted = deletedArcIndices.size >= arcCount && Array.from({ length: arcCount }, (_, index) => index).every(
|
|
462
|
+
(index) => deletedArcIndices.has(index)
|
|
463
|
+
);
|
|
464
|
+
const alreadyClear = drawing.strokes.length === 0 && selectedArcIndices.size === 0 && allArcsDeleted && groupTicksHidden;
|
|
465
|
+
if (alreadyClear) return;
|
|
466
|
+
history.push(createHistorySnapshot());
|
|
467
|
+
drawing.resetStrokes();
|
|
468
|
+
setLiveStroke(null);
|
|
469
|
+
eraseGesture.current = null;
|
|
470
|
+
setEraseHoverIds(/* @__PURE__ */ new Set());
|
|
382
471
|
setDeletedArcIndices(new Set(Array.from({ length: arcCount }, (_, i) => i)));
|
|
383
472
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
384
473
|
setGroupTicksHidden(true);
|
|
385
|
-
}, [
|
|
474
|
+
}, [
|
|
475
|
+
drawing,
|
|
476
|
+
history,
|
|
477
|
+
groupCount,
|
|
478
|
+
deletedArcIndices,
|
|
479
|
+
selectedArcIndices,
|
|
480
|
+
groupTicksHidden,
|
|
481
|
+
createHistorySnapshot
|
|
482
|
+
]);
|
|
483
|
+
const onReset = clearAll;
|
|
386
484
|
const showAllArcs = useCallback(() => {
|
|
387
485
|
setDeletedArcIndices((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
388
486
|
setGroupTicksHidden(false);
|
|
389
487
|
}, []);
|
|
390
488
|
const onUndo = useCallback(() => {
|
|
391
|
-
const
|
|
392
|
-
if (
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
489
|
+
const snapshot = history.undo();
|
|
490
|
+
if (!snapshot) return;
|
|
491
|
+
drawing.restoreStrokes(snapshot.strokes);
|
|
492
|
+
setDeletedArcIndices(new Set(snapshot.deletedArcIndices));
|
|
493
|
+
setSelectedArcIndices(new Set(snapshot.selectedArcIndices));
|
|
494
|
+
setGroupTicksHidden(snapshot.groupTicksHidden);
|
|
495
|
+
setLiveStroke(null);
|
|
496
|
+
eraseGesture.current = null;
|
|
497
|
+
setEraseHoverIds(/* @__PURE__ */ new Set());
|
|
400
498
|
}, [history, drawing]);
|
|
401
499
|
const onTogglePen = useCallback(
|
|
402
500
|
() => {
|
|
@@ -415,6 +513,7 @@ function NumberLineProvider({
|
|
|
415
513
|
const canDelete = drawing.strokes.some((s) => s.selected) || selectedArcIndices.size > 0;
|
|
416
514
|
useEffect(() => {
|
|
417
515
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
516
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
418
517
|
}, [drawing.tool]);
|
|
419
518
|
const keyHandlers = useRef({ toggleTool: drawing.toggleTool, clearArcSelection, canDelete, onDelete, onUndo });
|
|
420
519
|
keyHandlers.current = { toggleTool: drawing.toggleTool, clearArcSelection, canDelete, onDelete, onUndo };
|
|
@@ -466,11 +565,13 @@ function NumberLineProvider({
|
|
|
466
565
|
tool: drawing.tool,
|
|
467
566
|
strokes: drawing.strokes,
|
|
468
567
|
liveStroke,
|
|
568
|
+
eraseHoverIds,
|
|
469
569
|
selectedArcIndices,
|
|
470
570
|
deletedArcIndices,
|
|
471
571
|
onPointerDown,
|
|
472
572
|
onPointerMove,
|
|
473
573
|
onPointerUp,
|
|
574
|
+
onPointerLeave,
|
|
474
575
|
onStrokeClick,
|
|
475
576
|
onArcClick,
|
|
476
577
|
canDelete,
|
|
@@ -478,6 +579,7 @@ function NumberLineProvider({
|
|
|
478
579
|
onTogglePen,
|
|
479
580
|
onToggleEraser,
|
|
480
581
|
onDelete,
|
|
582
|
+
clearAll,
|
|
481
583
|
onReset,
|
|
482
584
|
onUndo,
|
|
483
585
|
showAllArcs
|
|
@@ -914,11 +1016,10 @@ const GroupArcsLayer = memo(function GroupArcsLayer2({
|
|
|
914
1016
|
}
|
|
915
1017
|
return /* @__PURE__ */ jsx("g", { className: "nl-arcs-layer", children: arcs });
|
|
916
1018
|
});
|
|
917
|
-
function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick }) {
|
|
918
|
-
const [hoveredId, setHoveredId] = useState(null);
|
|
1019
|
+
function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds, onStrokeClick }) {
|
|
919
1020
|
return /* @__PURE__ */ jsxs("g", { className: "nl-drawing-layer", transform: `scale(${width} ${height})`, children: [
|
|
920
1021
|
strokes.map((s) => /* @__PURE__ */ jsxs("g", { children: [
|
|
921
|
-
(s.selected || tool === "eraser" && s.id
|
|
1022
|
+
(s.selected || tool === "eraser" && eraseHoverIds.has(s.id)) && /* @__PURE__ */ jsx(
|
|
922
1023
|
"path",
|
|
923
1024
|
{
|
|
924
1025
|
d: s.d,
|
|
@@ -943,10 +1044,8 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
|
|
|
943
1044
|
vectorEffect: "non-scaling-stroke",
|
|
944
1045
|
pointerEvents: "stroke",
|
|
945
1046
|
style: { cursor: tool === "eraser" ? "cell" : tool === "none" ? "pointer" : "default" },
|
|
946
|
-
onPointerEnter: () => setHoveredId(s.id),
|
|
947
|
-
onPointerLeave: () => setHoveredId((prev) => prev === s.id ? null : prev),
|
|
948
1047
|
onClick: (e) => {
|
|
949
|
-
if (tool === "
|
|
1048
|
+
if (tool === "none") {
|
|
950
1049
|
e.stopPropagation();
|
|
951
1050
|
onStrokeClick(s.id, e);
|
|
952
1051
|
}
|
|
@@ -1013,11 +1112,13 @@ function NumberLine({ className }) {
|
|
|
1013
1112
|
tool,
|
|
1014
1113
|
strokes,
|
|
1015
1114
|
liveStroke,
|
|
1115
|
+
eraseHoverIds,
|
|
1016
1116
|
selectedArcIndices,
|
|
1017
1117
|
deletedArcIndices,
|
|
1018
1118
|
onPointerDown,
|
|
1019
1119
|
onPointerMove,
|
|
1020
1120
|
onPointerUp,
|
|
1121
|
+
onPointerLeave,
|
|
1021
1122
|
onStrokeClick,
|
|
1022
1123
|
onArcClick
|
|
1023
1124
|
} = useNumberLineContext();
|
|
@@ -1184,6 +1285,8 @@ function NumberLine({ className }) {
|
|
|
1184
1285
|
onPointerDown: handlePointerDown,
|
|
1185
1286
|
onPointerMove: handlePointerMove,
|
|
1186
1287
|
onPointerUp: handlePointerUp,
|
|
1288
|
+
onPointerCancel: handlePointerUp,
|
|
1289
|
+
onPointerLeave,
|
|
1187
1290
|
onWheel: handleWheel,
|
|
1188
1291
|
onDoubleClick: handleDoubleClick,
|
|
1189
1292
|
onMouseEnter: () => enableZoom && setIsZoomActive(true),
|
|
@@ -1242,6 +1345,7 @@ function NumberLine({ className }) {
|
|
|
1242
1345
|
tool,
|
|
1243
1346
|
width: w,
|
|
1244
1347
|
height: h,
|
|
1348
|
+
eraseHoverIds,
|
|
1245
1349
|
onStrokeClick
|
|
1246
1350
|
}
|
|
1247
1351
|
)
|
package/package.json
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@dcg-overseas/number-line",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Interactive number line component with group arcs and freehand drawing",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"main": "./dist/index.cjs",
|
|
8
|
-
"module": "./dist/index.js",
|
|
9
|
-
"types": "./dist/index.d.ts",
|
|
10
|
-
"exports": {
|
|
11
|
-
".": {
|
|
12
|
-
"import": {
|
|
13
|
-
"types": "./dist/index.d.ts",
|
|
14
|
-
"default": "./dist/index.js"
|
|
15
|
-
},
|
|
16
|
-
"require": {
|
|
17
|
-
"types": "./dist/index.d.cts",
|
|
18
|
-
"default": "./dist/index.cjs"
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"files": [
|
|
23
|
-
"dist"
|
|
24
|
-
],
|
|
25
|
-
"sideEffects": false,
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "vite build",
|
|
28
|
-
"build:watch": "vite build --watch",
|
|
29
|
-
"typecheck": "tsc --noEmit",
|
|
30
|
-
"clean": "rm -rf dist *.tsbuildinfo"
|
|
31
|
-
},
|
|
32
|
-
"peerDependencies": {
|
|
33
|
-
"react": "^18.0.0",
|
|
34
|
-
"react-dom": "^18.0.0"
|
|
35
|
-
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"@types/react": "^18.3.3",
|
|
38
|
-
"@types/react-dom": "^18.3.0",
|
|
39
|
-
"@vitejs/plugin-react": "^4.3.1",
|
|
40
|
-
"vite": "^5.3.1",
|
|
41
|
-
"vite-plugin-dts": "^3.9.1"
|
|
42
|
-
},
|
|
43
|
-
"publishConfig": {
|
|
44
|
-
"access": "public",
|
|
45
|
-
"registry": "https://registry.npmjs.org/"
|
|
46
|
-
}
|
|
47
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@dcg-overseas/number-line",
|
|
3
|
+
"version": "1.0.0-beta.0",
|
|
4
|
+
"description": "Interactive number line component with group arcs and freehand drawing",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "vite build",
|
|
28
|
+
"build:watch": "vite build --watch",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"react": "^18.0.0",
|
|
34
|
+
"react-dom": "^18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/react": "^18.3.3",
|
|
38
|
+
"@types/react-dom": "^18.3.0",
|
|
39
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
40
|
+
"vite": "^5.3.1",
|
|
41
|
+
"vite-plugin-dts": "^3.9.1"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public",
|
|
45
|
+
"registry": "https://registry.npmjs.org/"
|
|
46
|
+
}
|
|
47
|
+
}
|