@dcg-overseas/number-line 1.0.0-beta.1 → 1.0.0-beta.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/README.md +10 -10
- package/dist/index.cjs +24 -9
- package/dist/index.d.ts +9 -1
- package/dist/index.js +24 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -210,12 +210,12 @@ function App() {
|
|
|
210
210
|
| `canDelete` | `boolean` | 是否有可删除的选中项(笔迹或弧线) |
|
|
211
211
|
| `canUndo` | `boolean` | 是否有可撤销的操作 |
|
|
212
212
|
| `onTogglePen` | `() => void` | 切换画笔工具 |
|
|
213
|
-
| `onToggleEraser` | `() => void` | 切换橡皮擦 |
|
|
214
|
-
| `onDelete` | `() => void` | 删除当前选中 |
|
|
215
|
-
| `clearAll` | `() => void` | 可撤销地清空全部笔迹、弧线、选择和分组边界粗刻度;连续清空不会增加空历史项 |
|
|
216
|
-
| `onReset` | `() => void` | `clearAll` 的兼容别名,已弃用;新代码请使用 `clearAll` |
|
|
213
|
+
| `onToggleEraser` | `() => void` | 切换橡皮擦 |
|
|
214
|
+
| `onDelete` | `() => void` | 删除当前选中 |
|
|
215
|
+
| `clearAll` | `() => void` | 可撤销地清空全部笔迹、弧线、选择和分组边界粗刻度;连续清空不会增加空历史项 |
|
|
216
|
+
| `onReset` | `() => void` | `clearAll` 的兼容别名,已弃用;新代码请使用 `clearAll` |
|
|
217
217
|
| `onUndo` | `() => void` | 撤销上一步操作 |
|
|
218
|
-
| `showAllArcs` | `() => void` | 重新显示全部跳数弧线**及分组边界粗刻度**(清空删除记录)。即使参数未变、或刚 `clearAll` 隐藏后,也能强制把跳数显示出来,且不影响笔迹 |
|
|
218
|
+
| `showAllArcs` | `() => void` | 重新显示全部跳数弧线**及分组边界粗刻度**(清空删除记录)。即使参数未变、或刚 `clearAll` 隐藏后,也能强制把跳数显示出来,且不影响笔迹 |
|
|
219
219
|
|
|
220
220
|
#### 缩放 API(仅 `enableZoom=true` 时有效)
|
|
221
221
|
| 字段 | 类型 | 说明 |
|
|
@@ -235,7 +235,7 @@ function App() {
|
|
|
235
235
|
import { NumberLineProvider, NumberLine, useNumberLineContext } from '@dcg-overseas/number-line'
|
|
236
236
|
|
|
237
237
|
function Toolbar() {
|
|
238
|
-
const { tool, canDelete, canUndo, onTogglePen, onToggleEraser, onDelete, clearAll, onUndo } =
|
|
238
|
+
const { tool, canDelete, canUndo, onTogglePen, onToggleEraser, onDelete, clearAll, onUndo } =
|
|
239
239
|
useNumberLineContext()
|
|
240
240
|
|
|
241
241
|
return (
|
|
@@ -243,7 +243,7 @@ function Toolbar() {
|
|
|
243
243
|
<button data-active={tool === 'pen'} onClick={onTogglePen}>✏️ 画笔</button>
|
|
244
244
|
<button data-active={tool === 'eraser'} onClick={onToggleEraser}>🩹 擦除</button>
|
|
245
245
|
<button disabled={!canDelete} onClick={onDelete}>🗑 删除</button>
|
|
246
|
-
<button onClick={clearAll}>♻️ 全部清除</button>
|
|
246
|
+
<button onClick={clearAll}>♻️ 全部清除</button>
|
|
247
247
|
<button disabled={!canUndo} onClick={onUndo}>↶ 撤销</button>
|
|
248
248
|
</div>
|
|
249
249
|
)
|
|
@@ -305,9 +305,9 @@ import type {
|
|
|
305
305
|
|
|
306
306
|
### 操作级撤销
|
|
307
307
|
|
|
308
|
-
撤销栈记录每次操作发生前的完整状态快照,包括笔迹、已删除弧线、已选弧线和粗分组刻度状态。
|
|
309
|
-
画笔、橡皮擦、删除选中及 `clearAll` 都复用同一恢复流程,因此一次清空或批量删除可以完整还原。
|
|
310
|
-
`min`、`max`、`groupSize`、`groupCount` 或 `tickStep` 改变时会执行不可撤销初始化并清空历史,防止把上一道题的数据恢复到新坐标范围。
|
|
308
|
+
撤销栈记录每次操作发生前的完整状态快照,包括笔迹、已删除弧线、已选弧线和粗分组刻度状态。
|
|
309
|
+
画笔、橡皮擦、删除选中及 `clearAll` 都复用同一恢复流程,因此一次清空或批量删除可以完整还原。
|
|
310
|
+
`min`、`max`、`groupSize`、`groupCount` 或 `tickStep` 改变时会执行不可撤销初始化并清空历史,防止把上一道题的数据恢复到新坐标范围。
|
|
311
311
|
|
|
312
312
|
---
|
|
313
313
|
|
package/dist/index.cjs
CHANGED
|
@@ -223,6 +223,7 @@ function useNumberLineContext() {
|
|
|
223
223
|
if (!ctx) throw new Error("Must be used inside <NumberLineProvider>");
|
|
224
224
|
return ctx;
|
|
225
225
|
}
|
|
226
|
+
const DEFAULT_DRAWING_STROKE_WIDTH = 2.5;
|
|
226
227
|
function normalizeDomain(lo, hi) {
|
|
227
228
|
const a = Number(lo);
|
|
228
229
|
const b = Number(hi);
|
|
@@ -255,6 +256,7 @@ function NumberLineProvider({
|
|
|
255
256
|
groupSize,
|
|
256
257
|
groupCount,
|
|
257
258
|
tickStep,
|
|
259
|
+
strokeWidth: configuredStrokeWidth,
|
|
258
260
|
arcColors,
|
|
259
261
|
showGroupTicks = true,
|
|
260
262
|
showMajorTicks = true,
|
|
@@ -270,6 +272,7 @@ function NumberLineProvider({
|
|
|
270
272
|
children
|
|
271
273
|
}) {
|
|
272
274
|
const { min: domainMin, max: domainMax } = normalizeDomain(min, max);
|
|
275
|
+
const strokeWidth = typeof configuredStrokeWidth === "number" && Number.isFinite(configuredStrokeWidth) && configuredStrokeWidth > 0 ? configuredStrokeWidth : DEFAULT_DRAWING_STROKE_WIDTH;
|
|
273
276
|
const [selectedArcIndices, setSelectedArcIndices] = React.useState(/* @__PURE__ */ new Set());
|
|
274
277
|
const [deletedArcIndices, setDeletedArcIndices] = React.useState(/* @__PURE__ */ new Set());
|
|
275
278
|
const [groupTicksHidden, setGroupTicksHidden] = React.useState(false);
|
|
@@ -357,7 +360,6 @@ function NumberLineProvider({
|
|
|
357
360
|
);
|
|
358
361
|
const onPointerDown = React.useCallback(
|
|
359
362
|
(e) => {
|
|
360
|
-
e.currentTarget.focus();
|
|
361
363
|
if (drawing.tool === "eraser") {
|
|
362
364
|
if (eraseGesture.current) return;
|
|
363
365
|
const coords2 = getLocalCoords(e);
|
|
@@ -552,6 +554,7 @@ function NumberLineProvider({
|
|
|
552
554
|
groupSize,
|
|
553
555
|
groupCount,
|
|
554
556
|
tickStep,
|
|
557
|
+
strokeWidth,
|
|
555
558
|
arcColors,
|
|
556
559
|
showGroupTicks: showGroupTicks && !groupTicksHidden,
|
|
557
560
|
showMajorTicks,
|
|
@@ -731,14 +734,15 @@ const AxisLayer = React.memo(function AxisLayer2({
|
|
|
731
734
|
const renderAsMajor = !renderAsGroup && majorActive;
|
|
732
735
|
const renderAsHalf = !renderAsGroup && !renderAsMajor && halfTickOffset > 0 && Math.abs(offset % step - halfTickOffset) < 1e-9;
|
|
733
736
|
const showLabel = (renderAsGroup || renderAsMajor) && offset % labelStep === 0;
|
|
734
|
-
const currentShowLabelStep = typeof getShowLabelStep
|
|
737
|
+
const currentShowLabelStep = typeof getShowLabelStep === "function" ? getShowLabelStep({
|
|
735
738
|
viewMin,
|
|
736
739
|
viewMax,
|
|
737
740
|
offset,
|
|
738
741
|
labelStep,
|
|
739
742
|
length,
|
|
740
743
|
v
|
|
741
|
-
});
|
|
744
|
+
}) : void 0;
|
|
745
|
+
const shouldShowLabel = currentShowLabelStep ?? (showLabel || showLabelStep);
|
|
742
746
|
const tickH = renderAsGroup ? 7 : renderAsMajor ? 10 : renderAsHalf ? 8 : 6;
|
|
743
747
|
const strokeW = renderAsGroup ? 4 : renderAsMajor ? 2 : renderAsHalf ? 2 : 1;
|
|
744
748
|
const arcIndex = renderAsGroup ? offset > 0 ? offset / groupSize - 1 : offset === 0 ? 0 : -1 : -1;
|
|
@@ -756,7 +760,7 @@ const AxisLayer = React.memo(function AxisLayer2({
|
|
|
756
760
|
strokeWidth: strokeW
|
|
757
761
|
}
|
|
758
762
|
),
|
|
759
|
-
|
|
763
|
+
shouldShowLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
760
764
|
"text",
|
|
761
765
|
{
|
|
762
766
|
x,
|
|
@@ -790,6 +794,7 @@ function AnimatedArc({
|
|
|
790
794
|
max,
|
|
791
795
|
hitCursor,
|
|
792
796
|
shouldAnimate,
|
|
797
|
+
showLabel,
|
|
793
798
|
onArcClick
|
|
794
799
|
}) {
|
|
795
800
|
const measureRef = React.useRef(null);
|
|
@@ -872,7 +877,7 @@ function AnimatedArc({
|
|
|
872
877
|
pointerEvents: "none"
|
|
873
878
|
}
|
|
874
879
|
),
|
|
875
|
-
arcVisible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
880
|
+
arcVisible && showLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
876
881
|
"path",
|
|
877
882
|
{
|
|
878
883
|
d: arcPath,
|
|
@@ -969,6 +974,8 @@ const GroupArcsLayer = React.memo(function GroupArcsLayer2({
|
|
|
969
974
|
if (groupSize <= 0 || groupCount <= 0) return null;
|
|
970
975
|
const shouldAnimate = groupSize > 0 && groupCount > 0;
|
|
971
976
|
const toX = createValueToX(viewMin, viewMax, viewWidth, horizontalLayout);
|
|
977
|
+
const pixelsPerGroup = Math.abs(toX(min + groupSize) - toX(min));
|
|
978
|
+
const arcLabelInterval = Math.max(1, Math.ceil(36 / Math.max(1, pixelsPerGroup)));
|
|
972
979
|
const arcs = [];
|
|
973
980
|
for (let g = 0; g < groupCount; g++) {
|
|
974
981
|
if (deletedArcIndices.has(g)) continue;
|
|
@@ -988,6 +995,7 @@ const GroupArcsLayer = React.memo(function GroupArcsLayer2({
|
|
|
988
995
|
);
|
|
989
996
|
const arcPath = buildArcPath({ x1, x2, y: axisY, rx, ry });
|
|
990
997
|
const isSelected = selectedArcIndices.has(g);
|
|
998
|
+
const showLabel = arcLabelInterval === 1 || (g + 1) % arcLabelInterval === 0 || g === groupCount - 1;
|
|
991
999
|
const hitCursor = tool === "none" ? "pointer" : "default";
|
|
992
1000
|
arcs.push(
|
|
993
1001
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1005,6 +1013,7 @@ const GroupArcsLayer = React.memo(function GroupArcsLayer2({
|
|
|
1005
1013
|
max,
|
|
1006
1014
|
hitCursor,
|
|
1007
1015
|
shouldAnimate,
|
|
1016
|
+
showLabel,
|
|
1008
1017
|
onArcClick: (index, e) => {
|
|
1009
1018
|
if (tool === "none") {
|
|
1010
1019
|
e.stopPropagation();
|
|
@@ -1018,7 +1027,7 @@ const GroupArcsLayer = React.memo(function GroupArcsLayer2({
|
|
|
1018
1027
|
}
|
|
1019
1028
|
return /* @__PURE__ */ jsxRuntime.jsx("g", { className: "nl-arcs-layer", children: arcs });
|
|
1020
1029
|
});
|
|
1021
|
-
function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds, onStrokeClick }) {
|
|
1030
|
+
function DrawingLayer({ strokes, liveStroke, tool, width, height, strokeWidth, eraseHoverIds, onStrokeClick }) {
|
|
1022
1031
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { className: "nl-drawing-layer", transform: `scale(${width} ${height})`, children: [
|
|
1023
1032
|
strokes.map((s) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
1024
1033
|
(s.selected || tool === "eraser" && eraseHoverIds.has(s.id)) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1060,7 +1069,7 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds,
|
|
|
1060
1069
|
d: s.d,
|
|
1061
1070
|
fill: "none",
|
|
1062
1071
|
stroke: "#7c3aed",
|
|
1063
|
-
strokeWidth
|
|
1072
|
+
strokeWidth,
|
|
1064
1073
|
strokeLinecap: "round",
|
|
1065
1074
|
strokeLinejoin: "round",
|
|
1066
1075
|
vectorEffect: "non-scaling-stroke",
|
|
@@ -1074,7 +1083,7 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds,
|
|
|
1074
1083
|
d: liveStroke.d,
|
|
1075
1084
|
fill: "none",
|
|
1076
1085
|
stroke: "#7c3aed",
|
|
1077
|
-
strokeWidth
|
|
1086
|
+
strokeWidth,
|
|
1078
1087
|
strokeLinecap: "round",
|
|
1079
1088
|
strokeLinejoin: "round",
|
|
1080
1089
|
vectorEffect: "non-scaling-stroke",
|
|
@@ -1099,6 +1108,7 @@ function NumberLine({ className }) {
|
|
|
1099
1108
|
groupSize,
|
|
1100
1109
|
groupCount,
|
|
1101
1110
|
tickStep,
|
|
1111
|
+
strokeWidth,
|
|
1102
1112
|
arcColors,
|
|
1103
1113
|
showGroupTicks,
|
|
1104
1114
|
showMajorTicks,
|
|
@@ -1281,7 +1291,11 @@ function NumberLine({ className }) {
|
|
|
1281
1291
|
display: "block",
|
|
1282
1292
|
cursor: svgCursor,
|
|
1283
1293
|
touchAction: "none",
|
|
1284
|
-
overflow: "visible"
|
|
1294
|
+
overflow: "visible",
|
|
1295
|
+
// 数轴自身带有键盘快捷键,所以 SVG 需要保留 tabIndex;但各浏览器会在鼠标、
|
|
1296
|
+
// 触摸和键盘聚焦时都绘制一圈与教学内容无关的边框。该工具使用按钮状态表达
|
|
1297
|
+
// 可操作性,因此统一移除原生 outline,避免它被误认为数轴的选中标记。
|
|
1298
|
+
outline: "none"
|
|
1285
1299
|
},
|
|
1286
1300
|
onPointerDown: handlePointerDown,
|
|
1287
1301
|
onPointerMove: handlePointerMove,
|
|
@@ -1346,6 +1360,7 @@ function NumberLine({ className }) {
|
|
|
1346
1360
|
tool,
|
|
1347
1361
|
width: w,
|
|
1348
1362
|
height: h,
|
|
1363
|
+
strokeWidth,
|
|
1349
1364
|
eraseHoverIds,
|
|
1350
1365
|
onStrokeClick
|
|
1351
1366
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export declare interface NumberLineContextValue {
|
|
|
15
15
|
groupSize: number;
|
|
16
16
|
groupCount: number;
|
|
17
17
|
tickStep: number;
|
|
18
|
+
/** 用户自由绘制笔迹的固定视觉线宽,单位为 CSS 像素。 */
|
|
19
|
+
strokeWidth: number;
|
|
18
20
|
arcColors?: string[];
|
|
19
21
|
showGroupTicks: boolean;
|
|
20
22
|
showMajorTicks: boolean;
|
|
@@ -60,6 +62,8 @@ export declare interface NumberLineProps {
|
|
|
60
62
|
groupSize: number;
|
|
61
63
|
groupCount: number;
|
|
62
64
|
tickStep: number;
|
|
65
|
+
/** 用户自由绘制笔迹的固定视觉线宽,单位为 CSS 像素;默认 2.5。 */
|
|
66
|
+
strokeWidth?: number;
|
|
63
67
|
arcColors?: string[];
|
|
64
68
|
/** show group boundary ticks (tallest). Default: true */
|
|
65
69
|
showGroupTicks?: boolean;
|
|
@@ -80,10 +84,14 @@ export declare interface NumberLineProps {
|
|
|
80
84
|
viewMax?: number;
|
|
81
85
|
/** fired whenever internal viewport changes (uncontrolled mode) */
|
|
82
86
|
onViewChange?: (viewMin: number, viewMax: number) => void;
|
|
87
|
+
/**
|
|
88
|
+
* 自定义主刻度标签可见性。提供该回调后,其返回值会覆盖组件的自动标签密度,
|
|
89
|
+
* 适用于小窗等需要按实际可用宽度降低标签数量的场景。
|
|
90
|
+
*/
|
|
83
91
|
getShowLabelStep?: (params: unknown) => boolean;
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
export declare function NumberLineProvider({ min, max, groupSize, groupCount, tickStep, arcColors, showGroupTicks, showMajorTicks, showMinorTicks, enableZoom, minZoom, maxZoom, showLabelStep, viewMin: controlledViewMin, viewMax: controlledViewMax, getShowLabelStep, onViewChange, children, }: NumberLineProviderProps): JSX_2.Element;
|
|
94
|
+
export declare function NumberLineProvider({ min, max, groupSize, groupCount, tickStep, strokeWidth: configuredStrokeWidth, arcColors, showGroupTicks, showMajorTicks, showMinorTicks, enableZoom, minZoom, maxZoom, showLabelStep, viewMin: controlledViewMin, viewMax: controlledViewMax, getShowLabelStep, onViewChange, children, }: NumberLineProviderProps): JSX_2.Element;
|
|
87
95
|
|
|
88
96
|
declare interface NumberLineProviderProps extends NumberLineProps {
|
|
89
97
|
children: default_2.ReactNode;
|
package/dist/index.js
CHANGED
|
@@ -221,6 +221,7 @@ function useNumberLineContext() {
|
|
|
221
221
|
if (!ctx) throw new Error("Must be used inside <NumberLineProvider>");
|
|
222
222
|
return ctx;
|
|
223
223
|
}
|
|
224
|
+
const DEFAULT_DRAWING_STROKE_WIDTH = 2.5;
|
|
224
225
|
function normalizeDomain(lo, hi) {
|
|
225
226
|
const a = Number(lo);
|
|
226
227
|
const b = Number(hi);
|
|
@@ -253,6 +254,7 @@ function NumberLineProvider({
|
|
|
253
254
|
groupSize,
|
|
254
255
|
groupCount,
|
|
255
256
|
tickStep,
|
|
257
|
+
strokeWidth: configuredStrokeWidth,
|
|
256
258
|
arcColors,
|
|
257
259
|
showGroupTicks = true,
|
|
258
260
|
showMajorTicks = true,
|
|
@@ -268,6 +270,7 @@ function NumberLineProvider({
|
|
|
268
270
|
children
|
|
269
271
|
}) {
|
|
270
272
|
const { min: domainMin, max: domainMax } = normalizeDomain(min, max);
|
|
273
|
+
const strokeWidth = typeof configuredStrokeWidth === "number" && Number.isFinite(configuredStrokeWidth) && configuredStrokeWidth > 0 ? configuredStrokeWidth : DEFAULT_DRAWING_STROKE_WIDTH;
|
|
271
274
|
const [selectedArcIndices, setSelectedArcIndices] = useState(/* @__PURE__ */ new Set());
|
|
272
275
|
const [deletedArcIndices, setDeletedArcIndices] = useState(/* @__PURE__ */ new Set());
|
|
273
276
|
const [groupTicksHidden, setGroupTicksHidden] = useState(false);
|
|
@@ -355,7 +358,6 @@ function NumberLineProvider({
|
|
|
355
358
|
);
|
|
356
359
|
const onPointerDown = useCallback(
|
|
357
360
|
(e) => {
|
|
358
|
-
e.currentTarget.focus();
|
|
359
361
|
if (drawing.tool === "eraser") {
|
|
360
362
|
if (eraseGesture.current) return;
|
|
361
363
|
const coords2 = getLocalCoords(e);
|
|
@@ -550,6 +552,7 @@ function NumberLineProvider({
|
|
|
550
552
|
groupSize,
|
|
551
553
|
groupCount,
|
|
552
554
|
tickStep,
|
|
555
|
+
strokeWidth,
|
|
553
556
|
arcColors,
|
|
554
557
|
showGroupTicks: showGroupTicks && !groupTicksHidden,
|
|
555
558
|
showMajorTicks,
|
|
@@ -729,14 +732,15 @@ const AxisLayer = React.memo(function AxisLayer2({
|
|
|
729
732
|
const renderAsMajor = !renderAsGroup && majorActive;
|
|
730
733
|
const renderAsHalf = !renderAsGroup && !renderAsMajor && halfTickOffset > 0 && Math.abs(offset % step - halfTickOffset) < 1e-9;
|
|
731
734
|
const showLabel = (renderAsGroup || renderAsMajor) && offset % labelStep === 0;
|
|
732
|
-
const currentShowLabelStep = typeof getShowLabelStep
|
|
735
|
+
const currentShowLabelStep = typeof getShowLabelStep === "function" ? getShowLabelStep({
|
|
733
736
|
viewMin,
|
|
734
737
|
viewMax,
|
|
735
738
|
offset,
|
|
736
739
|
labelStep,
|
|
737
740
|
length,
|
|
738
741
|
v
|
|
739
|
-
});
|
|
742
|
+
}) : void 0;
|
|
743
|
+
const shouldShowLabel = currentShowLabelStep ?? (showLabel || showLabelStep);
|
|
740
744
|
const tickH = renderAsGroup ? 7 : renderAsMajor ? 10 : renderAsHalf ? 8 : 6;
|
|
741
745
|
const strokeW = renderAsGroup ? 4 : renderAsMajor ? 2 : renderAsHalf ? 2 : 1;
|
|
742
746
|
const arcIndex = renderAsGroup ? offset > 0 ? offset / groupSize - 1 : offset === 0 ? 0 : -1 : -1;
|
|
@@ -754,7 +758,7 @@ const AxisLayer = React.memo(function AxisLayer2({
|
|
|
754
758
|
strokeWidth: strokeW
|
|
755
759
|
}
|
|
756
760
|
),
|
|
757
|
-
|
|
761
|
+
shouldShowLabel && /* @__PURE__ */ jsx(
|
|
758
762
|
"text",
|
|
759
763
|
{
|
|
760
764
|
x,
|
|
@@ -788,6 +792,7 @@ function AnimatedArc({
|
|
|
788
792
|
max,
|
|
789
793
|
hitCursor,
|
|
790
794
|
shouldAnimate,
|
|
795
|
+
showLabel,
|
|
791
796
|
onArcClick
|
|
792
797
|
}) {
|
|
793
798
|
const measureRef = useRef(null);
|
|
@@ -870,7 +875,7 @@ function AnimatedArc({
|
|
|
870
875
|
pointerEvents: "none"
|
|
871
876
|
}
|
|
872
877
|
),
|
|
873
|
-
arcVisible && /* @__PURE__ */ jsx(
|
|
878
|
+
arcVisible && showLabel && /* @__PURE__ */ jsx(
|
|
874
879
|
"path",
|
|
875
880
|
{
|
|
876
881
|
d: arcPath,
|
|
@@ -967,6 +972,8 @@ const GroupArcsLayer = memo(function GroupArcsLayer2({
|
|
|
967
972
|
if (groupSize <= 0 || groupCount <= 0) return null;
|
|
968
973
|
const shouldAnimate = groupSize > 0 && groupCount > 0;
|
|
969
974
|
const toX = createValueToX(viewMin, viewMax, viewWidth, horizontalLayout);
|
|
975
|
+
const pixelsPerGroup = Math.abs(toX(min + groupSize) - toX(min));
|
|
976
|
+
const arcLabelInterval = Math.max(1, Math.ceil(36 / Math.max(1, pixelsPerGroup)));
|
|
970
977
|
const arcs = [];
|
|
971
978
|
for (let g = 0; g < groupCount; g++) {
|
|
972
979
|
if (deletedArcIndices.has(g)) continue;
|
|
@@ -986,6 +993,7 @@ const GroupArcsLayer = memo(function GroupArcsLayer2({
|
|
|
986
993
|
);
|
|
987
994
|
const arcPath = buildArcPath({ x1, x2, y: axisY, rx, ry });
|
|
988
995
|
const isSelected = selectedArcIndices.has(g);
|
|
996
|
+
const showLabel = arcLabelInterval === 1 || (g + 1) % arcLabelInterval === 0 || g === groupCount - 1;
|
|
989
997
|
const hitCursor = tool === "none" ? "pointer" : "default";
|
|
990
998
|
arcs.push(
|
|
991
999
|
/* @__PURE__ */ jsx(
|
|
@@ -1003,6 +1011,7 @@ const GroupArcsLayer = memo(function GroupArcsLayer2({
|
|
|
1003
1011
|
max,
|
|
1004
1012
|
hitCursor,
|
|
1005
1013
|
shouldAnimate,
|
|
1014
|
+
showLabel,
|
|
1006
1015
|
onArcClick: (index, e) => {
|
|
1007
1016
|
if (tool === "none") {
|
|
1008
1017
|
e.stopPropagation();
|
|
@@ -1016,7 +1025,7 @@ const GroupArcsLayer = memo(function GroupArcsLayer2({
|
|
|
1016
1025
|
}
|
|
1017
1026
|
return /* @__PURE__ */ jsx("g", { className: "nl-arcs-layer", children: arcs });
|
|
1018
1027
|
});
|
|
1019
|
-
function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds, onStrokeClick }) {
|
|
1028
|
+
function DrawingLayer({ strokes, liveStroke, tool, width, height, strokeWidth, eraseHoverIds, onStrokeClick }) {
|
|
1020
1029
|
return /* @__PURE__ */ jsxs("g", { className: "nl-drawing-layer", transform: `scale(${width} ${height})`, children: [
|
|
1021
1030
|
strokes.map((s) => /* @__PURE__ */ jsxs("g", { children: [
|
|
1022
1031
|
(s.selected || tool === "eraser" && eraseHoverIds.has(s.id)) && /* @__PURE__ */ jsx(
|
|
@@ -1058,7 +1067,7 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds,
|
|
|
1058
1067
|
d: s.d,
|
|
1059
1068
|
fill: "none",
|
|
1060
1069
|
stroke: "#7c3aed",
|
|
1061
|
-
strokeWidth
|
|
1070
|
+
strokeWidth,
|
|
1062
1071
|
strokeLinecap: "round",
|
|
1063
1072
|
strokeLinejoin: "round",
|
|
1064
1073
|
vectorEffect: "non-scaling-stroke",
|
|
@@ -1072,7 +1081,7 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, eraseHoverIds,
|
|
|
1072
1081
|
d: liveStroke.d,
|
|
1073
1082
|
fill: "none",
|
|
1074
1083
|
stroke: "#7c3aed",
|
|
1075
|
-
strokeWidth
|
|
1084
|
+
strokeWidth,
|
|
1076
1085
|
strokeLinecap: "round",
|
|
1077
1086
|
strokeLinejoin: "round",
|
|
1078
1087
|
vectorEffect: "non-scaling-stroke",
|
|
@@ -1097,6 +1106,7 @@ function NumberLine({ className }) {
|
|
|
1097
1106
|
groupSize,
|
|
1098
1107
|
groupCount,
|
|
1099
1108
|
tickStep,
|
|
1109
|
+
strokeWidth,
|
|
1100
1110
|
arcColors,
|
|
1101
1111
|
showGroupTicks,
|
|
1102
1112
|
showMajorTicks,
|
|
@@ -1279,7 +1289,11 @@ function NumberLine({ className }) {
|
|
|
1279
1289
|
display: "block",
|
|
1280
1290
|
cursor: svgCursor,
|
|
1281
1291
|
touchAction: "none",
|
|
1282
|
-
overflow: "visible"
|
|
1292
|
+
overflow: "visible",
|
|
1293
|
+
// 数轴自身带有键盘快捷键,所以 SVG 需要保留 tabIndex;但各浏览器会在鼠标、
|
|
1294
|
+
// 触摸和键盘聚焦时都绘制一圈与教学内容无关的边框。该工具使用按钮状态表达
|
|
1295
|
+
// 可操作性,因此统一移除原生 outline,避免它被误认为数轴的选中标记。
|
|
1296
|
+
outline: "none"
|
|
1283
1297
|
},
|
|
1284
1298
|
onPointerDown: handlePointerDown,
|
|
1285
1299
|
onPointerMove: handlePointerMove,
|
|
@@ -1344,6 +1358,7 @@ function NumberLine({ className }) {
|
|
|
1344
1358
|
tool,
|
|
1345
1359
|
width: w,
|
|
1346
1360
|
height: h,
|
|
1361
|
+
strokeWidth,
|
|
1347
1362
|
eraseHoverIds,
|
|
1348
1363
|
onStrokeClick
|
|
1349
1364
|
}
|