@dcg-overseas/number-line 0.1.29 → 0.1.31
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 +94 -18
- package/dist/index.d.ts +3 -0
- package/dist/index.js +94 -18
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -247,6 +247,19 @@ function normalizeDomain(lo, hi) {
|
|
|
247
247
|
}
|
|
248
248
|
return { min: x, max: y };
|
|
249
249
|
}
|
|
250
|
+
function eraseRadius(pointerType) {
|
|
251
|
+
return pointerType === "touch" ? 28 : 16;
|
|
252
|
+
}
|
|
253
|
+
function strokeWithinRadius(d, px, py, radius, w, h) {
|
|
254
|
+
const nums = d.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi);
|
|
255
|
+
if (!nums) return false;
|
|
256
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
257
|
+
const x = parseFloat(nums[i]) * w;
|
|
258
|
+
const y = parseFloat(nums[i + 1]) * h;
|
|
259
|
+
if (Math.hypot(x - px, y - py) < radius) return true;
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
250
263
|
function NumberLineProvider({
|
|
251
264
|
min,
|
|
252
265
|
max,
|
|
@@ -285,6 +298,8 @@ function NumberLineProvider({
|
|
|
285
298
|
});
|
|
286
299
|
const [liveStroke, setLiveStroke] = React.useState(null);
|
|
287
300
|
const svgPoint = React.useRef(null);
|
|
301
|
+
const [eraseHoverIds, setEraseHoverIds] = React.useState(/* @__PURE__ */ new Set());
|
|
302
|
+
const eraseGesture = React.useRef(null);
|
|
288
303
|
React.useEffect(() => {
|
|
289
304
|
setDeletedArcIndices(/* @__PURE__ */ new Set());
|
|
290
305
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
@@ -304,28 +319,87 @@ function NumberLineProvider({
|
|
|
304
319
|
},
|
|
305
320
|
[svgRef]
|
|
306
321
|
);
|
|
322
|
+
const strokeIdsNear = React.useCallback(
|
|
323
|
+
(x, y, radius) => {
|
|
324
|
+
const ids = [];
|
|
325
|
+
for (const s of drawing.strokes) {
|
|
326
|
+
if (strokeWithinRadius(s.d, x, y, radius, containerWidth, containerHeight)) {
|
|
327
|
+
ids.push(s.id);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return ids;
|
|
331
|
+
},
|
|
332
|
+
[drawing.strokes, containerWidth, containerHeight]
|
|
333
|
+
);
|
|
334
|
+
const eraseAt = React.useCallback(
|
|
335
|
+
(x, y, radius) => {
|
|
336
|
+
const gesture = eraseGesture.current;
|
|
337
|
+
if (!gesture) return;
|
|
338
|
+
for (const id of strokeIdsNear(x, y, radius)) {
|
|
339
|
+
if (!gesture.removed.has(id)) {
|
|
340
|
+
gesture.removed.add(id);
|
|
341
|
+
drawing.eraseStroke(id);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
[strokeIdsNear, drawing]
|
|
346
|
+
);
|
|
307
347
|
const onPointerDown = React.useCallback(
|
|
308
348
|
(e) => {
|
|
309
349
|
e.currentTarget.focus();
|
|
350
|
+
if (drawing.tool === "eraser") {
|
|
351
|
+
if (eraseGesture.current) return;
|
|
352
|
+
const coords2 = getLocalCoords(e);
|
|
353
|
+
if (!coords2) return;
|
|
354
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
355
|
+
eraseGesture.current = { before: drawing.strokes, removed: /* @__PURE__ */ new Set() };
|
|
356
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
357
|
+
eraseAt(coords2[0], coords2[1], eraseRadius(e.pointerType));
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
310
360
|
if (drawing.tool !== "pen") return;
|
|
311
361
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
312
362
|
const coords = getLocalCoords(e);
|
|
313
363
|
if (!coords) return;
|
|
314
364
|
drawing.startStroke(coords[0], coords[1], containerWidth, containerHeight);
|
|
315
365
|
},
|
|
316
|
-
[drawing, getLocalCoords, containerWidth, containerHeight]
|
|
366
|
+
[drawing, getLocalCoords, containerWidth, containerHeight, eraseAt]
|
|
317
367
|
);
|
|
318
368
|
const onPointerMove = React.useCallback(
|
|
319
369
|
(e) => {
|
|
370
|
+
if (drawing.tool === "eraser") {
|
|
371
|
+
const coords2 = getLocalCoords(e);
|
|
372
|
+
if (!coords2) return;
|
|
373
|
+
const radius = eraseRadius(e.pointerType);
|
|
374
|
+
if (eraseGesture.current) {
|
|
375
|
+
eraseAt(coords2[0], coords2[1], radius);
|
|
376
|
+
} else {
|
|
377
|
+
const nextIds = strokeIdsNear(coords2[0], coords2[1], radius);
|
|
378
|
+
setEraseHoverIds((prev) => {
|
|
379
|
+
if (nextIds.length === prev.size && nextIds.every((id) => prev.has(id)))
|
|
380
|
+
return prev;
|
|
381
|
+
return new Set(nextIds);
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
320
386
|
if (drawing.tool !== "pen") return;
|
|
321
387
|
const coords = getLocalCoords(e);
|
|
322
388
|
if (!coords) return;
|
|
323
389
|
const live = drawing.continueStroke(coords[0], coords[1], containerWidth, containerHeight);
|
|
324
390
|
if (live) setLiveStroke(live);
|
|
325
391
|
},
|
|
326
|
-
[drawing, getLocalCoords, containerWidth, containerHeight]
|
|
392
|
+
[drawing, getLocalCoords, containerWidth, containerHeight, eraseAt, strokeIdsNear]
|
|
327
393
|
);
|
|
328
394
|
const onPointerUp = React.useCallback(() => {
|
|
395
|
+
const gesture = eraseGesture.current;
|
|
396
|
+
if (gesture) {
|
|
397
|
+
eraseGesture.current = null;
|
|
398
|
+
if (gesture.removed.size > 0) {
|
|
399
|
+
history.push({ type: "delete", strokesBefore: gesture.before, arcIndices: [] });
|
|
400
|
+
}
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
329
403
|
if (drawing.tool !== "pen") return;
|
|
330
404
|
const stroke = drawing.endStroke();
|
|
331
405
|
setLiveStroke(null);
|
|
@@ -334,21 +408,18 @@ function NumberLineProvider({
|
|
|
334
408
|
history.push({ type: "add", strokes: [stroke] });
|
|
335
409
|
}
|
|
336
410
|
}, [drawing, history]);
|
|
411
|
+
const onPointerLeave = React.useCallback(() => {
|
|
412
|
+
if (eraseGesture.current) return;
|
|
413
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
414
|
+
}, []);
|
|
337
415
|
const onStrokeClick = React.useCallback(
|
|
338
416
|
(id, e) => {
|
|
339
417
|
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") {
|
|
418
|
+
if (drawing.tool === "none") {
|
|
348
419
|
drawing.toggleSelect(id);
|
|
349
420
|
}
|
|
350
421
|
},
|
|
351
|
-
[drawing
|
|
422
|
+
[drawing]
|
|
352
423
|
);
|
|
353
424
|
const onArcClick = React.useCallback(
|
|
354
425
|
(groupIndex, e) => {
|
|
@@ -417,6 +488,7 @@ function NumberLineProvider({
|
|
|
417
488
|
const canDelete = drawing.strokes.some((s) => s.selected) || selectedArcIndices.size > 0;
|
|
418
489
|
React.useEffect(() => {
|
|
419
490
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
491
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
420
492
|
}, [drawing.tool]);
|
|
421
493
|
const keyHandlers = React.useRef({ toggleTool: drawing.toggleTool, clearArcSelection, canDelete, onDelete, onUndo });
|
|
422
494
|
keyHandlers.current = { toggleTool: drawing.toggleTool, clearArcSelection, canDelete, onDelete, onUndo };
|
|
@@ -468,11 +540,13 @@ function NumberLineProvider({
|
|
|
468
540
|
tool: drawing.tool,
|
|
469
541
|
strokes: drawing.strokes,
|
|
470
542
|
liveStroke,
|
|
543
|
+
eraseHoverIds,
|
|
471
544
|
selectedArcIndices,
|
|
472
545
|
deletedArcIndices,
|
|
473
546
|
onPointerDown,
|
|
474
547
|
onPointerMove,
|
|
475
548
|
onPointerUp,
|
|
549
|
+
onPointerLeave,
|
|
476
550
|
onStrokeClick,
|
|
477
551
|
onArcClick,
|
|
478
552
|
canDelete,
|
|
@@ -639,7 +713,7 @@ const AxisLayer = React.memo(function AxisLayer2({
|
|
|
639
713
|
});
|
|
640
714
|
const tickH = renderAsGroup ? 7 : renderAsMajor ? 10 : renderAsHalf ? 8 : 6;
|
|
641
715
|
const strokeW = renderAsGroup ? 4 : renderAsMajor ? 2 : renderAsHalf ? 2 : 1;
|
|
642
|
-
const arcIndex = renderAsGroup
|
|
716
|
+
const arcIndex = renderAsGroup ? offset > 0 ? offset / groupSize - 1 : offset === 0 ? 0 : -1 : -1;
|
|
643
717
|
const arcColored = arcIndex >= 0 && arcIndex < groupCount && !(deletedArcIndices == null ? void 0 : deletedArcIndices.has(arcIndex));
|
|
644
718
|
const color = arcColored ? arcColors[arcIndex % arcColors.length] : renderAsGroup || renderAsMajor || renderAsHalf ? "#374151" : "#4b5563";
|
|
645
719
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
@@ -916,11 +990,10 @@ const GroupArcsLayer = React.memo(function GroupArcsLayer2({
|
|
|
916
990
|
}
|
|
917
991
|
return /* @__PURE__ */ jsxRuntime.jsx("g", { className: "nl-arcs-layer", children: arcs });
|
|
918
992
|
});
|
|
919
|
-
function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick }) {
|
|
920
|
-
const [hoveredId, setHoveredId] = React.useState(null);
|
|
993
|
+
function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds, onStrokeClick }) {
|
|
921
994
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { className: "nl-drawing-layer", transform: `scale(${width} ${height})`, children: [
|
|
922
995
|
strokes.map((s) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
923
|
-
(s.selected || tool === "eraser" && s.id
|
|
996
|
+
(s.selected || tool === "eraser" && eraseHoverIds.has(s.id)) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
924
997
|
"path",
|
|
925
998
|
{
|
|
926
999
|
d: s.d,
|
|
@@ -945,10 +1018,8 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
|
|
|
945
1018
|
vectorEffect: "non-scaling-stroke",
|
|
946
1019
|
pointerEvents: "stroke",
|
|
947
1020
|
style: { cursor: tool === "eraser" ? "cell" : tool === "none" ? "pointer" : "default" },
|
|
948
|
-
onPointerEnter: () => setHoveredId(s.id),
|
|
949
|
-
onPointerLeave: () => setHoveredId((prev) => prev === s.id ? null : prev),
|
|
950
1021
|
onClick: (e) => {
|
|
951
|
-
if (tool === "
|
|
1022
|
+
if (tool === "none") {
|
|
952
1023
|
e.stopPropagation();
|
|
953
1024
|
onStrokeClick(s.id, e);
|
|
954
1025
|
}
|
|
@@ -1015,11 +1086,13 @@ function NumberLine({ className }) {
|
|
|
1015
1086
|
tool,
|
|
1016
1087
|
strokes,
|
|
1017
1088
|
liveStroke,
|
|
1089
|
+
eraseHoverIds,
|
|
1018
1090
|
selectedArcIndices,
|
|
1019
1091
|
deletedArcIndices,
|
|
1020
1092
|
onPointerDown,
|
|
1021
1093
|
onPointerMove,
|
|
1022
1094
|
onPointerUp,
|
|
1095
|
+
onPointerLeave,
|
|
1023
1096
|
onStrokeClick,
|
|
1024
1097
|
onArcClick
|
|
1025
1098
|
} = useNumberLineContext();
|
|
@@ -1186,6 +1259,8 @@ function NumberLine({ className }) {
|
|
|
1186
1259
|
onPointerDown: handlePointerDown,
|
|
1187
1260
|
onPointerMove: handlePointerMove,
|
|
1188
1261
|
onPointerUp: handlePointerUp,
|
|
1262
|
+
onPointerCancel: handlePointerUp,
|
|
1263
|
+
onPointerLeave,
|
|
1189
1264
|
onWheel: handleWheel,
|
|
1190
1265
|
onDoubleClick: handleDoubleClick,
|
|
1191
1266
|
onMouseEnter: () => enableZoom && setIsZoomActive(true),
|
|
@@ -1244,6 +1319,7 @@ function NumberLine({ className }) {
|
|
|
1244
1319
|
tool,
|
|
1245
1320
|
width: w,
|
|
1246
1321
|
height: h,
|
|
1322
|
+
eraseHoverIds,
|
|
1247
1323
|
onStrokeClick
|
|
1248
1324
|
}
|
|
1249
1325
|
)
|
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;
|
package/dist/index.js
CHANGED
|
@@ -245,6 +245,19 @@ function normalizeDomain(lo, hi) {
|
|
|
245
245
|
}
|
|
246
246
|
return { min: x, max: y };
|
|
247
247
|
}
|
|
248
|
+
function eraseRadius(pointerType) {
|
|
249
|
+
return pointerType === "touch" ? 28 : 16;
|
|
250
|
+
}
|
|
251
|
+
function strokeWithinRadius(d, px, py, radius, w, h) {
|
|
252
|
+
const nums = d.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi);
|
|
253
|
+
if (!nums) return false;
|
|
254
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
255
|
+
const x = parseFloat(nums[i]) * w;
|
|
256
|
+
const y = parseFloat(nums[i + 1]) * h;
|
|
257
|
+
if (Math.hypot(x - px, y - py) < radius) return true;
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
248
261
|
function NumberLineProvider({
|
|
249
262
|
min,
|
|
250
263
|
max,
|
|
@@ -283,6 +296,8 @@ function NumberLineProvider({
|
|
|
283
296
|
});
|
|
284
297
|
const [liveStroke, setLiveStroke] = useState(null);
|
|
285
298
|
const svgPoint = useRef(null);
|
|
299
|
+
const [eraseHoverIds, setEraseHoverIds] = useState(/* @__PURE__ */ new Set());
|
|
300
|
+
const eraseGesture = useRef(null);
|
|
286
301
|
useEffect(() => {
|
|
287
302
|
setDeletedArcIndices(/* @__PURE__ */ new Set());
|
|
288
303
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
@@ -302,28 +317,87 @@ function NumberLineProvider({
|
|
|
302
317
|
},
|
|
303
318
|
[svgRef]
|
|
304
319
|
);
|
|
320
|
+
const strokeIdsNear = useCallback(
|
|
321
|
+
(x, y, radius) => {
|
|
322
|
+
const ids = [];
|
|
323
|
+
for (const s of drawing.strokes) {
|
|
324
|
+
if (strokeWithinRadius(s.d, x, y, radius, containerWidth, containerHeight)) {
|
|
325
|
+
ids.push(s.id);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return ids;
|
|
329
|
+
},
|
|
330
|
+
[drawing.strokes, containerWidth, containerHeight]
|
|
331
|
+
);
|
|
332
|
+
const eraseAt = useCallback(
|
|
333
|
+
(x, y, radius) => {
|
|
334
|
+
const gesture = eraseGesture.current;
|
|
335
|
+
if (!gesture) return;
|
|
336
|
+
for (const id of strokeIdsNear(x, y, radius)) {
|
|
337
|
+
if (!gesture.removed.has(id)) {
|
|
338
|
+
gesture.removed.add(id);
|
|
339
|
+
drawing.eraseStroke(id);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
[strokeIdsNear, drawing]
|
|
344
|
+
);
|
|
305
345
|
const onPointerDown = useCallback(
|
|
306
346
|
(e) => {
|
|
307
347
|
e.currentTarget.focus();
|
|
348
|
+
if (drawing.tool === "eraser") {
|
|
349
|
+
if (eraseGesture.current) return;
|
|
350
|
+
const coords2 = getLocalCoords(e);
|
|
351
|
+
if (!coords2) return;
|
|
352
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
353
|
+
eraseGesture.current = { before: drawing.strokes, removed: /* @__PURE__ */ new Set() };
|
|
354
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
355
|
+
eraseAt(coords2[0], coords2[1], eraseRadius(e.pointerType));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
308
358
|
if (drawing.tool !== "pen") return;
|
|
309
359
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
310
360
|
const coords = getLocalCoords(e);
|
|
311
361
|
if (!coords) return;
|
|
312
362
|
drawing.startStroke(coords[0], coords[1], containerWidth, containerHeight);
|
|
313
363
|
},
|
|
314
|
-
[drawing, getLocalCoords, containerWidth, containerHeight]
|
|
364
|
+
[drawing, getLocalCoords, containerWidth, containerHeight, eraseAt]
|
|
315
365
|
);
|
|
316
366
|
const onPointerMove = useCallback(
|
|
317
367
|
(e) => {
|
|
368
|
+
if (drawing.tool === "eraser") {
|
|
369
|
+
const coords2 = getLocalCoords(e);
|
|
370
|
+
if (!coords2) return;
|
|
371
|
+
const radius = eraseRadius(e.pointerType);
|
|
372
|
+
if (eraseGesture.current) {
|
|
373
|
+
eraseAt(coords2[0], coords2[1], radius);
|
|
374
|
+
} else {
|
|
375
|
+
const nextIds = strokeIdsNear(coords2[0], coords2[1], radius);
|
|
376
|
+
setEraseHoverIds((prev) => {
|
|
377
|
+
if (nextIds.length === prev.size && nextIds.every((id) => prev.has(id)))
|
|
378
|
+
return prev;
|
|
379
|
+
return new Set(nextIds);
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
318
384
|
if (drawing.tool !== "pen") return;
|
|
319
385
|
const coords = getLocalCoords(e);
|
|
320
386
|
if (!coords) return;
|
|
321
387
|
const live = drawing.continueStroke(coords[0], coords[1], containerWidth, containerHeight);
|
|
322
388
|
if (live) setLiveStroke(live);
|
|
323
389
|
},
|
|
324
|
-
[drawing, getLocalCoords, containerWidth, containerHeight]
|
|
390
|
+
[drawing, getLocalCoords, containerWidth, containerHeight, eraseAt, strokeIdsNear]
|
|
325
391
|
);
|
|
326
392
|
const onPointerUp = useCallback(() => {
|
|
393
|
+
const gesture = eraseGesture.current;
|
|
394
|
+
if (gesture) {
|
|
395
|
+
eraseGesture.current = null;
|
|
396
|
+
if (gesture.removed.size > 0) {
|
|
397
|
+
history.push({ type: "delete", strokesBefore: gesture.before, arcIndices: [] });
|
|
398
|
+
}
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
327
401
|
if (drawing.tool !== "pen") return;
|
|
328
402
|
const stroke = drawing.endStroke();
|
|
329
403
|
setLiveStroke(null);
|
|
@@ -332,21 +406,18 @@ function NumberLineProvider({
|
|
|
332
406
|
history.push({ type: "add", strokes: [stroke] });
|
|
333
407
|
}
|
|
334
408
|
}, [drawing, history]);
|
|
409
|
+
const onPointerLeave = useCallback(() => {
|
|
410
|
+
if (eraseGesture.current) return;
|
|
411
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
412
|
+
}, []);
|
|
335
413
|
const onStrokeClick = useCallback(
|
|
336
414
|
(id, e) => {
|
|
337
415
|
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") {
|
|
416
|
+
if (drawing.tool === "none") {
|
|
346
417
|
drawing.toggleSelect(id);
|
|
347
418
|
}
|
|
348
419
|
},
|
|
349
|
-
[drawing
|
|
420
|
+
[drawing]
|
|
350
421
|
);
|
|
351
422
|
const onArcClick = useCallback(
|
|
352
423
|
(groupIndex, e) => {
|
|
@@ -415,6 +486,7 @@ function NumberLineProvider({
|
|
|
415
486
|
const canDelete = drawing.strokes.some((s) => s.selected) || selectedArcIndices.size > 0;
|
|
416
487
|
useEffect(() => {
|
|
417
488
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
489
|
+
setEraseHoverIds((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Set());
|
|
418
490
|
}, [drawing.tool]);
|
|
419
491
|
const keyHandlers = useRef({ toggleTool: drawing.toggleTool, clearArcSelection, canDelete, onDelete, onUndo });
|
|
420
492
|
keyHandlers.current = { toggleTool: drawing.toggleTool, clearArcSelection, canDelete, onDelete, onUndo };
|
|
@@ -466,11 +538,13 @@ function NumberLineProvider({
|
|
|
466
538
|
tool: drawing.tool,
|
|
467
539
|
strokes: drawing.strokes,
|
|
468
540
|
liveStroke,
|
|
541
|
+
eraseHoverIds,
|
|
469
542
|
selectedArcIndices,
|
|
470
543
|
deletedArcIndices,
|
|
471
544
|
onPointerDown,
|
|
472
545
|
onPointerMove,
|
|
473
546
|
onPointerUp,
|
|
547
|
+
onPointerLeave,
|
|
474
548
|
onStrokeClick,
|
|
475
549
|
onArcClick,
|
|
476
550
|
canDelete,
|
|
@@ -637,7 +711,7 @@ const AxisLayer = React.memo(function AxisLayer2({
|
|
|
637
711
|
});
|
|
638
712
|
const tickH = renderAsGroup ? 7 : renderAsMajor ? 10 : renderAsHalf ? 8 : 6;
|
|
639
713
|
const strokeW = renderAsGroup ? 4 : renderAsMajor ? 2 : renderAsHalf ? 2 : 1;
|
|
640
|
-
const arcIndex = renderAsGroup
|
|
714
|
+
const arcIndex = renderAsGroup ? offset > 0 ? offset / groupSize - 1 : offset === 0 ? 0 : -1 : -1;
|
|
641
715
|
const arcColored = arcIndex >= 0 && arcIndex < groupCount && !(deletedArcIndices == null ? void 0 : deletedArcIndices.has(arcIndex));
|
|
642
716
|
const color = arcColored ? arcColors[arcIndex % arcColors.length] : renderAsGroup || renderAsMajor || renderAsHalf ? "#374151" : "#4b5563";
|
|
643
717
|
return /* @__PURE__ */ jsxs("g", { children: [
|
|
@@ -914,11 +988,10 @@ const GroupArcsLayer = memo(function GroupArcsLayer2({
|
|
|
914
988
|
}
|
|
915
989
|
return /* @__PURE__ */ jsx("g", { className: "nl-arcs-layer", children: arcs });
|
|
916
990
|
});
|
|
917
|
-
function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick }) {
|
|
918
|
-
const [hoveredId, setHoveredId] = useState(null);
|
|
991
|
+
function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds, onStrokeClick }) {
|
|
919
992
|
return /* @__PURE__ */ jsxs("g", { className: "nl-drawing-layer", transform: `scale(${width} ${height})`, children: [
|
|
920
993
|
strokes.map((s) => /* @__PURE__ */ jsxs("g", { children: [
|
|
921
|
-
(s.selected || tool === "eraser" && s.id
|
|
994
|
+
(s.selected || tool === "eraser" && eraseHoverIds.has(s.id)) && /* @__PURE__ */ jsx(
|
|
922
995
|
"path",
|
|
923
996
|
{
|
|
924
997
|
d: s.d,
|
|
@@ -943,10 +1016,8 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
|
|
|
943
1016
|
vectorEffect: "non-scaling-stroke",
|
|
944
1017
|
pointerEvents: "stroke",
|
|
945
1018
|
style: { cursor: tool === "eraser" ? "cell" : tool === "none" ? "pointer" : "default" },
|
|
946
|
-
onPointerEnter: () => setHoveredId(s.id),
|
|
947
|
-
onPointerLeave: () => setHoveredId((prev) => prev === s.id ? null : prev),
|
|
948
1019
|
onClick: (e) => {
|
|
949
|
-
if (tool === "
|
|
1020
|
+
if (tool === "none") {
|
|
950
1021
|
e.stopPropagation();
|
|
951
1022
|
onStrokeClick(s.id, e);
|
|
952
1023
|
}
|
|
@@ -1013,11 +1084,13 @@ function NumberLine({ className }) {
|
|
|
1013
1084
|
tool,
|
|
1014
1085
|
strokes,
|
|
1015
1086
|
liveStroke,
|
|
1087
|
+
eraseHoverIds,
|
|
1016
1088
|
selectedArcIndices,
|
|
1017
1089
|
deletedArcIndices,
|
|
1018
1090
|
onPointerDown,
|
|
1019
1091
|
onPointerMove,
|
|
1020
1092
|
onPointerUp,
|
|
1093
|
+
onPointerLeave,
|
|
1021
1094
|
onStrokeClick,
|
|
1022
1095
|
onArcClick
|
|
1023
1096
|
} = useNumberLineContext();
|
|
@@ -1184,6 +1257,8 @@ function NumberLine({ className }) {
|
|
|
1184
1257
|
onPointerDown: handlePointerDown,
|
|
1185
1258
|
onPointerMove: handlePointerMove,
|
|
1186
1259
|
onPointerUp: handlePointerUp,
|
|
1260
|
+
onPointerCancel: handlePointerUp,
|
|
1261
|
+
onPointerLeave,
|
|
1187
1262
|
onWheel: handleWheel,
|
|
1188
1263
|
onDoubleClick: handleDoubleClick,
|
|
1189
1264
|
onMouseEnter: () => enableZoom && setIsZoomActive(true),
|
|
@@ -1242,6 +1317,7 @@ function NumberLine({ className }) {
|
|
|
1242
1317
|
tool,
|
|
1243
1318
|
width: w,
|
|
1244
1319
|
height: h,
|
|
1320
|
+
eraseHoverIds,
|
|
1245
1321
|
onStrokeClick
|
|
1246
1322
|
}
|
|
1247
1323
|
)
|