@dcg-overseas/number-line 0.1.26 → 0.1.28

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 CHANGED
@@ -353,11 +353,7 @@ function NumberLineProvider({
353
353
  const onArcClick = React.useCallback(
354
354
  (groupIndex, e) => {
355
355
  e.stopPropagation();
356
- if (drawing.tool === "eraser") {
357
- const strokesBefore = drawing.strokes;
358
- setDeletedArcIndices((prev) => /* @__PURE__ */ new Set([...prev, groupIndex]));
359
- history.push({ type: "delete", strokesBefore, arcIndices: [groupIndex] });
360
- } else if (drawing.tool === "none") {
356
+ if (drawing.tool === "none") {
361
357
  setSelectedArcIndices((prev) => {
362
358
  const next = new Set(prev);
363
359
  if (next.has(groupIndex)) next.delete(groupIndex);
@@ -366,7 +362,7 @@ function NumberLineProvider({
366
362
  });
367
363
  }
368
364
  },
369
- [drawing.tool, drawing.strokes, history]
365
+ [drawing.tool]
370
366
  );
371
367
  const clearArcSelection = React.useCallback(() => setSelectedArcIndices(/* @__PURE__ */ new Set()), []);
372
368
  const onDelete = React.useCallback(() => {
@@ -554,6 +550,9 @@ const ARC_COLORS = [
554
550
  "#16a34a"
555
551
  ];
556
552
  const ARC_RY_RATIO = 0.9;
553
+ const ARC_LABEL_GAP = 6;
554
+ const ARC_LABEL_FONT_SIZE = 16;
555
+ const ARC_LABEL_SPACE = ARC_LABEL_GAP + ARC_LABEL_FONT_SIZE;
557
556
  const ARC_ANIMATION_DRAW_MS = 1e3;
558
557
  const ARC_ANIMATION_PAUSE_MS = 400;
559
558
  function gcd(a, b) {
@@ -812,7 +811,7 @@ function AnimatedArc({
812
811
  x: x2,
813
812
  y: textY,
814
813
  textAnchor: edgeTextAnchor(),
815
- fontSize: 16,
814
+ fontSize: ARC_LABEL_FONT_SIZE,
816
815
  fill: strokeColor,
817
816
  fontWeight: "600",
818
817
  pointerEvents: "none",
@@ -882,12 +881,12 @@ const GroupArcsLayer = React.memo(function GroupArcsLayer2({
882
881
  const color = arcColors[g % arcColors.length];
883
882
  const MIN_ARROW_SPAN = 24;
884
883
  const textY = Math.min(
885
- axisY - ry - ARC_BASELINE_LIFT - 6,
884
+ axisY - ry - ARC_BASELINE_LIFT - ARC_LABEL_GAP,
886
885
  axisY - MIN_ARROW_SPAN
887
886
  );
888
887
  const arcPath = buildArcPath({ x1, x2, y: axisY, rx, ry });
889
888
  const isSelected = selectedArcIndices.has(g);
890
- const hitCursor = tool === "eraser" ? "cell" : tool === "none" ? "pointer" : "default";
889
+ const hitCursor = tool === "none" ? "pointer" : "default";
891
890
  arcs.push(
892
891
  /* @__PURE__ */ jsxRuntime.jsx(
893
892
  AnimatedArc,
@@ -905,7 +904,7 @@ const GroupArcsLayer = React.memo(function GroupArcsLayer2({
905
904
  hitCursor,
906
905
  shouldAnimate,
907
906
  onArcClick: (index, e) => {
908
- if (tool === "eraser" || tool === "none") {
907
+ if (tool === "none") {
909
908
  e.stopPropagation();
910
909
  onArcClick(index, e);
911
910
  }
@@ -918,9 +917,10 @@ const GroupArcsLayer = React.memo(function GroupArcsLayer2({
918
917
  return /* @__PURE__ */ jsxRuntime.jsx("g", { className: "nl-arcs-layer", children: arcs });
919
918
  });
920
919
  function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick }) {
920
+ const [hoveredId, setHoveredId] = React.useState(null);
921
921
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { className: "nl-drawing-layer", transform: `scale(${width} ${height})`, children: [
922
922
  strokes.map((s) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
923
- s.selected && /* @__PURE__ */ jsxRuntime.jsx(
923
+ (s.selected || tool === "eraser" && s.id === hoveredId) && /* @__PURE__ */ jsxRuntime.jsx(
924
924
  "path",
925
925
  {
926
926
  d: s.d,
@@ -945,6 +945,8 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
945
945
  vectorEffect: "non-scaling-stroke",
946
946
  pointerEvents: "stroke",
947
947
  style: { cursor: tool === "eraser" ? "cell" : tool === "none" ? "pointer" : "default" },
948
+ onPointerEnter: () => setHoveredId(s.id),
949
+ onPointerLeave: () => setHoveredId((prev) => prev === s.id ? null : prev),
948
950
  onClick: (e) => {
949
951
  if (tool === "eraser" || tool === "none") {
950
952
  e.stopPropagation();
@@ -984,7 +986,7 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
984
986
  ] });
985
987
  }
986
988
  const LABEL_SPACE_BELOW_AXIS = 42;
987
- const LABEL_ABOVE_AXIS_SLOP = 30;
989
+ const LABEL_ABOVE_AXIS_SLOP = ARC_BASELINE_LIFT + ARC_LABEL_SPACE;
988
990
  const ARC_TOP_PADDING = 16;
989
991
  const MIN_ARC_HEIGHT = 24;
990
992
  function NumberLine({ className }) {
@@ -1034,11 +1036,14 @@ function NumberLine({ className }) {
1034
1036
  const rx = usable * groupSize / range / 2;
1035
1037
  ryTypical = Math.min(rx * ARC_RY_RATIO, h * 0.48);
1036
1038
  }
1037
- const minAxisY = MIN_ARC_HEIGHT + ARC_TOP_PADDING;
1039
+ const minAxisY = MIN_ARC_HEIGHT + ARC_BASELINE_LIFT + ARC_LABEL_SPACE + ARC_TOP_PADDING;
1038
1040
  const maxAxisY = h - LABEL_SPACE_BELOW_AXIS;
1039
1041
  const axisYCentered = (h + ryTypical + LABEL_ABOVE_AXIS_SLOP - LABEL_SPACE_BELOW_AXIS) / 2;
1040
1042
  const axisY = Math.min(maxAxisY, Math.max(minAxisY, axisYCentered));
1041
- const arcMaxHeight = Math.max(MIN_ARC_HEIGHT, axisY - ARC_TOP_PADDING);
1043
+ const arcMaxHeight = Math.max(
1044
+ MIN_ARC_HEIGHT,
1045
+ axisY - ARC_TOP_PADDING - ARC_BASELINE_LIFT - ARC_LABEL_SPACE
1046
+ );
1042
1047
  const [isZoomActive, setIsZoomActive] = React.useState(false);
1043
1048
  const [isPanning, setIsPanning] = React.useState(false);
1044
1049
  const panStart = React.useRef(null);
package/dist/index.js CHANGED
@@ -351,11 +351,7 @@ function NumberLineProvider({
351
351
  const onArcClick = useCallback(
352
352
  (groupIndex, e) => {
353
353
  e.stopPropagation();
354
- if (drawing.tool === "eraser") {
355
- const strokesBefore = drawing.strokes;
356
- setDeletedArcIndices((prev) => /* @__PURE__ */ new Set([...prev, groupIndex]));
357
- history.push({ type: "delete", strokesBefore, arcIndices: [groupIndex] });
358
- } else if (drawing.tool === "none") {
354
+ if (drawing.tool === "none") {
359
355
  setSelectedArcIndices((prev) => {
360
356
  const next = new Set(prev);
361
357
  if (next.has(groupIndex)) next.delete(groupIndex);
@@ -364,7 +360,7 @@ function NumberLineProvider({
364
360
  });
365
361
  }
366
362
  },
367
- [drawing.tool, drawing.strokes, history]
363
+ [drawing.tool]
368
364
  );
369
365
  const clearArcSelection = useCallback(() => setSelectedArcIndices(/* @__PURE__ */ new Set()), []);
370
366
  const onDelete = useCallback(() => {
@@ -552,6 +548,9 @@ const ARC_COLORS = [
552
548
  "#16a34a"
553
549
  ];
554
550
  const ARC_RY_RATIO = 0.9;
551
+ const ARC_LABEL_GAP = 6;
552
+ const ARC_LABEL_FONT_SIZE = 16;
553
+ const ARC_LABEL_SPACE = ARC_LABEL_GAP + ARC_LABEL_FONT_SIZE;
555
554
  const ARC_ANIMATION_DRAW_MS = 1e3;
556
555
  const ARC_ANIMATION_PAUSE_MS = 400;
557
556
  function gcd(a, b) {
@@ -810,7 +809,7 @@ function AnimatedArc({
810
809
  x: x2,
811
810
  y: textY,
812
811
  textAnchor: edgeTextAnchor(),
813
- fontSize: 16,
812
+ fontSize: ARC_LABEL_FONT_SIZE,
814
813
  fill: strokeColor,
815
814
  fontWeight: "600",
816
815
  pointerEvents: "none",
@@ -880,12 +879,12 @@ const GroupArcsLayer = memo(function GroupArcsLayer2({
880
879
  const color = arcColors[g % arcColors.length];
881
880
  const MIN_ARROW_SPAN = 24;
882
881
  const textY = Math.min(
883
- axisY - ry - ARC_BASELINE_LIFT - 6,
882
+ axisY - ry - ARC_BASELINE_LIFT - ARC_LABEL_GAP,
884
883
  axisY - MIN_ARROW_SPAN
885
884
  );
886
885
  const arcPath = buildArcPath({ x1, x2, y: axisY, rx, ry });
887
886
  const isSelected = selectedArcIndices.has(g);
888
- const hitCursor = tool === "eraser" ? "cell" : tool === "none" ? "pointer" : "default";
887
+ const hitCursor = tool === "none" ? "pointer" : "default";
889
888
  arcs.push(
890
889
  /* @__PURE__ */ jsx(
891
890
  AnimatedArc,
@@ -903,7 +902,7 @@ const GroupArcsLayer = memo(function GroupArcsLayer2({
903
902
  hitCursor,
904
903
  shouldAnimate,
905
904
  onArcClick: (index, e) => {
906
- if (tool === "eraser" || tool === "none") {
905
+ if (tool === "none") {
907
906
  e.stopPropagation();
908
907
  onArcClick(index, e);
909
908
  }
@@ -916,9 +915,10 @@ const GroupArcsLayer = memo(function GroupArcsLayer2({
916
915
  return /* @__PURE__ */ jsx("g", { className: "nl-arcs-layer", children: arcs });
917
916
  });
918
917
  function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick }) {
918
+ const [hoveredId, setHoveredId] = useState(null);
919
919
  return /* @__PURE__ */ jsxs("g", { className: "nl-drawing-layer", transform: `scale(${width} ${height})`, children: [
920
920
  strokes.map((s) => /* @__PURE__ */ jsxs("g", { children: [
921
- s.selected && /* @__PURE__ */ jsx(
921
+ (s.selected || tool === "eraser" && s.id === hoveredId) && /* @__PURE__ */ jsx(
922
922
  "path",
923
923
  {
924
924
  d: s.d,
@@ -943,6 +943,8 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
943
943
  vectorEffect: "non-scaling-stroke",
944
944
  pointerEvents: "stroke",
945
945
  style: { cursor: tool === "eraser" ? "cell" : tool === "none" ? "pointer" : "default" },
946
+ onPointerEnter: () => setHoveredId(s.id),
947
+ onPointerLeave: () => setHoveredId((prev) => prev === s.id ? null : prev),
946
948
  onClick: (e) => {
947
949
  if (tool === "eraser" || tool === "none") {
948
950
  e.stopPropagation();
@@ -982,7 +984,7 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
982
984
  ] });
983
985
  }
984
986
  const LABEL_SPACE_BELOW_AXIS = 42;
985
- const LABEL_ABOVE_AXIS_SLOP = 30;
987
+ const LABEL_ABOVE_AXIS_SLOP = ARC_BASELINE_LIFT + ARC_LABEL_SPACE;
986
988
  const ARC_TOP_PADDING = 16;
987
989
  const MIN_ARC_HEIGHT = 24;
988
990
  function NumberLine({ className }) {
@@ -1032,11 +1034,14 @@ function NumberLine({ className }) {
1032
1034
  const rx = usable * groupSize / range / 2;
1033
1035
  ryTypical = Math.min(rx * ARC_RY_RATIO, h * 0.48);
1034
1036
  }
1035
- const minAxisY = MIN_ARC_HEIGHT + ARC_TOP_PADDING;
1037
+ const minAxisY = MIN_ARC_HEIGHT + ARC_BASELINE_LIFT + ARC_LABEL_SPACE + ARC_TOP_PADDING;
1036
1038
  const maxAxisY = h - LABEL_SPACE_BELOW_AXIS;
1037
1039
  const axisYCentered = (h + ryTypical + LABEL_ABOVE_AXIS_SLOP - LABEL_SPACE_BELOW_AXIS) / 2;
1038
1040
  const axisY = Math.min(maxAxisY, Math.max(minAxisY, axisYCentered));
1039
- const arcMaxHeight = Math.max(MIN_ARC_HEIGHT, axisY - ARC_TOP_PADDING);
1041
+ const arcMaxHeight = Math.max(
1042
+ MIN_ARC_HEIGHT,
1043
+ axisY - ARC_TOP_PADDING - ARC_BASELINE_LIFT - ARC_LABEL_SPACE
1044
+ );
1040
1045
  const [isZoomActive, setIsZoomActive] = useState(false);
1041
1046
  const [isPanning, setIsPanning] = useState(false);
1042
1047
  const panStart = useRef(null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcg-overseas/number-line",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "Interactive number line component with group arcs and freehand drawing",
5
5
  "type": "module",
6
6
  "license": "MIT",