@xom11/whiteboard 0.7.0 → 0.10.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 +51 -1
- package/dist/chunk-74VEEZBV.mjs +619 -0
- package/dist/chunk-74VEEZBV.mjs.map +1 -0
- package/dist/{chunk-BJX4YNA5.mjs → chunk-G7FR3AIV.mjs} +68 -12
- package/dist/chunk-G7FR3AIV.mjs.map +1 -0
- package/dist/{chunk-SHFOGORM.mjs → chunk-PDKKDZ4H.mjs} +4 -4
- package/dist/{chunk-SHFOGORM.mjs.map → chunk-PDKKDZ4H.mjs.map} +1 -1
- package/dist/chunk-PWIMZIB6.mjs +62 -0
- package/dist/chunk-PWIMZIB6.mjs.map +1 -0
- package/dist/{chunk-LPM4MM45.mjs → chunk-SBDMF4NQ.mjs} +3 -2
- package/dist/chunk-SBDMF4NQ.mjs.map +1 -0
- package/dist/chunk-WQOABS6N.mjs +197 -0
- package/dist/chunk-WQOABS6N.mjs.map +1 -0
- package/dist/{chunk-3SSQKRRO.mjs → chunk-ZVN356JZ.mjs} +4 -4
- package/dist/{chunk-3SSQKRRO.mjs.map → chunk-ZVN356JZ.mjs.map} +1 -1
- package/dist/geometry-2d.js +344 -228
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +2 -2
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +3411 -1277
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +3 -2
- package/dist/graph-2d.js +360 -66
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +2 -2
- package/dist/{host-T2W6R6SO.mjs → host-DJETSFCG.mjs} +272 -223
- package/dist/host-DJETSFCG.mjs.map +1 -0
- package/dist/{host-2QGKMGCT.mjs → host-LZH2FZ2N.mjs} +3 -3
- package/dist/{host-2QGKMGCT.mjs.map → host-LZH2FZ2N.mjs.map} +1 -1
- package/dist/host-N6ACNJKI.mjs +3226 -0
- package/dist/host-N6ACNJKI.mjs.map +1 -0
- package/dist/index.d.mts +133 -6
- package/dist/index.d.ts +133 -6
- package/dist/index.js +5634 -1999
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1231 -146
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -6
- package/dist/chunk-BJX4YNA5.mjs.map +0 -1
- package/dist/chunk-DJTBZEAR.mjs +0 -25
- package/dist/chunk-DJTBZEAR.mjs.map +0 -1
- package/dist/chunk-HM7RIXJE.mjs +0 -331
- package/dist/chunk-HM7RIXJE.mjs.map +0 -1
- package/dist/chunk-HYXFHEDJ.mjs +0 -129
- package/dist/chunk-HYXFHEDJ.mjs.map +0 -1
- package/dist/chunk-LPM4MM45.mjs.map +0 -1
- package/dist/host-T2W6R6SO.mjs.map +0 -1
- package/dist/host-XUFON6CQ.mjs +0 -1422
- package/dist/host-XUFON6CQ.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { serializeBoard, renderGeometrySvgFromState, isGeometryCustomData } from './chunk-
|
|
3
|
-
import { useChordShortcut, MobileToolDrawer } from './chunk-
|
|
2
|
+
import { serializeBoard, renderGeometrySvgFromState, isGeometryCustomData, safeJsx } from './chunk-G7FR3AIV.mjs';
|
|
3
|
+
import { useChordShortcut, MobileToolDrawer } from './chunk-SBDMF4NQ.mjs';
|
|
4
4
|
import { resolveAttrColors, paletteFor, themeLabel, themeAxis, themeGrid } from './chunk-HTBLO5JO.mjs';
|
|
5
5
|
import { useIsMobile } from './chunk-P2AOIF7S.mjs';
|
|
6
6
|
import { insertStampImage } from './chunk-C6SCVOMC.mjs';
|
|
@@ -259,8 +259,12 @@ function letterForGroup(g) {
|
|
|
259
259
|
}
|
|
260
260
|
function objKind(obj) {
|
|
261
261
|
if (!obj) return "other";
|
|
262
|
+
const ec = typeof obj.elementClass === "number" ? obj.elementClass : null;
|
|
263
|
+
if (ec === 1) return "point";
|
|
264
|
+
if (ec === 2) return "line";
|
|
265
|
+
if (ec === 3) return "circle";
|
|
262
266
|
const e = (obj.elType || obj.type || "").toString().toLowerCase();
|
|
263
|
-
if (e === "point" || e === "glider" || e === "midpoint") return "point";
|
|
267
|
+
if (e === "point" || e === "glider" || e === "midpoint" || e === "intersection" || e === "otherintersection" || e === "reflection" || e === "mirrorpoint" || e === "mirrorelement" || e === "orthogonalprojection" || e === "parallelpoint") return "point";
|
|
264
268
|
if (e === "line" || e === "segment" || e === "arrow" || e === "axis" || e === "normal" || e === "parallel" || e === "perpendicular" || e === "tangent" || e === "bisector" || e === "perpendicularsegment") return "line";
|
|
265
269
|
if (e === "circle" || e === "circumcircle") return "circle";
|
|
266
270
|
return "other";
|
|
@@ -282,7 +286,7 @@ function handleDown(ctx, e) {
|
|
|
282
286
|
if (!sc) return;
|
|
283
287
|
const [sx, sy] = sc;
|
|
284
288
|
const hits2 = ctx.objectsAt(e).map(ctx.promoteLabel).filter((o) => o !== ctx.axisObjsRef.current.x && o !== ctx.axisObjsRef.current.y);
|
|
285
|
-
const obj = hits2.find((o) => objKind(o) === "point") ??
|
|
289
|
+
const obj = hits2.find((o) => objKind(o) === "point") ?? ctx.findNearestPoint(e, 12) ?? hits2[0];
|
|
286
290
|
if (obj) {
|
|
287
291
|
const shift = !!(e.shiftKey || e.altKey);
|
|
288
292
|
ctx.toggleSelect(obj, shift);
|
|
@@ -320,14 +324,8 @@ function handleDown(ctx, e) {
|
|
|
320
324
|
const tmp1 = ctx.boardRef.current.create("intersection", [a, b, 1], { visible: false, withLabel: false });
|
|
321
325
|
const d0 = Math.hypot((tmp0.X?.() ?? 0) - x, (tmp0.Y?.() ?? 0) - y);
|
|
322
326
|
const d1 = Math.hypot((tmp1.X?.() ?? 0) - x, (tmp1.Y?.() ?? 0) - y);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
} catch {
|
|
326
|
-
}
|
|
327
|
-
try {
|
|
328
|
-
ctx.boardRef.current.removeObject(tmp1);
|
|
329
|
-
} catch {
|
|
330
|
-
}
|
|
327
|
+
safeJsx("handlers.removeObject(intersect.tmp0)", () => ctx.boardRef.current.removeObject(tmp0));
|
|
328
|
+
safeJsx("handlers.removeObject(intersect.tmp1)", () => ctx.boardRef.current.removeObject(tmp1));
|
|
331
329
|
const idx = d0 <= d1 ? 0 : 1;
|
|
332
330
|
ctx.create("intersection", [aId, bId, idx], attrs);
|
|
333
331
|
}
|
|
@@ -364,7 +362,7 @@ function handleDown(ctx, e) {
|
|
|
364
362
|
})();
|
|
365
363
|
if (ctx.pendingRef.current.length > 0 && ctx.boardRef.current) {
|
|
366
364
|
const prev = ctx.pendingRef.current[ctx.pendingRef.current.length - 1];
|
|
367
|
-
|
|
365
|
+
safeJsx("handlers.createPreviewSegment", () => {
|
|
368
366
|
const seg = ctx.boardRef.current.create("segment", [prev, pick2], {
|
|
369
367
|
strokeColor: "#3b82f6",
|
|
370
368
|
strokeWidth: 1.5,
|
|
@@ -374,8 +372,7 @@ function handleDown(ctx, e) {
|
|
|
374
372
|
withLabel: false
|
|
375
373
|
});
|
|
376
374
|
ctx.previewSegRef.current.push(seg);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
375
|
+
});
|
|
379
376
|
}
|
|
380
377
|
ctx.pendingRef.current.push(pick2);
|
|
381
378
|
ctx.setPendingCount(ctx.pendingRef.current.length);
|
|
@@ -486,10 +483,7 @@ function handleUp(ctx, e) {
|
|
|
486
483
|
if (!sc2) return;
|
|
487
484
|
const [ex, ey] = sc2;
|
|
488
485
|
if (mq.rect) {
|
|
489
|
-
|
|
490
|
-
ctx.boardRef.current?.removeObject(mq.rect);
|
|
491
|
-
} catch {
|
|
492
|
-
}
|
|
486
|
+
safeJsx("handlers.removeObject(marquee.rect)", () => ctx.boardRef.current?.removeObject(mq.rect));
|
|
493
487
|
}
|
|
494
488
|
if (Math.hypot(ex - mq.startSx, ey - mq.startSy) < 4) return;
|
|
495
489
|
const x1 = Math.min(mq.startSx, ex), x2 = Math.max(mq.startSx, ex);
|
|
@@ -522,10 +516,7 @@ function handleUp(ctx, e) {
|
|
|
522
516
|
}
|
|
523
517
|
}
|
|
524
518
|
ctx.setSelectionTick((tt) => tt + 1);
|
|
525
|
-
|
|
526
|
-
board.update();
|
|
527
|
-
} catch {
|
|
528
|
-
}
|
|
519
|
+
safeJsx("handlers.board.update(marquee)", () => board.update());
|
|
529
520
|
return;
|
|
530
521
|
}
|
|
531
522
|
if (t !== "move") return;
|
|
@@ -538,7 +529,7 @@ function handleUp(ctx, e) {
|
|
|
538
529
|
const moved = Math.hypot(sx - start.sx, sy - start.sy);
|
|
539
530
|
if (moved > 4) return;
|
|
540
531
|
const hits = ctx.objectsAt(e).map(ctx.promoteLabel).filter((o) => o !== ctx.axisObjsRef.current.x && o !== ctx.axisObjsRef.current.y);
|
|
541
|
-
const best = hits.find((o) => objKind(o) === "point") ??
|
|
532
|
+
const best = hits.find((o) => objKind(o) === "point") ?? ctx.findNearestPoint(e, 12) ?? hits[0];
|
|
542
533
|
if (!best) {
|
|
543
534
|
ctx.lastMoveClickRef.current = { obj: null, time: 0 };
|
|
544
535
|
return;
|
|
@@ -572,12 +563,9 @@ function handleMove(ctx, e) {
|
|
|
572
563
|
const [x2u, y2u] = ux2 && ux2.length >= 2 ? [ux2[0], ux2[1]] : toUsr(Math.max(startSx, sx), Math.max(startSy, sy));
|
|
573
564
|
const rect = ctx.marqueeRef.current.rect;
|
|
574
565
|
if (rect) {
|
|
575
|
-
|
|
576
|
-
ctx.boardRef.current.removeObject(rect);
|
|
577
|
-
} catch {
|
|
578
|
-
}
|
|
566
|
+
safeJsx("handlers.removeObject(marquee.prevRect)", () => ctx.boardRef.current.removeObject(rect));
|
|
579
567
|
}
|
|
580
|
-
|
|
568
|
+
safeJsx("handlers.createMarqueePolygon", () => {
|
|
581
569
|
ctx.marqueeRef.current.rect = ctx.boardRef.current.create("polygon", [
|
|
582
570
|
[x1u, y1u],
|
|
583
571
|
[x2u, y1u],
|
|
@@ -592,8 +580,7 @@ function handleMove(ctx, e) {
|
|
|
592
580
|
highlight: false,
|
|
593
581
|
withLabel: false
|
|
594
582
|
});
|
|
595
|
-
}
|
|
596
|
-
}
|
|
583
|
+
});
|
|
597
584
|
}
|
|
598
585
|
return;
|
|
599
586
|
}
|
|
@@ -603,14 +590,13 @@ function handleMove(ctx, e) {
|
|
|
603
590
|
ctx.previewRafRef.current = requestAnimationFrame(() => {
|
|
604
591
|
ctx.previewRafRef.current = null;
|
|
605
592
|
if (!ctx.boardRef.current || !ctx.phantomRef.current) return;
|
|
606
|
-
|
|
593
|
+
safeJsx("handlers.phantomMove", () => {
|
|
607
594
|
const coords = ctx.boardRef.current.getUsrCoordsOfMouse(e);
|
|
608
595
|
const JXG = ctx.jxgRef.current;
|
|
609
596
|
if (!JXG) return;
|
|
610
597
|
ctx.phantomRef.current.setPositionDirectly(JXG.COORDS_BY_USER, [coords[0], coords[1]]);
|
|
611
598
|
ctx.boardRef.current.update();
|
|
612
|
-
}
|
|
613
|
-
}
|
|
599
|
+
});
|
|
614
600
|
});
|
|
615
601
|
}
|
|
616
602
|
var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
@@ -622,6 +608,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
622
608
|
const jxgRef = useRef(null);
|
|
623
609
|
const axisObjsRef = useRef({});
|
|
624
610
|
const creationLogRef = useRef([]);
|
|
611
|
+
const redoStackRef = useRef([]);
|
|
625
612
|
const [tool, setTool] = useState("move");
|
|
626
613
|
const toolRef = useRef("move");
|
|
627
614
|
toolRef.current = tool;
|
|
@@ -665,19 +652,31 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
665
652
|
const nextLocalId = useCallback(() => "j" + creationLogRef.current.length, []);
|
|
666
653
|
const resolveArgs = useCallback((args) => {
|
|
667
654
|
return args.map((a) => {
|
|
668
|
-
if (typeof a === "string"
|
|
669
|
-
return objMapRef.current.get(a);
|
|
655
|
+
if (typeof a === "string") {
|
|
656
|
+
if (objMapRef.current.has(a)) return objMapRef.current.get(a);
|
|
657
|
+
const m = /^(.+):border:(\d+)$/.exec(a);
|
|
658
|
+
if (m) {
|
|
659
|
+
const poly = objMapRef.current.get(m[1]);
|
|
660
|
+
const idx = parseInt(m[2], 10);
|
|
661
|
+
if (poly && Array.isArray(poly.borders) && poly.borders[idx]) {
|
|
662
|
+
return poly.borders[idx];
|
|
663
|
+
}
|
|
664
|
+
}
|
|
670
665
|
}
|
|
671
666
|
return a;
|
|
672
667
|
});
|
|
673
668
|
}, []);
|
|
669
|
+
const pushCreationLog = useCallback((entry) => {
|
|
670
|
+
creationLogRef.current.push(entry);
|
|
671
|
+
redoStackRef.current = [];
|
|
672
|
+
}, []);
|
|
674
673
|
const pushLog = useCallback(
|
|
675
674
|
(id, type, args, attrs, obj) => {
|
|
676
|
-
|
|
675
|
+
pushCreationLog({ id, type, args, attrs });
|
|
677
676
|
objMapRef.current.set(id, obj);
|
|
678
677
|
setHistoryTick((t) => t + 1);
|
|
679
678
|
},
|
|
680
|
-
[]
|
|
679
|
+
[pushCreationLog]
|
|
681
680
|
);
|
|
682
681
|
const create = useCallback(
|
|
683
682
|
(type, args, attrs = {}) => {
|
|
@@ -692,15 +691,27 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
692
691
|
[nextLocalId, resolveArgs, pushLog]
|
|
693
692
|
);
|
|
694
693
|
const localIdOf = useCallback((obj) => {
|
|
694
|
+
if (!obj) return null;
|
|
695
695
|
for (const [id, o] of objMapRef.current.entries()) {
|
|
696
696
|
if (o === obj) return id;
|
|
697
697
|
}
|
|
698
|
+
for (const [id, o] of objMapRef.current.entries()) {
|
|
699
|
+
const borders = o?.borders;
|
|
700
|
+
if (Array.isArray(borders)) {
|
|
701
|
+
const idx = borders.indexOf(obj);
|
|
702
|
+
if (idx >= 0) return `${id}:border:${idx}`;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
698
705
|
return null;
|
|
699
706
|
}, []);
|
|
700
707
|
const snapshotObject = useCallback((obj, anchorScreen) => {
|
|
701
708
|
const o = obj;
|
|
702
709
|
const k = objKind(o);
|
|
703
710
|
if (k !== "point" && k !== "line" && k !== "circle") return null;
|
|
711
|
+
for (const owner of objMapRef.current.values()) {
|
|
712
|
+
const borders = owner?.borders;
|
|
713
|
+
if (Array.isArray(borders) && borders.indexOf(o) >= 0) return null;
|
|
714
|
+
}
|
|
704
715
|
const v = o.visProp ?? {};
|
|
705
716
|
const showLabel = v.withlabel !== false;
|
|
706
717
|
const showValue = valueLabelsRef.current.has(o);
|
|
@@ -760,16 +771,10 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
760
771
|
if (patch.remove) {
|
|
761
772
|
const vl = valueLabelsRef.current.get(o);
|
|
762
773
|
if (vl) {
|
|
763
|
-
|
|
764
|
-
boardRef.current.removeObject(vl);
|
|
765
|
-
} catch {
|
|
766
|
-
}
|
|
774
|
+
safeJsx("MiniBoard.removeObject(valueLabel)", () => boardRef.current.removeObject(vl));
|
|
767
775
|
valueLabelsRef.current.delete(o);
|
|
768
776
|
}
|
|
769
|
-
|
|
770
|
-
boardRef.current.removeObject(o);
|
|
771
|
-
} catch {
|
|
772
|
-
}
|
|
777
|
+
safeJsx("MiniBoard.removeObject(target)", () => boardRef.current.removeObject(o));
|
|
773
778
|
const board = boardRef.current;
|
|
774
779
|
const aliveIds = /* @__PURE__ */ new Set();
|
|
775
780
|
for (const [id, obj2] of objMapRef.current.entries()) {
|
|
@@ -794,7 +799,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
794
799
|
const targetId = localIdOf(o);
|
|
795
800
|
if (targetId) {
|
|
796
801
|
const id = nextLocalId();
|
|
797
|
-
|
|
802
|
+
pushCreationLog({ id, type: "valueLabel", args: [targetId], attrs: {} });
|
|
798
803
|
objMapRef.current.set(id, txt);
|
|
799
804
|
setHistoryTick((t) => t + 1);
|
|
800
805
|
}
|
|
@@ -803,10 +808,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
803
808
|
const txt = valueLabelsRef.current.get(o);
|
|
804
809
|
valueLabelsRef.current.delete(o);
|
|
805
810
|
if (txt) {
|
|
806
|
-
|
|
807
|
-
boardRef.current.removeObject(txt);
|
|
808
|
-
} catch {
|
|
809
|
-
}
|
|
811
|
+
safeJsx("MiniBoard.removeObject(valueLabel.text)", () => boardRef.current.removeObject(txt));
|
|
810
812
|
const txtId = localIdOf(txt);
|
|
811
813
|
if (txtId) {
|
|
812
814
|
creationLogRef.current = creationLogRef.current.filter((e) => e.id !== txtId);
|
|
@@ -817,10 +819,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
817
819
|
}
|
|
818
820
|
}
|
|
819
821
|
if (patch.attrs) {
|
|
820
|
-
|
|
821
|
-
o.setAttribute(patch.attrs);
|
|
822
|
-
} catch {
|
|
823
|
-
}
|
|
822
|
+
safeJsx("MiniBoard.setAttribute", () => o.setAttribute(patch.attrs));
|
|
824
823
|
const id = localIdOf(o);
|
|
825
824
|
if (id) {
|
|
826
825
|
const entry = creationLogRef.current.find((e) => e.id === id);
|
|
@@ -828,19 +827,13 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
828
827
|
setHistoryTick((t) => t + 1);
|
|
829
828
|
}
|
|
830
829
|
}
|
|
831
|
-
|
|
832
|
-
boardRef.current.update();
|
|
833
|
-
} catch {
|
|
834
|
-
}
|
|
830
|
+
safeJsx("MiniBoard.board.update(mutate)", () => boardRef.current.update());
|
|
835
831
|
}, [createValueLabelFor, localIdOf, nextLocalId]);
|
|
836
832
|
const clearPreviewSegs = useCallback(() => {
|
|
837
833
|
const b = boardRef.current;
|
|
838
834
|
if (!b) return;
|
|
839
835
|
for (const s of previewSegRef.current) {
|
|
840
|
-
|
|
841
|
-
b.removeObject(s);
|
|
842
|
-
} catch {
|
|
843
|
-
}
|
|
836
|
+
safeJsx("MiniBoard.removeObject(previewSeg)", () => b.removeObject(s));
|
|
844
837
|
}
|
|
845
838
|
previewSegRef.current = [];
|
|
846
839
|
}, []);
|
|
@@ -848,17 +841,11 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
848
841
|
const b = boardRef.current;
|
|
849
842
|
if (!b) return;
|
|
850
843
|
if (previewShapeRef.current) {
|
|
851
|
-
|
|
852
|
-
b.removeObject(previewShapeRef.current);
|
|
853
|
-
} catch {
|
|
854
|
-
}
|
|
844
|
+
safeJsx("MiniBoard.removeObject(previewShape)", () => b.removeObject(previewShapeRef.current));
|
|
855
845
|
previewShapeRef.current = null;
|
|
856
846
|
}
|
|
857
847
|
if (phantomRef.current) {
|
|
858
|
-
|
|
859
|
-
b.removeObject(phantomRef.current);
|
|
860
|
-
} catch {
|
|
861
|
-
}
|
|
848
|
+
safeJsx("MiniBoard.removeObject(phantom)", () => b.removeObject(phantomRef.current));
|
|
862
849
|
phantomRef.current = null;
|
|
863
850
|
}
|
|
864
851
|
}, []);
|
|
@@ -870,7 +857,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
870
857
|
}, [clearPreviewSegs, removePhantom]);
|
|
871
858
|
const applySelectionStyle = useCallback((obj) => {
|
|
872
859
|
if (!obj || selOriginalRef.current.has(obj)) return;
|
|
873
|
-
|
|
860
|
+
safeJsx("MiniBoard.applySelectionStyle", () => {
|
|
874
861
|
const visProp = obj.visProp ?? {};
|
|
875
862
|
selOriginalRef.current.set(obj, {
|
|
876
863
|
strokeColor: visProp.strokecolor,
|
|
@@ -882,19 +869,17 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
882
869
|
} else {
|
|
883
870
|
obj.setAttribute({ strokeColor: "#06b6d4", strokeWidth: 3 });
|
|
884
871
|
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
872
|
+
});
|
|
887
873
|
}, []);
|
|
888
874
|
const restoreSelectionStyle = useCallback((obj) => {
|
|
889
875
|
const orig = selOriginalRef.current.get(obj);
|
|
890
876
|
if (!orig) return;
|
|
891
|
-
|
|
877
|
+
safeJsx("MiniBoard.restoreSelectionStyle", () => {
|
|
892
878
|
const attrs = {};
|
|
893
879
|
if (orig.strokeColor !== void 0) attrs.strokeColor = orig.strokeColor;
|
|
894
880
|
if (orig.strokeWidth !== void 0) attrs.strokeWidth = orig.strokeWidth;
|
|
895
881
|
obj.setAttribute(attrs);
|
|
896
|
-
}
|
|
897
|
-
}
|
|
882
|
+
});
|
|
898
883
|
selOriginalRef.current.delete(obj);
|
|
899
884
|
}, []);
|
|
900
885
|
const clearSelection = useCallback(() => {
|
|
@@ -903,10 +888,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
903
888
|
}
|
|
904
889
|
selectedSetRef.current.clear();
|
|
905
890
|
setSelectionTick((t) => t + 1);
|
|
906
|
-
|
|
907
|
-
boardRef.current?.update();
|
|
908
|
-
} catch {
|
|
909
|
-
}
|
|
891
|
+
safeJsx("MiniBoard.board.update(clearSelection)", () => boardRef.current?.update());
|
|
910
892
|
}, [restoreSelectionStyle]);
|
|
911
893
|
const toggleSelect = useCallback((obj, additive) => {
|
|
912
894
|
if (!obj) return;
|
|
@@ -926,10 +908,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
926
908
|
}
|
|
927
909
|
}
|
|
928
910
|
setSelectionTick((t) => t + 1);
|
|
929
|
-
|
|
930
|
-
boardRef.current?.update();
|
|
931
|
-
} catch {
|
|
932
|
-
}
|
|
911
|
+
safeJsx("MiniBoard.board.update(toggleSelect)", () => boardRef.current?.update());
|
|
933
912
|
}, [applySelectionStyle, restoreSelectionStyle]);
|
|
934
913
|
const deleteSelected = useCallback(() => {
|
|
935
914
|
const board = boardRef.current;
|
|
@@ -937,10 +916,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
937
916
|
if (selectedSetRef.current.size === 0) return;
|
|
938
917
|
for (const o of selectedSetRef.current) selOriginalRef.current.delete(o);
|
|
939
918
|
for (const o of selectedSetRef.current) {
|
|
940
|
-
|
|
941
|
-
board.removeObject(o);
|
|
942
|
-
} catch {
|
|
943
|
-
}
|
|
919
|
+
safeJsx("MiniBoard.removeObject(selected)", () => board.removeObject(o));
|
|
944
920
|
}
|
|
945
921
|
selectedSetRef.current.clear();
|
|
946
922
|
const aliveIds = /* @__PURE__ */ new Set();
|
|
@@ -1013,10 +989,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1013
989
|
const b = boardRef.current;
|
|
1014
990
|
if (!b) return;
|
|
1015
991
|
if (previewShapeRef.current) {
|
|
1016
|
-
|
|
1017
|
-
b.removeObject(previewShapeRef.current);
|
|
1018
|
-
} catch {
|
|
1019
|
-
}
|
|
992
|
+
safeJsx("MiniBoard.removeObject(refreshPreview)", () => b.removeObject(previewShapeRef.current));
|
|
1020
993
|
previewShapeRef.current = null;
|
|
1021
994
|
}
|
|
1022
995
|
const t = toolRef.current;
|
|
@@ -1135,7 +1108,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1135
1108
|
}
|
|
1136
1109
|
case "toggleLabel": {
|
|
1137
1110
|
const obj = picks[0];
|
|
1138
|
-
|
|
1111
|
+
safeJsx("MiniBoard.toggleLabel", () => {
|
|
1139
1112
|
if (obj.label) {
|
|
1140
1113
|
const visible = obj.label.visProp.visible !== false;
|
|
1141
1114
|
obj.label.setAttribute({ visible: !visible });
|
|
@@ -1144,23 +1117,21 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1144
1117
|
obj.setAttribute({ withLabel: !cur });
|
|
1145
1118
|
}
|
|
1146
1119
|
boardRef.current.update();
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1120
|
+
});
|
|
1149
1121
|
break;
|
|
1150
1122
|
}
|
|
1151
1123
|
case "toggleVisible": {
|
|
1152
1124
|
const obj = picks[0];
|
|
1153
|
-
|
|
1125
|
+
safeJsx("MiniBoard.toggleVisible", () => {
|
|
1154
1126
|
const visible = obj.visProp.visible !== false;
|
|
1155
1127
|
obj.setAttribute({ visible: !visible });
|
|
1156
1128
|
boardRef.current.update();
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1129
|
+
});
|
|
1159
1130
|
break;
|
|
1160
1131
|
}
|
|
1161
1132
|
case "delete": {
|
|
1162
1133
|
const obj = picks[0];
|
|
1163
|
-
|
|
1134
|
+
safeJsx("MiniBoard.deleteOne", () => {
|
|
1164
1135
|
boardRef.current.removeObject(obj);
|
|
1165
1136
|
const board = boardRef.current;
|
|
1166
1137
|
const aliveIds = /* @__PURE__ */ new Set();
|
|
@@ -1175,8 +1146,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1175
1146
|
if (!aliveIds.has(id)) objMapRef.current.delete(id);
|
|
1176
1147
|
}
|
|
1177
1148
|
setHistoryTick((t) => t + 1);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1149
|
+
});
|
|
1180
1150
|
break;
|
|
1181
1151
|
}
|
|
1182
1152
|
}
|
|
@@ -1211,7 +1181,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1211
1181
|
}
|
|
1212
1182
|
const stepId = nextLocalId();
|
|
1213
1183
|
const stepObj = boardRef.current.create("transform", step.params, step.attrs);
|
|
1214
|
-
|
|
1184
|
+
pushCreationLog({ id: stepId, type: "transform", args: stepLogArgs, attrs: step.attrs });
|
|
1215
1185
|
objMapRef.current.set(stepId, stepObj);
|
|
1216
1186
|
transformObjs.push(stepObj);
|
|
1217
1187
|
transformIds.push(stepId);
|
|
@@ -1225,7 +1195,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1225
1195
|
const newName = srcName ? `${srcName}'` : nextLabel();
|
|
1226
1196
|
const attrs = { name: newName, size: 3, color: "#0ea5e9", strokeColor: "#0ea5e9", fillColor: "#0ea5e9" };
|
|
1227
1197
|
const obj = boardRef.current.create("point", [src, transformParent], attrs);
|
|
1228
|
-
|
|
1198
|
+
pushCreationLog({ id, type: "point", args: [srcId ?? src, transformLogRef], attrs });
|
|
1229
1199
|
objMapRef.current.set(id, obj);
|
|
1230
1200
|
return obj;
|
|
1231
1201
|
});
|
|
@@ -1256,6 +1226,45 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1256
1226
|
}
|
|
1257
1227
|
setHistoryTick((t) => t + 1);
|
|
1258
1228
|
}, [create, flashWarn, localIdOf, nextLabel, nextLocalId]);
|
|
1229
|
+
const recreateFromLogEntry = useCallback((el) => {
|
|
1230
|
+
const board = boardRef.current;
|
|
1231
|
+
if (!board) return false;
|
|
1232
|
+
const idMap = objMapRef.current;
|
|
1233
|
+
const resolve = (a) => {
|
|
1234
|
+
if (typeof a === "string") {
|
|
1235
|
+
if (idMap.has(a)) return idMap.get(a);
|
|
1236
|
+
const m = /^(.+):border:(\d+)$/.exec(a);
|
|
1237
|
+
if (m) {
|
|
1238
|
+
const poly = idMap.get(m[1]);
|
|
1239
|
+
const idx = parseInt(m[2], 10);
|
|
1240
|
+
if (poly && Array.isArray(poly.borders) && poly.borders[idx]) {
|
|
1241
|
+
return poly.borders[idx];
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
if (Array.isArray(a)) return a.map(resolve);
|
|
1246
|
+
return a;
|
|
1247
|
+
};
|
|
1248
|
+
const resolved = el.args.map(resolve);
|
|
1249
|
+
try {
|
|
1250
|
+
if (el.type === "valueLabel") {
|
|
1251
|
+
const target = resolved[0];
|
|
1252
|
+
if (!target) return false;
|
|
1253
|
+
const txt = createValueLabelFor(target);
|
|
1254
|
+
if (!txt) return false;
|
|
1255
|
+
idMap.set(el.id, txt);
|
|
1256
|
+
valueLabelsRef.current.set(target, txt);
|
|
1257
|
+
return true;
|
|
1258
|
+
}
|
|
1259
|
+
const themedAttrs = resolveAttrColors({ ...el.attrs }, paletteFor(isDarkRef.current));
|
|
1260
|
+
const obj = board.create(el.type, resolved, themedAttrs);
|
|
1261
|
+
idMap.set(el.id, obj);
|
|
1262
|
+
return true;
|
|
1263
|
+
} catch (err) {
|
|
1264
|
+
console.warn("Recreate failed for", el.type, err);
|
|
1265
|
+
return false;
|
|
1266
|
+
}
|
|
1267
|
+
}, [createValueLabelFor]);
|
|
1259
1268
|
const undoLast = useCallback(() => {
|
|
1260
1269
|
const b = boardRef.current;
|
|
1261
1270
|
if (!b) return;
|
|
@@ -1265,21 +1274,31 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1265
1274
|
const obj = objMapRef.current.get(last.id);
|
|
1266
1275
|
objMapRef.current.delete(last.id);
|
|
1267
1276
|
if (obj) {
|
|
1268
|
-
|
|
1269
|
-
b.removeObject(obj);
|
|
1270
|
-
} catch {
|
|
1271
|
-
}
|
|
1277
|
+
safeJsx("MiniBoard.removeObject(undo)", () => b.removeObject(obj));
|
|
1272
1278
|
clearPending();
|
|
1279
|
+
redoStackRef.current.push(last);
|
|
1273
1280
|
setHistoryTick((t) => t + 1);
|
|
1274
|
-
|
|
1275
|
-
b.update();
|
|
1276
|
-
} catch {
|
|
1277
|
-
}
|
|
1281
|
+
safeJsx("MiniBoard.board.update(undo)", () => b.update());
|
|
1278
1282
|
return;
|
|
1279
1283
|
}
|
|
1280
1284
|
}
|
|
1281
1285
|
setHistoryTick((t) => t + 1);
|
|
1282
1286
|
}, [clearPending]);
|
|
1287
|
+
const redoNext = useCallback(() => {
|
|
1288
|
+
const b = boardRef.current;
|
|
1289
|
+
if (!b) return;
|
|
1290
|
+
const entry = redoStackRef.current.pop();
|
|
1291
|
+
if (!entry) {
|
|
1292
|
+
setHistoryTick((t) => t + 1);
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
const ok = recreateFromLogEntry(entry);
|
|
1296
|
+
if (ok) {
|
|
1297
|
+
creationLogRef.current.push(entry);
|
|
1298
|
+
}
|
|
1299
|
+
setHistoryTick((t) => t + 1);
|
|
1300
|
+
safeJsx("MiniBoard.board.update(redo)", () => b.update());
|
|
1301
|
+
}, [recreateFromLogEntry]);
|
|
1283
1302
|
useEffect(() => {
|
|
1284
1303
|
const onKey = (e) => {
|
|
1285
1304
|
const ae = document.activeElement;
|
|
@@ -1291,6 +1310,13 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1291
1310
|
undoLastRef.current();
|
|
1292
1311
|
return;
|
|
1293
1312
|
}
|
|
1313
|
+
if ((e.metaKey || e.ctrlKey) && (e.key.toLowerCase() === "z" && e.shiftKey || e.key.toLowerCase() === "y" && !e.shiftKey)) {
|
|
1314
|
+
if (inField) return;
|
|
1315
|
+
e.preventDefault();
|
|
1316
|
+
e.stopPropagation();
|
|
1317
|
+
redoNextRef.current();
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1294
1320
|
if (e.key === "Escape" && !inField) {
|
|
1295
1321
|
if (pendingRef.current.length > 0) {
|
|
1296
1322
|
e.preventDefault();
|
|
@@ -1337,16 +1363,14 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1337
1363
|
if (!sc) return [];
|
|
1338
1364
|
const [sx, sy] = sc;
|
|
1339
1365
|
const list = [];
|
|
1340
|
-
|
|
1366
|
+
safeJsx("MiniBoard.objectsAt.loop", () => {
|
|
1341
1367
|
const objs = b.objectsList || [];
|
|
1342
1368
|
for (const o of objs) {
|
|
1343
|
-
|
|
1369
|
+
safeJsx("MiniBoard.objectsAt.hasPoint", () => {
|
|
1344
1370
|
if (o.hasPoint && o.hasPoint(sx, sy)) list.push(o);
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1371
|
+
});
|
|
1347
1372
|
}
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1373
|
+
});
|
|
1350
1374
|
return list;
|
|
1351
1375
|
}, [screenCoordsOf]);
|
|
1352
1376
|
const findNearestPoint = useCallback((evt, tolPx = 12) => {
|
|
@@ -1356,24 +1380,23 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1356
1380
|
if (!sc) return null;
|
|
1357
1381
|
const [sx, sy] = sc;
|
|
1358
1382
|
const tol2 = tolPx * tolPx;
|
|
1359
|
-
|
|
1360
|
-
|
|
1383
|
+
const bestRef = { current: null };
|
|
1384
|
+
safeJsx("MiniBoard.findNearestPoint.loop", () => {
|
|
1361
1385
|
const objs = b.objectsList || [];
|
|
1362
1386
|
for (const o of objs) {
|
|
1363
|
-
|
|
1364
|
-
if (objKind(o) !== "point")
|
|
1387
|
+
safeJsx("MiniBoard.findNearestPoint.iter", () => {
|
|
1388
|
+
if (objKind(o) !== "point") return;
|
|
1365
1389
|
const pc = o.coords?.scrCoords;
|
|
1366
|
-
if (!pc)
|
|
1390
|
+
if (!pc) return;
|
|
1367
1391
|
const dx = pc[1] - sx;
|
|
1368
1392
|
const dy = pc[2] - sy;
|
|
1369
1393
|
const d2 = dx * dx + dy * dy;
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
}
|
|
1394
|
+
const cur = bestRef.current;
|
|
1395
|
+
if (d2 <= tol2 && (!cur || d2 < cur.d2)) bestRef.current = { obj: o, d2 };
|
|
1396
|
+
});
|
|
1373
1397
|
}
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
return best ? best.obj : null;
|
|
1398
|
+
});
|
|
1399
|
+
return bestRef.current ? bestRef.current.obj : null;
|
|
1377
1400
|
}, [screenCoordsOf]);
|
|
1378
1401
|
const promoteLabel = useCallback((o) => {
|
|
1379
1402
|
if (!o) return o;
|
|
@@ -1381,31 +1404,25 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1381
1404
|
if (t !== "text") return o;
|
|
1382
1405
|
const b = boardRef.current;
|
|
1383
1406
|
if (!b) return o;
|
|
1384
|
-
|
|
1407
|
+
const promoted = safeJsx("MiniBoard.promoteLabel", () => {
|
|
1385
1408
|
for (const c of b.objectsList || []) {
|
|
1386
1409
|
if (c.label === o) return c;
|
|
1387
1410
|
}
|
|
1388
|
-
|
|
1389
|
-
}
|
|
1390
|
-
return o;
|
|
1411
|
+
return null;
|
|
1412
|
+
}, null);
|
|
1413
|
+
return promoted ?? o;
|
|
1391
1414
|
}, []);
|
|
1392
1415
|
const pendingTransformRef = useRef(null);
|
|
1393
1416
|
const transformSubsRef = useRef(/* @__PURE__ */ new Set());
|
|
1394
1417
|
const emitTransform = useCallback((info) => {
|
|
1395
1418
|
transformSubsRef.current.forEach((cb) => {
|
|
1396
|
-
|
|
1397
|
-
cb(info);
|
|
1398
|
-
} catch {
|
|
1399
|
-
}
|
|
1419
|
+
safeJsx("MiniBoard.emitTransform.cb", () => cb(info));
|
|
1400
1420
|
});
|
|
1401
1421
|
}, []);
|
|
1402
1422
|
const selectSubsRef = useRef(/* @__PURE__ */ new Set());
|
|
1403
1423
|
const emitSelect = useCallback((snap) => {
|
|
1404
1424
|
selectSubsRef.current.forEach((cb) => {
|
|
1405
|
-
|
|
1406
|
-
cb(snap);
|
|
1407
|
-
} catch {
|
|
1408
|
-
}
|
|
1425
|
+
safeJsx("MiniBoard.emitSelect.cb", () => cb(snap));
|
|
1409
1426
|
});
|
|
1410
1427
|
}, []);
|
|
1411
1428
|
const moveDownRef = useRef(null);
|
|
@@ -1417,7 +1434,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1417
1434
|
const JXG = (await import('jsxgraph')).default;
|
|
1418
1435
|
if (cancelled || !containerRef.current) return;
|
|
1419
1436
|
jxgRef.current = JXG;
|
|
1420
|
-
|
|
1437
|
+
safeJsx("MiniBoard.applyJxgOptions", () => {
|
|
1421
1438
|
const opts = JXG.Options;
|
|
1422
1439
|
if (opts) {
|
|
1423
1440
|
opts.text = opts.text || {};
|
|
@@ -1430,8 +1447,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1430
1447
|
opts.label.strokeColor = themeLabel(isDarkRef.current);
|
|
1431
1448
|
opts.text.strokeColor = themeLabel(isDarkRef.current);
|
|
1432
1449
|
}
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1450
|
+
});
|
|
1435
1451
|
const board = JXG.JSXGraph.initBoard(containerId, {
|
|
1436
1452
|
boundingbox: initialState?.bbox ?? [-10, 10, 10, -10],
|
|
1437
1453
|
axis: false,
|
|
@@ -1452,43 +1468,20 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1452
1468
|
});
|
|
1453
1469
|
boardRef.current = board;
|
|
1454
1470
|
if (initialState && initialState.elements.length > 0) {
|
|
1455
|
-
const idMap = objMapRef.current;
|
|
1456
1471
|
for (const el of initialState.elements) {
|
|
1457
|
-
|
|
1458
|
-
try {
|
|
1459
|
-
if (el.type === "valueLabel") {
|
|
1460
|
-
const target = resolved[0];
|
|
1461
|
-
if (target) {
|
|
1462
|
-
const txt = createValueLabelFor(target);
|
|
1463
|
-
if (txt) {
|
|
1464
|
-
idMap.set(el.id, txt);
|
|
1465
|
-
valueLabelsRef.current.set(target, txt);
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
continue;
|
|
1469
|
-
}
|
|
1470
|
-
const themedAttrs = resolveAttrColors({ ...el.attrs }, paletteFor(isDarkRef.current));
|
|
1471
|
-
const obj = board.create(el.type, resolved, themedAttrs);
|
|
1472
|
-
idMap.set(el.id, obj);
|
|
1473
|
-
} catch (err) {
|
|
1474
|
-
console.warn("Replay failed for", el.type, err);
|
|
1475
|
-
}
|
|
1472
|
+
recreateFromLogEntry(el);
|
|
1476
1473
|
}
|
|
1477
1474
|
creationLogRef.current = [...initialState.elements];
|
|
1478
1475
|
labelIdxRef.current = initialState.elements.filter((e) => e.type === "point").length;
|
|
1479
1476
|
}
|
|
1480
1477
|
if (showAxisRef.current) {
|
|
1481
|
-
|
|
1478
|
+
safeJsx("MiniBoard.initAxes", () => {
|
|
1482
1479
|
axisObjsRef.current.x = board.create("axis", [[0, 0], [1, 0]], { strokeColor: themeAxis(isDarkRef.current), name: "", withLabel: false });
|
|
1483
1480
|
axisObjsRef.current.y = board.create("axis", [[0, 0], [0, 1]], { strokeColor: themeAxis(isDarkRef.current), name: "", withLabel: false });
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1481
|
+
});
|
|
1486
1482
|
}
|
|
1487
1483
|
if (showGridRef.current) {
|
|
1488
|
-
|
|
1489
|
-
board.create("grid", [], { strokeColor: themeGrid(isDarkRef.current), strokeOpacity: 1 });
|
|
1490
|
-
} catch {
|
|
1491
|
-
}
|
|
1484
|
+
safeJsx("MiniBoard.initGrid", () => board.create("grid", [], { strokeColor: themeGrid(isDarkRef.current), strokeOpacity: 1 }));
|
|
1492
1485
|
}
|
|
1493
1486
|
board.on("down", (e) => {
|
|
1494
1487
|
const ctx = {
|
|
@@ -1639,6 +1632,8 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1639
1632
|
setShowGrid: (b) => setShowGridRef.current(b),
|
|
1640
1633
|
undo: () => undoLastRef.current(),
|
|
1641
1634
|
canUndo: () => creationLogRef.current.length > 0,
|
|
1635
|
+
redo: () => redoNextRef.current(),
|
|
1636
|
+
canRedo: () => redoStackRef.current.length > 0,
|
|
1642
1637
|
subscribe: (cb) => {
|
|
1643
1638
|
subscribersRef.current.add(cb);
|
|
1644
1639
|
return () => {
|
|
@@ -1651,15 +1646,14 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1651
1646
|
const b = boardRef.current;
|
|
1652
1647
|
if (!b) return [];
|
|
1653
1648
|
const out = [];
|
|
1654
|
-
|
|
1649
|
+
safeJsx("MiniBoard.getAllPointNames", () => {
|
|
1655
1650
|
const objs = b.objectsList || [];
|
|
1656
1651
|
for (const o of objs) {
|
|
1657
1652
|
if (objKind(o) === "point" && typeof o.name === "string" && o.name) {
|
|
1658
1653
|
out.push(o.name);
|
|
1659
1654
|
}
|
|
1660
1655
|
}
|
|
1661
|
-
}
|
|
1662
|
-
}
|
|
1656
|
+
});
|
|
1663
1657
|
return out;
|
|
1664
1658
|
},
|
|
1665
1659
|
onSelect: (cb) => {
|
|
@@ -1720,10 +1714,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1720
1714
|
previewRafRef.current = null;
|
|
1721
1715
|
}
|
|
1722
1716
|
if (boardRef.current && jxgRef.current) {
|
|
1723
|
-
|
|
1724
|
-
jxgRef.current.JSXGraph.freeBoard(boardRef.current);
|
|
1725
|
-
} catch {
|
|
1726
|
-
}
|
|
1717
|
+
safeJsx("MiniBoard.freeBoard", () => jxgRef.current.JSXGraph.freeBoard(boardRef.current));
|
|
1727
1718
|
boardRef.current = null;
|
|
1728
1719
|
}
|
|
1729
1720
|
};
|
|
@@ -1731,19 +1722,13 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1731
1722
|
useEffect(() => {
|
|
1732
1723
|
const b = boardRef.current;
|
|
1733
1724
|
if (!b) return;
|
|
1734
|
-
|
|
1725
|
+
safeJsx("MiniBoard.toggleAxis", () => {
|
|
1735
1726
|
if (axisObjsRef.current.x) {
|
|
1736
|
-
|
|
1737
|
-
b.removeObject(axisObjsRef.current.x);
|
|
1738
|
-
} catch {
|
|
1739
|
-
}
|
|
1727
|
+
safeJsx("MiniBoard.removeObject(axisX)", () => b.removeObject(axisObjsRef.current.x));
|
|
1740
1728
|
axisObjsRef.current.x = void 0;
|
|
1741
1729
|
}
|
|
1742
1730
|
if (axisObjsRef.current.y) {
|
|
1743
|
-
|
|
1744
|
-
b.removeObject(axisObjsRef.current.y);
|
|
1745
|
-
} catch {
|
|
1746
|
-
}
|
|
1731
|
+
safeJsx("MiniBoard.removeObject(axisY)", () => b.removeObject(axisObjsRef.current.y));
|
|
1747
1732
|
axisObjsRef.current.y = void 0;
|
|
1748
1733
|
}
|
|
1749
1734
|
if (showAxis) {
|
|
@@ -1751,28 +1736,23 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1751
1736
|
axisObjsRef.current.y = b.create("axis", [[0, 0], [0, 1]], { strokeColor: themeAxis(isDarkRef.current), name: "", withLabel: false });
|
|
1752
1737
|
}
|
|
1753
1738
|
b.update();
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1739
|
+
});
|
|
1756
1740
|
}, [showAxis]);
|
|
1757
1741
|
useEffect(() => {
|
|
1758
1742
|
const b = boardRef.current;
|
|
1759
1743
|
if (!b) return;
|
|
1760
|
-
|
|
1744
|
+
safeJsx("MiniBoard.toggleGrid", () => {
|
|
1761
1745
|
const objs = Object.values(b.objects || {});
|
|
1762
1746
|
for (const o of objs) {
|
|
1763
1747
|
if (o && (o.elType === "grid" || o.type === "grid" || o.visProp && o.visProp.type === "grid")) {
|
|
1764
|
-
|
|
1765
|
-
b.removeObject(o);
|
|
1766
|
-
} catch {
|
|
1767
|
-
}
|
|
1748
|
+
safeJsx("MiniBoard.removeObject(grid)", () => b.removeObject(o));
|
|
1768
1749
|
}
|
|
1769
1750
|
}
|
|
1770
1751
|
if (showGrid) {
|
|
1771
1752
|
b.create("grid", [], { strokeColor: themeGrid(isDarkRef.current), strokeOpacity: 1 });
|
|
1772
1753
|
}
|
|
1773
1754
|
b.update();
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1755
|
+
});
|
|
1776
1756
|
}, [showGrid]);
|
|
1777
1757
|
const handleToolChange = useCallback((t) => {
|
|
1778
1758
|
clearPending();
|
|
@@ -1780,10 +1760,9 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1780
1760
|
setTool(t);
|
|
1781
1761
|
const b = boardRef.current;
|
|
1782
1762
|
if (b) {
|
|
1783
|
-
|
|
1763
|
+
safeJsx("MiniBoard.setPanForTool", () => {
|
|
1784
1764
|
if (b.attr?.pan) b.attr.pan.enabled = t !== "select";
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1765
|
+
});
|
|
1787
1766
|
}
|
|
1788
1767
|
}, [clearPending]);
|
|
1789
1768
|
const handleToolChangeRef = useRef(handleToolChange);
|
|
@@ -1791,10 +1770,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1791
1770
|
const subscribersRef = useRef(/* @__PURE__ */ new Set());
|
|
1792
1771
|
const notifySubscribers = useCallback(() => {
|
|
1793
1772
|
subscribersRef.current.forEach((cb) => {
|
|
1794
|
-
|
|
1795
|
-
cb();
|
|
1796
|
-
} catch {
|
|
1797
|
-
}
|
|
1773
|
+
safeJsx("MiniBoard.notifySubscriber.cb", () => cb());
|
|
1798
1774
|
});
|
|
1799
1775
|
}, []);
|
|
1800
1776
|
useEffect(() => {
|
|
@@ -1802,6 +1778,8 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
|
|
|
1802
1778
|
}, [tool, showAxis, showGrid, historyTick, notifySubscribers]);
|
|
1803
1779
|
const undoLastRef = useRef(undoLast);
|
|
1804
1780
|
undoLastRef.current = undoLast;
|
|
1781
|
+
const redoNextRef = useRef(redoNext);
|
|
1782
|
+
redoNextRef.current = redoNext;
|
|
1805
1783
|
const clearPendingRef = useRef(clearPending);
|
|
1806
1784
|
clearPendingRef.current = clearPending;
|
|
1807
1785
|
const finalizeTransformCreateRef = useRef(finalizeTransformCreate);
|
|
@@ -1885,6 +1863,12 @@ function UndoIcon() {
|
|
|
1885
1863
|
/* @__PURE__ */ jsx("path", { d: "M3.51 13a9 9 0 1 0 2.13-9.36L3 7" })
|
|
1886
1864
|
] });
|
|
1887
1865
|
}
|
|
1866
|
+
function RedoIcon() {
|
|
1867
|
+
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1868
|
+
/* @__PURE__ */ jsx("polyline", { points: "21 7 21 13 15 13" }),
|
|
1869
|
+
/* @__PURE__ */ jsx("path", { d: "M20.49 13a9 9 0 1 1-2.13-9.36L21 7" })
|
|
1870
|
+
] });
|
|
1871
|
+
}
|
|
1888
1872
|
function AxisIcon() {
|
|
1889
1873
|
return /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1890
1874
|
/* @__PURE__ */ jsx("line", { x1: "4", y1: "20", x2: "20", y2: "20" }),
|
|
@@ -1929,7 +1913,7 @@ function useToolHoverTooltip() {
|
|
|
1929
1913
|
return { hover, portalReady, showHover, hideHover };
|
|
1930
1914
|
}
|
|
1931
1915
|
function DesktopGeometryPanel(props) {
|
|
1932
|
-
const { activeTool, onToolChange, showAxis, showGrid, onShowAxisChange, onShowGridChange, onUndo, canUndo, onClose, isDark, chordGroup } = props;
|
|
1916
|
+
const { activeTool, onToolChange, showAxis, showGrid, onShowAxisChange, onShowGridChange, onUndo, canUndo, onRedo, canRedo, onClose, isDark, chordGroup } = props;
|
|
1933
1917
|
const grouped = useMemo(() => {
|
|
1934
1918
|
return TOOLS.reduce((acc, t) => {
|
|
1935
1919
|
var _a;
|
|
@@ -1978,9 +1962,23 @@ function DesktopGeometryPanel(props) {
|
|
|
1978
1962
|
disabled: !canUndo,
|
|
1979
1963
|
title: "Ho\xE0n t\xE1c (Ctrl/Cmd+Z)",
|
|
1980
1964
|
"aria-label": "Ho\xE0n t\xE1c",
|
|
1965
|
+
"data-testid": "undo-btn",
|
|
1981
1966
|
className: "ml-auto inline-flex items-center justify-center rounded p-1 text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent",
|
|
1982
1967
|
children: /* @__PURE__ */ jsx(UndoIcon, {})
|
|
1983
1968
|
}
|
|
1969
|
+
),
|
|
1970
|
+
/* @__PURE__ */ jsx(
|
|
1971
|
+
"button",
|
|
1972
|
+
{
|
|
1973
|
+
type: "button",
|
|
1974
|
+
onClick: onRedo,
|
|
1975
|
+
disabled: !canRedo,
|
|
1976
|
+
title: "L\xE0m l\u1EA1i (Ctrl/Cmd+Shift+Z)",
|
|
1977
|
+
"aria-label": "L\xE0m l\u1EA1i",
|
|
1978
|
+
"data-testid": "redo-btn",
|
|
1979
|
+
className: "inline-flex items-center justify-center rounded p-1 text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent",
|
|
1980
|
+
children: /* @__PURE__ */ jsx(RedoIcon, {})
|
|
1981
|
+
}
|
|
1984
1982
|
)
|
|
1985
1983
|
] }) }),
|
|
1986
1984
|
groupKeys.map((group) => {
|
|
@@ -2101,6 +2099,8 @@ function MobileGeometryPanel(props) {
|
|
|
2101
2099
|
onShowGridChange,
|
|
2102
2100
|
onUndo,
|
|
2103
2101
|
canUndo,
|
|
2102
|
+
onRedo,
|
|
2103
|
+
canRedo,
|
|
2104
2104
|
isDark,
|
|
2105
2105
|
drawerOpen,
|
|
2106
2106
|
onDrawerClose
|
|
@@ -2149,6 +2149,13 @@ function MobileGeometryPanel(props) {
|
|
|
2149
2149
|
icon: /* @__PURE__ */ jsx(UndoIcon, {}),
|
|
2150
2150
|
onClick: onUndo,
|
|
2151
2151
|
disabled: !canUndo
|
|
2152
|
+
},
|
|
2153
|
+
{
|
|
2154
|
+
label: "L\xE0m l\u1EA1i",
|
|
2155
|
+
title: "L\xE0m l\u1EA1i (Ctrl/Cmd+Shift+Z)",
|
|
2156
|
+
icon: /* @__PURE__ */ jsx(RedoIcon, {}),
|
|
2157
|
+
onClick: onRedo,
|
|
2158
|
+
disabled: !canRedo
|
|
2152
2159
|
}
|
|
2153
2160
|
],
|
|
2154
2161
|
groups,
|
|
@@ -2533,9 +2540,10 @@ var TransformParamPopover = ({ kind, anchor, defaultValue, onConfirm, onCancel,
|
|
|
2533
2540
|
return createPortal(node, document.body);
|
|
2534
2541
|
};
|
|
2535
2542
|
var GeometryEditorPanel = forwardRef(
|
|
2536
|
-
function GeometryEditorPanel2({ initialState, onInsert, onClose, withLeftPanel = false, onStateChange, isDark, isMobile = false, onOpenDrawer }, ref) {
|
|
2543
|
+
function GeometryEditorPanel2({ initialState, onInsert, onClose, withLeftPanel = false, onStateChange, isDark, isMobile = false, onOpenDrawer, onUndo, onRedo, canUndo, canRedo }, ref) {
|
|
2537
2544
|
const handleRef = useRef(null);
|
|
2538
2545
|
const [ready, setReady] = useState(false);
|
|
2546
|
+
const [hasContent, setHasContent] = useState(false);
|
|
2539
2547
|
const [propsPopover, setPropsPopover] = useState(null);
|
|
2540
2548
|
const [transformPopover, setTransformPopover] = useState(null);
|
|
2541
2549
|
const onStateChangeRef = useRef(onStateChange);
|
|
@@ -2544,13 +2552,16 @@ var GeometryEditorPanel = forwardRef(
|
|
|
2544
2552
|
}, [onStateChange]);
|
|
2545
2553
|
const emitState = useCallback(() => {
|
|
2546
2554
|
const h = handleRef.current;
|
|
2555
|
+
if (!h) return;
|
|
2556
|
+
setHasContent(h.getCreationLog().length > 0);
|
|
2547
2557
|
const cb = onStateChangeRef.current;
|
|
2548
|
-
if (!
|
|
2558
|
+
if (!cb) return;
|
|
2549
2559
|
cb({
|
|
2550
2560
|
tool: h.getTool(),
|
|
2551
2561
|
showAxis: h.getShowAxis(),
|
|
2552
2562
|
showGrid: h.getShowGrid(),
|
|
2553
|
-
canUndo: h.canUndo()
|
|
2563
|
+
canUndo: h.canUndo(),
|
|
2564
|
+
canRedo: h.canRedo()
|
|
2554
2565
|
});
|
|
2555
2566
|
}, []);
|
|
2556
2567
|
const handleReady = useCallback((h) => {
|
|
@@ -2592,6 +2603,7 @@ var GeometryEditorPanel = forwardRef(
|
|
|
2592
2603
|
setShowAxis: (b) => handleRef.current?.setShowAxis(b),
|
|
2593
2604
|
setShowGrid: (b) => handleRef.current?.setShowGrid(b),
|
|
2594
2605
|
undo: () => handleRef.current?.undo(),
|
|
2606
|
+
redo: () => handleRef.current?.redo(),
|
|
2595
2607
|
insert: performInsert,
|
|
2596
2608
|
hasContent: () => (handleRef.current?.getCreationLog().length ?? 0) > 0
|
|
2597
2609
|
}), [performInsert]);
|
|
@@ -2641,17 +2653,46 @@ var GeometryEditorPanel = forwardRef(
|
|
|
2641
2653
|
] }),
|
|
2642
2654
|
"D\u1EF1ng h\xECnh h\u1ECDc"
|
|
2643
2655
|
] }),
|
|
2644
|
-
isMobile && /* @__PURE__ */
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2656
|
+
isMobile && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2657
|
+
/* @__PURE__ */ jsx(
|
|
2658
|
+
"button",
|
|
2659
|
+
{
|
|
2660
|
+
type: "button",
|
|
2661
|
+
onClick: onUndo,
|
|
2662
|
+
disabled: !canUndo,
|
|
2663
|
+
"aria-label": "Ho\xE0n t\xE1c",
|
|
2664
|
+
title: "Ho\xE0n t\xE1c (Ctrl/Cmd+Z)",
|
|
2665
|
+
"data-testid": "undo-btn-mobile",
|
|
2666
|
+
className: "inline-flex h-9 w-9 items-center justify-center rounded transition hover:bg-white/15 disabled:opacity-40",
|
|
2667
|
+
children: /* @__PURE__ */ jsx(UndoIcon, {})
|
|
2668
|
+
}
|
|
2669
|
+
),
|
|
2670
|
+
/* @__PURE__ */ jsx(
|
|
2671
|
+
"button",
|
|
2672
|
+
{
|
|
2673
|
+
type: "button",
|
|
2674
|
+
onClick: onRedo,
|
|
2675
|
+
disabled: !canRedo,
|
|
2676
|
+
"aria-label": "L\xE0m l\u1EA1i",
|
|
2677
|
+
title: "L\xE0m l\u1EA1i (Ctrl/Cmd+Shift+Z)",
|
|
2678
|
+
"data-testid": "redo-btn-mobile",
|
|
2679
|
+
className: "inline-flex h-9 w-9 items-center justify-center rounded transition hover:bg-white/15 disabled:opacity-40",
|
|
2680
|
+
children: /* @__PURE__ */ jsx(RedoIcon, {})
|
|
2681
|
+
}
|
|
2682
|
+
),
|
|
2683
|
+
/* @__PURE__ */ jsx(
|
|
2684
|
+
"button",
|
|
2685
|
+
{
|
|
2686
|
+
type: "button",
|
|
2687
|
+
onClick: handleInsert,
|
|
2688
|
+
disabled: !ready || !hasContent,
|
|
2689
|
+
title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
|
|
2690
|
+
"data-testid": "geometry-insert-btn-mobile",
|
|
2691
|
+
className: "rounded bg-white/15 px-3 py-1.5 text-xs font-semibold transition hover:bg-white/25 disabled:opacity-50",
|
|
2692
|
+
children: "Ch\xE8n"
|
|
2693
|
+
}
|
|
2694
|
+
)
|
|
2695
|
+
] }),
|
|
2655
2696
|
/* @__PURE__ */ jsx("button", { onClick: onClose, "aria-label": "\u0110\xF3ng", className: "inline-flex h-9 w-9 items-center justify-center rounded transition hover:bg-white/15", children: /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2656
2697
|
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" }),
|
|
2657
2698
|
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" })
|
|
@@ -2745,7 +2786,8 @@ var GeometryEditorPanel = forwardRef(
|
|
|
2745
2786
|
"button",
|
|
2746
2787
|
{
|
|
2747
2788
|
onClick: handleInsert,
|
|
2748
|
-
disabled: !ready,
|
|
2789
|
+
disabled: !ready || !hasContent,
|
|
2790
|
+
title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
|
|
2749
2791
|
"data-testid": "geometry-insert-btn",
|
|
2750
2792
|
className: "rounded bg-emerald-600 px-3 py-1 text-xs font-medium text-white transition hover:bg-emerald-700 disabled:opacity-50",
|
|
2751
2793
|
children: "Ch\xE8n"
|
|
@@ -2762,7 +2804,8 @@ var INITIAL_GEOM_STATE = {
|
|
|
2762
2804
|
tool: "move",
|
|
2763
2805
|
showAxis: false,
|
|
2764
2806
|
showGrid: false,
|
|
2765
|
-
canUndo: false
|
|
2807
|
+
canUndo: false,
|
|
2808
|
+
canRedo: false
|
|
2766
2809
|
};
|
|
2767
2810
|
var GeometryStampHost = forwardRef(
|
|
2768
2811
|
function GeometryStampHost2({ api, editingElement, onClose, isDark }, ref) {
|
|
@@ -2828,6 +2871,8 @@ var GeometryStampHost = forwardRef(
|
|
|
2828
2871
|
onShowGridChange: (b) => panelRef.current?.setShowGrid(b),
|
|
2829
2872
|
onUndo: () => panelRef.current?.undo(),
|
|
2830
2873
|
canUndo: geomState.canUndo,
|
|
2874
|
+
onRedo: () => panelRef.current?.redo(),
|
|
2875
|
+
canRedo: geomState.canRedo,
|
|
2831
2876
|
onClose,
|
|
2832
2877
|
isDark,
|
|
2833
2878
|
isMobile,
|
|
@@ -2847,7 +2892,11 @@ var GeometryStampHost = forwardRef(
|
|
|
2847
2892
|
withLeftPanel: !isMobile,
|
|
2848
2893
|
isDark,
|
|
2849
2894
|
isMobile,
|
|
2850
|
-
onOpenDrawer: () => setDrawerOpen(true)
|
|
2895
|
+
onOpenDrawer: () => setDrawerOpen(true),
|
|
2896
|
+
onUndo: () => panelRef.current?.undo(),
|
|
2897
|
+
onRedo: () => panelRef.current?.redo(),
|
|
2898
|
+
canUndo: geomState.canUndo,
|
|
2899
|
+
canRedo: geomState.canRedo
|
|
2851
2900
|
}
|
|
2852
2901
|
)
|
|
2853
2902
|
] });
|
|
@@ -2855,5 +2904,5 @@ var GeometryStampHost = forwardRef(
|
|
|
2855
2904
|
);
|
|
2856
2905
|
|
|
2857
2906
|
export { GeometryStampHost };
|
|
2858
|
-
//# sourceMappingURL=host-
|
|
2859
|
-
//# sourceMappingURL=host-
|
|
2907
|
+
//# sourceMappingURL=host-DJETSFCG.mjs.map
|
|
2908
|
+
//# sourceMappingURL=host-DJETSFCG.mjs.map
|