@dcg-overseas/number-line 0.1.5 → 0.1.7
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 +39 -13
- package/dist/index.js +39 -13
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -234,6 +234,19 @@ function useNumberLineContext() {
|
|
|
234
234
|
if (!ctx) throw new Error("Must be used inside <NumberLineProvider>");
|
|
235
235
|
return ctx;
|
|
236
236
|
}
|
|
237
|
+
function normalizeDomain(lo, hi) {
|
|
238
|
+
const a = Number(lo);
|
|
239
|
+
const b = Number(hi);
|
|
240
|
+
const x = Math.min(a, b);
|
|
241
|
+
const y = Math.max(a, b);
|
|
242
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
243
|
+
return { min: 0, max: 1 };
|
|
244
|
+
}
|
|
245
|
+
if (y <= x) {
|
|
246
|
+
return { min: x, max: x + 1 };
|
|
247
|
+
}
|
|
248
|
+
return { min: x, max: y };
|
|
249
|
+
}
|
|
237
250
|
function NumberLineProvider({
|
|
238
251
|
min,
|
|
239
252
|
max,
|
|
@@ -252,14 +265,15 @@ function NumberLineProvider({
|
|
|
252
265
|
onViewChange,
|
|
253
266
|
children
|
|
254
267
|
}) {
|
|
268
|
+
const { min: domainMin, max: domainMax } = normalizeDomain(min, max);
|
|
255
269
|
const [selectedArcIndices, setSelectedArcIndices] = React.useState(/* @__PURE__ */ new Set());
|
|
256
270
|
const [deletedArcIndices, setDeletedArcIndices] = React.useState(/* @__PURE__ */ new Set());
|
|
257
271
|
const { ref: svgRef, width: containerWidth, height: containerHeight } = useContainerSize();
|
|
258
272
|
const history = useHistory();
|
|
259
273
|
const drawing = useDrawing();
|
|
260
274
|
const { viewMin, viewMax, zoomAt, panByFraction, resetView } = useZoom({
|
|
261
|
-
dataMin:
|
|
262
|
-
dataMax:
|
|
275
|
+
dataMin: domainMin,
|
|
276
|
+
dataMax: domainMax,
|
|
263
277
|
minZoom,
|
|
264
278
|
maxZoom,
|
|
265
279
|
controlledMin: controlledViewMin,
|
|
@@ -271,7 +285,7 @@ function NumberLineProvider({
|
|
|
271
285
|
React.useEffect(() => {
|
|
272
286
|
setDeletedArcIndices(/* @__PURE__ */ new Set());
|
|
273
287
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
274
|
-
}, [
|
|
288
|
+
}, [domainMin, domainMax, groupSize, groupCount]);
|
|
275
289
|
const getLocalCoords = React.useCallback(
|
|
276
290
|
(e) => {
|
|
277
291
|
const svg = svgRef.current;
|
|
@@ -428,8 +442,8 @@ function NumberLineProvider({
|
|
|
428
442
|
containerWidth,
|
|
429
443
|
containerHeight,
|
|
430
444
|
svgCursor,
|
|
431
|
-
min,
|
|
432
|
-
max,
|
|
445
|
+
min: domainMin,
|
|
446
|
+
max: domainMax,
|
|
433
447
|
groupSize,
|
|
434
448
|
groupCount,
|
|
435
449
|
tickStep,
|
|
@@ -774,9 +788,11 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
|
|
|
774
788
|
)
|
|
775
789
|
] });
|
|
776
790
|
}
|
|
777
|
-
const
|
|
778
|
-
const
|
|
779
|
-
const
|
|
791
|
+
const PAD_LEFT_LEGACY = 30;
|
|
792
|
+
const PAD_RIGHT_LEGACY = 40;
|
|
793
|
+
const PAD_X = (PAD_LEFT_LEGACY + PAD_RIGHT_LEGACY) / 2;
|
|
794
|
+
const LABEL_SPACE_BELOW_AXIS = 42;
|
|
795
|
+
const LABEL_ABOVE_AXIS_SLOP = 32;
|
|
780
796
|
const ARC_TOP_PADDING = 16;
|
|
781
797
|
const MIN_ARC_HEIGHT = 24;
|
|
782
798
|
function NumberLine({ className }) {
|
|
@@ -813,7 +829,17 @@ function NumberLine({ className }) {
|
|
|
813
829
|
} = useNumberLineContext();
|
|
814
830
|
const w = containerWidth;
|
|
815
831
|
const h = containerHeight;
|
|
816
|
-
const
|
|
832
|
+
const range = Math.max(viewMax - viewMin, 1e-6);
|
|
833
|
+
const usable = Math.max(w - 2 * PAD_X, 1);
|
|
834
|
+
let ryTypical = MIN_ARC_HEIGHT * 0.5;
|
|
835
|
+
if (groupSize > 0 && groupCount > 0) {
|
|
836
|
+
const rx = usable * groupSize / range / 2;
|
|
837
|
+
ryTypical = Math.min(rx * 0.7, h * 0.48);
|
|
838
|
+
}
|
|
839
|
+
const minAxisY = MIN_ARC_HEIGHT + ARC_TOP_PADDING;
|
|
840
|
+
const maxAxisY = h - LABEL_SPACE_BELOW_AXIS;
|
|
841
|
+
const axisYCentered = (h + ryTypical + LABEL_ABOVE_AXIS_SLOP - LABEL_SPACE_BELOW_AXIS) / 2;
|
|
842
|
+
const axisY = Math.min(maxAxisY, Math.max(minAxisY, axisYCentered));
|
|
817
843
|
const arcMaxHeight = Math.max(MIN_ARC_HEIGHT, axisY - ARC_TOP_PADDING);
|
|
818
844
|
const [isZoomActive, setIsZoomActive] = React.useState(false);
|
|
819
845
|
const [isPanning, setIsPanning] = React.useState(false);
|
|
@@ -980,8 +1006,8 @@ function NumberLine({ className }) {
|
|
|
980
1006
|
viewMin,
|
|
981
1007
|
viewMax,
|
|
982
1008
|
axisY,
|
|
983
|
-
padLeft:
|
|
984
|
-
padRight:
|
|
1009
|
+
padLeft: PAD_X,
|
|
1010
|
+
padRight: PAD_X,
|
|
985
1011
|
viewWidth: w,
|
|
986
1012
|
containerWidth: w,
|
|
987
1013
|
groupSize,
|
|
@@ -1002,8 +1028,8 @@ function NumberLine({ className }) {
|
|
|
1002
1028
|
groupSize,
|
|
1003
1029
|
groupCount,
|
|
1004
1030
|
axisY,
|
|
1005
|
-
padLeft:
|
|
1006
|
-
padRight:
|
|
1031
|
+
padLeft: PAD_X,
|
|
1032
|
+
padRight: PAD_X,
|
|
1007
1033
|
viewWidth: w,
|
|
1008
1034
|
arcMaxHeight,
|
|
1009
1035
|
tool,
|
package/dist/index.js
CHANGED
|
@@ -232,6 +232,19 @@ function useNumberLineContext() {
|
|
|
232
232
|
if (!ctx) throw new Error("Must be used inside <NumberLineProvider>");
|
|
233
233
|
return ctx;
|
|
234
234
|
}
|
|
235
|
+
function normalizeDomain(lo, hi) {
|
|
236
|
+
const a = Number(lo);
|
|
237
|
+
const b = Number(hi);
|
|
238
|
+
const x = Math.min(a, b);
|
|
239
|
+
const y = Math.max(a, b);
|
|
240
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
241
|
+
return { min: 0, max: 1 };
|
|
242
|
+
}
|
|
243
|
+
if (y <= x) {
|
|
244
|
+
return { min: x, max: x + 1 };
|
|
245
|
+
}
|
|
246
|
+
return { min: x, max: y };
|
|
247
|
+
}
|
|
235
248
|
function NumberLineProvider({
|
|
236
249
|
min,
|
|
237
250
|
max,
|
|
@@ -250,14 +263,15 @@ function NumberLineProvider({
|
|
|
250
263
|
onViewChange,
|
|
251
264
|
children
|
|
252
265
|
}) {
|
|
266
|
+
const { min: domainMin, max: domainMax } = normalizeDomain(min, max);
|
|
253
267
|
const [selectedArcIndices, setSelectedArcIndices] = useState(/* @__PURE__ */ new Set());
|
|
254
268
|
const [deletedArcIndices, setDeletedArcIndices] = useState(/* @__PURE__ */ new Set());
|
|
255
269
|
const { ref: svgRef, width: containerWidth, height: containerHeight } = useContainerSize();
|
|
256
270
|
const history = useHistory();
|
|
257
271
|
const drawing = useDrawing();
|
|
258
272
|
const { viewMin, viewMax, zoomAt, panByFraction, resetView } = useZoom({
|
|
259
|
-
dataMin:
|
|
260
|
-
dataMax:
|
|
273
|
+
dataMin: domainMin,
|
|
274
|
+
dataMax: domainMax,
|
|
261
275
|
minZoom,
|
|
262
276
|
maxZoom,
|
|
263
277
|
controlledMin: controlledViewMin,
|
|
@@ -269,7 +283,7 @@ function NumberLineProvider({
|
|
|
269
283
|
useEffect(() => {
|
|
270
284
|
setDeletedArcIndices(/* @__PURE__ */ new Set());
|
|
271
285
|
setSelectedArcIndices(/* @__PURE__ */ new Set());
|
|
272
|
-
}, [
|
|
286
|
+
}, [domainMin, domainMax, groupSize, groupCount]);
|
|
273
287
|
const getLocalCoords = useCallback(
|
|
274
288
|
(e) => {
|
|
275
289
|
const svg = svgRef.current;
|
|
@@ -426,8 +440,8 @@ function NumberLineProvider({
|
|
|
426
440
|
containerWidth,
|
|
427
441
|
containerHeight,
|
|
428
442
|
svgCursor,
|
|
429
|
-
min,
|
|
430
|
-
max,
|
|
443
|
+
min: domainMin,
|
|
444
|
+
max: domainMax,
|
|
431
445
|
groupSize,
|
|
432
446
|
groupCount,
|
|
433
447
|
tickStep,
|
|
@@ -772,9 +786,11 @@ function DrawingLayer({ strokes, liveStroke, tool, width, height, onStrokeClick
|
|
|
772
786
|
)
|
|
773
787
|
] });
|
|
774
788
|
}
|
|
775
|
-
const
|
|
776
|
-
const
|
|
777
|
-
const
|
|
789
|
+
const PAD_LEFT_LEGACY = 30;
|
|
790
|
+
const PAD_RIGHT_LEGACY = 40;
|
|
791
|
+
const PAD_X = (PAD_LEFT_LEGACY + PAD_RIGHT_LEGACY) / 2;
|
|
792
|
+
const LABEL_SPACE_BELOW_AXIS = 42;
|
|
793
|
+
const LABEL_ABOVE_AXIS_SLOP = 32;
|
|
778
794
|
const ARC_TOP_PADDING = 16;
|
|
779
795
|
const MIN_ARC_HEIGHT = 24;
|
|
780
796
|
function NumberLine({ className }) {
|
|
@@ -811,7 +827,17 @@ function NumberLine({ className }) {
|
|
|
811
827
|
} = useNumberLineContext();
|
|
812
828
|
const w = containerWidth;
|
|
813
829
|
const h = containerHeight;
|
|
814
|
-
const
|
|
830
|
+
const range = Math.max(viewMax - viewMin, 1e-6);
|
|
831
|
+
const usable = Math.max(w - 2 * PAD_X, 1);
|
|
832
|
+
let ryTypical = MIN_ARC_HEIGHT * 0.5;
|
|
833
|
+
if (groupSize > 0 && groupCount > 0) {
|
|
834
|
+
const rx = usable * groupSize / range / 2;
|
|
835
|
+
ryTypical = Math.min(rx * 0.7, h * 0.48);
|
|
836
|
+
}
|
|
837
|
+
const minAxisY = MIN_ARC_HEIGHT + ARC_TOP_PADDING;
|
|
838
|
+
const maxAxisY = h - LABEL_SPACE_BELOW_AXIS;
|
|
839
|
+
const axisYCentered = (h + ryTypical + LABEL_ABOVE_AXIS_SLOP - LABEL_SPACE_BELOW_AXIS) / 2;
|
|
840
|
+
const axisY = Math.min(maxAxisY, Math.max(minAxisY, axisYCentered));
|
|
815
841
|
const arcMaxHeight = Math.max(MIN_ARC_HEIGHT, axisY - ARC_TOP_PADDING);
|
|
816
842
|
const [isZoomActive, setIsZoomActive] = useState(false);
|
|
817
843
|
const [isPanning, setIsPanning] = useState(false);
|
|
@@ -978,8 +1004,8 @@ function NumberLine({ className }) {
|
|
|
978
1004
|
viewMin,
|
|
979
1005
|
viewMax,
|
|
980
1006
|
axisY,
|
|
981
|
-
padLeft:
|
|
982
|
-
padRight:
|
|
1007
|
+
padLeft: PAD_X,
|
|
1008
|
+
padRight: PAD_X,
|
|
983
1009
|
viewWidth: w,
|
|
984
1010
|
containerWidth: w,
|
|
985
1011
|
groupSize,
|
|
@@ -1000,8 +1026,8 @@ function NumberLine({ className }) {
|
|
|
1000
1026
|
groupSize,
|
|
1001
1027
|
groupCount,
|
|
1002
1028
|
axisY,
|
|
1003
|
-
padLeft:
|
|
1004
|
-
padRight:
|
|
1029
|
+
padLeft: PAD_X,
|
|
1030
|
+
padRight: PAD_X,
|
|
1005
1031
|
viewWidth: w,
|
|
1006
1032
|
arcMaxHeight,
|
|
1007
1033
|
tool,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dcg-overseas/number-line",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Interactive number line component with group arcs and freehand drawing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
"dist"
|
|
24
24
|
],
|
|
25
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
|
+
},
|
|
26
32
|
"peerDependencies": {
|
|
27
33
|
"react": "^18.0.0",
|
|
28
34
|
"react-dom": "^18.0.0"
|
|
@@ -37,11 +43,5 @@
|
|
|
37
43
|
"publishConfig": {
|
|
38
44
|
"access": "public",
|
|
39
45
|
"registry": "https://registry.npmjs.org/"
|
|
40
|
-
},
|
|
41
|
-
"scripts": {
|
|
42
|
-
"build": "vite build",
|
|
43
|
-
"build:watch": "vite build --watch",
|
|
44
|
-
"typecheck": "tsc --noEmit",
|
|
45
|
-
"clean": "rm -rf dist *.tsbuildinfo"
|
|
46
46
|
}
|
|
47
|
-
}
|
|
47
|
+
}
|