@bwp-web/canvas 0.8.2 → 0.8.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/dist/context/EditCanvasContext.d.ts +19 -3
- package/dist/context/EditCanvasContext.d.ts.map +1 -1
- package/dist/context/ViewCanvasContext.d.ts +19 -4
- package/dist/context/ViewCanvasContext.d.ts.map +1 -1
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/useCanvasRef.d.ts +13 -0
- package/dist/context/useCanvasRef.d.ts.map +1 -0
- package/dist/hooks/useCanvasClick.d.ts +14 -0
- package/dist/hooks/useCanvasClick.d.ts.map +1 -1
- package/dist/hooks/useCanvasEvents.d.ts +13 -0
- package/dist/hooks/useCanvasEvents.d.ts.map +1 -1
- package/dist/hooks/useCanvasTooltip.d.ts +13 -0
- package/dist/hooks/useCanvasTooltip.d.ts.map +1 -1
- package/dist/hooks/useEditCanvas.d.ts +28 -3
- package/dist/hooks/useEditCanvas.d.ts.map +1 -1
- package/dist/hooks/useViewCanvas.d.ts +22 -0
- package/dist/hooks/useViewCanvas.d.ts.map +1 -1
- package/dist/index.cjs +341 -188
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +345 -193
- package/dist/index.js.map +1 -1
- package/dist/overlay/FixedSizeContent.d.ts +1 -1
- package/dist/overlay/ObjectOverlay.d.ts +16 -3
- package/dist/overlay/ObjectOverlay.d.ts.map +1 -1
- package/dist/overlay/OverlayBadge.d.ts +1 -1
- package/dist/overlay/OverlayContent.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -85,7 +85,7 @@ function Canvas({
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
// src/hooks/useEditCanvas.ts
|
|
88
|
-
import { useCallback, useEffect as useEffect2, useRef as useRef2, useState } from "react";
|
|
88
|
+
import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState } from "react";
|
|
89
89
|
import { Polygon as Polygon4 } from "fabric";
|
|
90
90
|
|
|
91
91
|
// src/viewport.ts
|
|
@@ -2455,6 +2455,7 @@ function useEditCanvas(options) {
|
|
|
2455
2455
|
const vertexEditCleanupRef = useRef2(null);
|
|
2456
2456
|
const keyboardCleanupRef = useRef2(null);
|
|
2457
2457
|
const historyRef = useRef2(null);
|
|
2458
|
+
const isInitialLoadRef = useRef2(false);
|
|
2458
2459
|
const optionsRef = useRef2(options);
|
|
2459
2460
|
optionsRef.current = options;
|
|
2460
2461
|
const savedSelectabilityRef = useRef2(
|
|
@@ -2467,6 +2468,11 @@ function useEditCanvas(options) {
|
|
|
2467
2468
|
const [isDirty, setIsDirty] = useState(false);
|
|
2468
2469
|
const [canUndo, setCanUndo] = useState(false);
|
|
2469
2470
|
const [canRedo, setCanRedo] = useState(false);
|
|
2471
|
+
const [objects, setObjects] = useState([]);
|
|
2472
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
2473
|
+
const [lockLightMode, setLockLightModeState] = useState(
|
|
2474
|
+
void 0
|
|
2475
|
+
);
|
|
2470
2476
|
const setMode = useCallback((setup) => {
|
|
2471
2477
|
vertexEditCleanupRef.current?.();
|
|
2472
2478
|
vertexEditCleanupRef.current = null;
|
|
@@ -2551,11 +2557,14 @@ function useEditCanvas(options) {
|
|
|
2551
2557
|
canvas.on("selection:created", (e) => setSelected(e.selected ?? []));
|
|
2552
2558
|
canvas.on("selection:updated", (e) => setSelected(e.selected ?? []));
|
|
2553
2559
|
canvas.on("selection:cleared", () => setSelected([]));
|
|
2554
|
-
if (opts?.trackChanges) {
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
canvas.on("
|
|
2560
|
+
if (opts?.trackChanges !== false) {
|
|
2561
|
+
const markDirtyIfNotLoading = () => {
|
|
2562
|
+
if (!isInitialLoadRef.current) setIsDirty(true);
|
|
2563
|
+
};
|
|
2564
|
+
canvas.on("object:added", markDirtyIfNotLoading);
|
|
2565
|
+
canvas.on("object:removed", markDirtyIfNotLoading);
|
|
2566
|
+
canvas.on("object:modified", markDirtyIfNotLoading);
|
|
2567
|
+
canvas.on("background:modified", markDirtyIfNotLoading);
|
|
2559
2568
|
}
|
|
2560
2569
|
if (opts?.history) {
|
|
2561
2570
|
const syncHistoryState = () => {
|
|
@@ -2593,8 +2602,31 @@ function useEditCanvas(options) {
|
|
|
2593
2602
|
}
|
|
2594
2603
|
}
|
|
2595
2604
|
function invokeOnReady() {
|
|
2596
|
-
const
|
|
2597
|
-
|
|
2605
|
+
const initPromise = (async () => {
|
|
2606
|
+
if (opts?.canvasData) {
|
|
2607
|
+
setIsLoading(true);
|
|
2608
|
+
isInitialLoadRef.current = true;
|
|
2609
|
+
try {
|
|
2610
|
+
const loaded = await loadCanvas(canvas, opts.canvasData, {
|
|
2611
|
+
filter: opts.filter,
|
|
2612
|
+
borderRadius: opts.borderRadius
|
|
2613
|
+
});
|
|
2614
|
+
setObjects(loaded);
|
|
2615
|
+
} finally {
|
|
2616
|
+
isInitialLoadRef.current = false;
|
|
2617
|
+
setIsLoading(false);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
})();
|
|
2621
|
+
initPromise.then(async () => {
|
|
2622
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2623
|
+
await Promise.resolve(onReadyResult);
|
|
2624
|
+
if (opts?.invertBackground !== void 0) {
|
|
2625
|
+
setBackgroundInverted(canvas, opts.invertBackground);
|
|
2626
|
+
}
|
|
2627
|
+
if (canvas.lockLightMode !== void 0) {
|
|
2628
|
+
setLockLightModeState(canvas.lockLightMode);
|
|
2629
|
+
}
|
|
2598
2630
|
if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {
|
|
2599
2631
|
fitViewportToBackground(canvas);
|
|
2600
2632
|
syncZoom(canvasRef, setZoom);
|
|
@@ -2624,37 +2656,25 @@ function useEditCanvas(options) {
|
|
|
2624
2656
|
alignmentCleanupRef.current = null;
|
|
2625
2657
|
}
|
|
2626
2658
|
}, [options?.enableAlignment]);
|
|
2659
|
+
useEffect2(() => {
|
|
2660
|
+
const canvas = canvasRef.current;
|
|
2661
|
+
if (!canvas || options?.invertBackground === void 0) return;
|
|
2662
|
+
setBackgroundInverted(canvas, options.invertBackground);
|
|
2663
|
+
}, [options?.invertBackground]);
|
|
2664
|
+
const setLockLightMode = useCallback((value) => {
|
|
2665
|
+
const canvas = canvasRef.current;
|
|
2666
|
+
if (canvas) {
|
|
2667
|
+
canvas.lockLightMode = value;
|
|
2668
|
+
}
|
|
2669
|
+
setLockLightModeState(value);
|
|
2670
|
+
}, []);
|
|
2627
2671
|
const setViewportMode = useCallback((mode) => {
|
|
2628
2672
|
viewportRef.current?.setMode(mode);
|
|
2629
2673
|
setViewportModeState(mode);
|
|
2630
2674
|
}, []);
|
|
2631
2675
|
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2632
|
-
const
|
|
2633
|
-
|
|
2634
|
-
const canvas = canvasRef.current;
|
|
2635
|
-
if (!canvas) throw new Error("Canvas not ready");
|
|
2636
|
-
const opts = optionsRef.current;
|
|
2637
|
-
const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
|
|
2638
|
-
const img = await setBackgroundImage(canvas, url, resizeOpts);
|
|
2639
|
-
if (opts?.autoFitToBackground !== false) {
|
|
2640
|
-
fitViewportToBackground(canvas);
|
|
2641
|
-
syncZoom(canvasRef, setZoom);
|
|
2642
|
-
}
|
|
2643
|
-
return img;
|
|
2644
|
-
},
|
|
2645
|
-
[]
|
|
2646
|
-
);
|
|
2647
|
-
return {
|
|
2648
|
-
/** Pass this to `<Canvas onReady={...} />` */
|
|
2649
|
-
onReady,
|
|
2650
|
-
/** Ref to the underlying Fabric canvas instance. */
|
|
2651
|
-
canvasRef,
|
|
2652
|
-
/** Current zoom level (reactive). */
|
|
2653
|
-
zoom,
|
|
2654
|
-
/** Currently selected objects (reactive). */
|
|
2655
|
-
selected,
|
|
2656
|
-
/** Viewport controls. */
|
|
2657
|
-
viewport: {
|
|
2676
|
+
const viewport = useMemo2(
|
|
2677
|
+
() => ({
|
|
2658
2678
|
/** Current viewport mode (reactive). */
|
|
2659
2679
|
mode: viewportMode,
|
|
2660
2680
|
/** Switch between 'select' and 'pan' viewport modes. */
|
|
@@ -2669,65 +2689,129 @@ function useEditCanvas(options) {
|
|
|
2669
2689
|
panToObject,
|
|
2670
2690
|
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2671
2691
|
zoomToFit
|
|
2692
|
+
}),
|
|
2693
|
+
[
|
|
2694
|
+
viewportMode,
|
|
2695
|
+
setViewportMode,
|
|
2696
|
+
resetViewport2,
|
|
2697
|
+
zoomIn,
|
|
2698
|
+
zoomOut,
|
|
2699
|
+
panToObject,
|
|
2700
|
+
zoomToFit
|
|
2701
|
+
]
|
|
2702
|
+
);
|
|
2703
|
+
const resetDirty = useCallback(() => setIsDirty(false), []);
|
|
2704
|
+
const markDirty = useCallback(() => setIsDirty(true), []);
|
|
2705
|
+
const undo = useCallback(async () => {
|
|
2706
|
+
const h = historyRef.current;
|
|
2707
|
+
if (!h) return;
|
|
2708
|
+
await h.undo();
|
|
2709
|
+
setCanUndo(h.canUndo());
|
|
2710
|
+
setCanRedo(h.canRedo());
|
|
2711
|
+
}, []);
|
|
2712
|
+
const redo = useCallback(async () => {
|
|
2713
|
+
const h = historyRef.current;
|
|
2714
|
+
if (!h) return;
|
|
2715
|
+
await h.redo();
|
|
2716
|
+
setCanUndo(h.canUndo());
|
|
2717
|
+
setCanRedo(h.canRedo());
|
|
2718
|
+
}, []);
|
|
2719
|
+
const setBackground = useCallback(
|
|
2720
|
+
async (url, bgOpts) => {
|
|
2721
|
+
const canvas = canvasRef.current;
|
|
2722
|
+
if (!canvas) throw new Error("Canvas not ready");
|
|
2723
|
+
const opts = optionsRef.current;
|
|
2724
|
+
const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
|
|
2725
|
+
const img = await setBackgroundImage(canvas, url, resizeOpts);
|
|
2726
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2727
|
+
fitViewportToBackground(canvas);
|
|
2728
|
+
syncZoom(canvasRef, setZoom);
|
|
2729
|
+
}
|
|
2730
|
+
return img;
|
|
2672
2731
|
},
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2732
|
+
[]
|
|
2733
|
+
);
|
|
2734
|
+
return useMemo2(
|
|
2735
|
+
() => ({
|
|
2736
|
+
/** Pass this to `<Canvas onReady={...} />` */
|
|
2737
|
+
onReady,
|
|
2738
|
+
/** Ref to the underlying Fabric canvas instance. */
|
|
2739
|
+
canvasRef,
|
|
2740
|
+
/** Current zoom level (reactive). */
|
|
2741
|
+
zoom,
|
|
2742
|
+
/** Loaded objects (reactive). Populated when `canvasData` is provided. */
|
|
2743
|
+
objects,
|
|
2744
|
+
/** Whether canvas data is currently being loaded. */
|
|
2745
|
+
isLoading,
|
|
2746
|
+
/** Currently selected objects (reactive). */
|
|
2747
|
+
selected,
|
|
2748
|
+
/** Viewport controls. */
|
|
2749
|
+
viewport,
|
|
2750
|
+
/** Whether vertex edit mode is currently active (reactive). */
|
|
2751
|
+
isEditingVertices,
|
|
2752
|
+
/**
|
|
2753
|
+
* Activate an interaction mode or return to select mode.
|
|
2754
|
+
*
|
|
2755
|
+
* Pass a setup function to activate a creation mode:
|
|
2756
|
+
* ```ts
|
|
2757
|
+
* canvas.setMode((c, viewport) =>
|
|
2758
|
+
* enableClickToCreate(c, factory, { viewport })
|
|
2759
|
+
* );
|
|
2760
|
+
* ```
|
|
2761
|
+
*
|
|
2762
|
+
* Pass `null` to deactivate and return to select mode:
|
|
2763
|
+
* ```ts
|
|
2764
|
+
* canvas.setMode(null);
|
|
2765
|
+
* ```
|
|
2766
|
+
*/
|
|
2767
|
+
setMode,
|
|
2768
|
+
/**
|
|
2769
|
+
* Set a background image from a URL. Automatically resizes if the image
|
|
2770
|
+
* exceeds the configured limits (opt out via `backgroundResize: false`),
|
|
2771
|
+
* and fits the viewport after setting if `autoFitToBackground` is enabled.
|
|
2772
|
+
*
|
|
2773
|
+
* Pass `{ preserveContrast: true }` to keep the current background contrast
|
|
2774
|
+
* when replacing the image.
|
|
2775
|
+
*/
|
|
2776
|
+
setBackground,
|
|
2777
|
+
/** Whether the canvas has been modified since the last `resetDirty()` call. Enabled by default (disable via `trackChanges: false`). */
|
|
2778
|
+
isDirty,
|
|
2779
|
+
/** Reset the dirty flag (e.g., after a successful save). */
|
|
2780
|
+
resetDirty,
|
|
2781
|
+
/** Manually mark the canvas as dirty (e.g., after a custom operation not tracked automatically). */
|
|
2782
|
+
markDirty,
|
|
2783
|
+
/** Undo the last change. Requires `history: true`. */
|
|
2784
|
+
undo,
|
|
2785
|
+
/** Redo a previously undone change. Requires `history: true`. */
|
|
2786
|
+
redo,
|
|
2787
|
+
/** Whether an undo operation is available (reactive). Requires `history: true`. */
|
|
2788
|
+
canUndo,
|
|
2789
|
+
/** Whether a redo operation is available (reactive). Requires `history: true`. */
|
|
2790
|
+
canRedo,
|
|
2791
|
+
/** Whether the canvas is locked to light mode. Read from loaded canvas data. */
|
|
2792
|
+
lockLightMode,
|
|
2793
|
+
/** Update lockLightMode on both the canvas instance and React state. */
|
|
2794
|
+
setLockLightMode
|
|
2795
|
+
}),
|
|
2796
|
+
// Only reactive state in deps — refs and stable callbacks are omitted
|
|
2797
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2798
|
+
[
|
|
2799
|
+
zoom,
|
|
2800
|
+
objects,
|
|
2801
|
+
isLoading,
|
|
2802
|
+
selected,
|
|
2803
|
+
viewport,
|
|
2804
|
+
isEditingVertices,
|
|
2805
|
+
isDirty,
|
|
2806
|
+
canUndo,
|
|
2807
|
+
canRedo,
|
|
2808
|
+
lockLightMode
|
|
2809
|
+
]
|
|
2810
|
+
);
|
|
2727
2811
|
}
|
|
2728
2812
|
|
|
2729
2813
|
// src/hooks/useViewCanvas.ts
|
|
2730
|
-
import { useCallback as useCallback2, useRef as useRef3, useState as useState2 } from "react";
|
|
2814
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
|
|
2731
2815
|
function lockCanvas(canvas) {
|
|
2732
2816
|
canvas.selection = false;
|
|
2733
2817
|
canvas.forEachObject((obj) => {
|
|
@@ -2740,6 +2824,8 @@ function useViewCanvas(options) {
|
|
|
2740
2824
|
const optionsRef = useRef3(options);
|
|
2741
2825
|
optionsRef.current = options;
|
|
2742
2826
|
const [zoom, setZoom] = useState2(1);
|
|
2827
|
+
const [objects, setObjects] = useState2([]);
|
|
2828
|
+
const [isLoading, setIsLoading] = useState2(false);
|
|
2743
2829
|
const onReady = useCallback2(
|
|
2744
2830
|
(canvas) => {
|
|
2745
2831
|
canvasRef.current = canvas;
|
|
@@ -2766,20 +2852,57 @@ function useViewCanvas(options) {
|
|
|
2766
2852
|
canvas.on("mouse:wheel", () => {
|
|
2767
2853
|
setZoom(canvas.getZoom());
|
|
2768
2854
|
});
|
|
2769
|
-
const
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2855
|
+
const initPromise = (async () => {
|
|
2856
|
+
if (opts?.canvasData) {
|
|
2857
|
+
setIsLoading(true);
|
|
2858
|
+
try {
|
|
2859
|
+
const loaded = await loadCanvas(canvas, opts.canvasData, {
|
|
2860
|
+
filter: opts.filter,
|
|
2861
|
+
borderRadius: opts.borderRadius
|
|
2862
|
+
});
|
|
2863
|
+
lockCanvas(canvas);
|
|
2864
|
+
setObjects(loaded);
|
|
2865
|
+
} finally {
|
|
2866
|
+
setIsLoading(false);
|
|
2775
2867
|
}
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2868
|
+
}
|
|
2869
|
+
})();
|
|
2870
|
+
initPromise.then(async () => {
|
|
2871
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2872
|
+
await Promise.resolve(onReadyResult);
|
|
2873
|
+
if (opts?.invertBackground !== void 0) {
|
|
2874
|
+
setBackgroundInverted(canvas, opts.invertBackground);
|
|
2875
|
+
}
|
|
2876
|
+
if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {
|
|
2877
|
+
fitViewportToBackground(canvas);
|
|
2878
|
+
syncZoom(canvasRef, setZoom);
|
|
2879
|
+
}
|
|
2880
|
+
});
|
|
2778
2881
|
},
|
|
2779
2882
|
// onReady and panAndZoom are intentionally excluded — we only initialize once
|
|
2780
2883
|
[]
|
|
2781
2884
|
);
|
|
2885
|
+
useEffect3(() => {
|
|
2886
|
+
const canvas = canvasRef.current;
|
|
2887
|
+
if (!canvas || options?.invertBackground === void 0) return;
|
|
2888
|
+
setBackgroundInverted(canvas, options.invertBackground);
|
|
2889
|
+
}, [options?.invertBackground]);
|
|
2782
2890
|
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2891
|
+
const viewport = useMemo3(
|
|
2892
|
+
() => ({
|
|
2893
|
+
/** Reset viewport to default (no pan, zoom = 1), or fit to background if one is set. */
|
|
2894
|
+
reset: resetViewport2,
|
|
2895
|
+
/** Zoom in toward the canvas center. Default step: 0.2. */
|
|
2896
|
+
zoomIn,
|
|
2897
|
+
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2898
|
+
zoomOut,
|
|
2899
|
+
/** Pan the viewport to center on a specific object. */
|
|
2900
|
+
panToObject,
|
|
2901
|
+
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2902
|
+
zoomToFit
|
|
2903
|
+
}),
|
|
2904
|
+
[resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit]
|
|
2905
|
+
);
|
|
2783
2906
|
const findObject = (id) => {
|
|
2784
2907
|
const c = canvasRef.current;
|
|
2785
2908
|
if (!c) return void 0;
|
|
@@ -2795,9 +2918,9 @@ function useViewCanvas(options) {
|
|
|
2795
2918
|
(styles) => {
|
|
2796
2919
|
const c = canvasRef.current;
|
|
2797
2920
|
if (!c) return;
|
|
2798
|
-
const
|
|
2921
|
+
const objects2 = c.getObjects();
|
|
2799
2922
|
const idMap = /* @__PURE__ */ new Map();
|
|
2800
|
-
for (const obj of
|
|
2923
|
+
for (const obj of objects2) {
|
|
2801
2924
|
if (obj.data?.id) idMap.set(obj.data.id, obj);
|
|
2802
2925
|
}
|
|
2803
2926
|
let updated = false;
|
|
@@ -2827,42 +2950,101 @@ function useViewCanvas(options) {
|
|
|
2827
2950
|
},
|
|
2828
2951
|
[]
|
|
2829
2952
|
);
|
|
2830
|
-
return
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
setObjectStyleByType
|
|
2856
|
-
};
|
|
2953
|
+
return useMemo3(
|
|
2954
|
+
() => ({
|
|
2955
|
+
/** Pass this to `<Canvas onReady={...} />` */
|
|
2956
|
+
onReady,
|
|
2957
|
+
/** Ref to the underlying Fabric canvas instance. */
|
|
2958
|
+
canvasRef,
|
|
2959
|
+
/** Current zoom level (reactive). */
|
|
2960
|
+
zoom,
|
|
2961
|
+
/** Loaded objects (reactive). Populated when `canvasData` is provided. */
|
|
2962
|
+
objects,
|
|
2963
|
+
/** Whether canvas data is currently being loaded. */
|
|
2964
|
+
isLoading,
|
|
2965
|
+
/** Viewport controls. */
|
|
2966
|
+
viewport,
|
|
2967
|
+
/** Update a single object's visual style by its `data.id`. */
|
|
2968
|
+
setObjectStyle,
|
|
2969
|
+
/** Batch-update multiple objects' visual styles in one render. Keyed by `data.id`. */
|
|
2970
|
+
setObjectStyles,
|
|
2971
|
+
/** Apply a visual style to all objects whose `data.type` matches. */
|
|
2972
|
+
setObjectStyleByType
|
|
2973
|
+
}),
|
|
2974
|
+
// Only reactive state in deps — refs and stable callbacks are omitted
|
|
2975
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2976
|
+
[zoom, objects, isLoading, viewport]
|
|
2977
|
+
);
|
|
2857
2978
|
}
|
|
2858
2979
|
|
|
2859
2980
|
// src/hooks/useCanvasEvents.ts
|
|
2860
|
-
import { useEffect as
|
|
2861
|
-
|
|
2981
|
+
import { useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
2982
|
+
|
|
2983
|
+
// src/context/ViewCanvasContext.tsx
|
|
2984
|
+
import { createContext, useContext } from "react";
|
|
2985
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
2986
|
+
var ViewCanvasContext = createContext(null);
|
|
2987
|
+
function ViewCanvasProvider({
|
|
2988
|
+
options,
|
|
2989
|
+
children
|
|
2990
|
+
}) {
|
|
2991
|
+
const canvas = useViewCanvas(options);
|
|
2992
|
+
return /* @__PURE__ */ jsx2(ViewCanvasContext.Provider, { value: canvas, children });
|
|
2993
|
+
}
|
|
2994
|
+
function useViewCanvasContext() {
|
|
2995
|
+
const ctx = useContext(ViewCanvasContext);
|
|
2996
|
+
if (ctx === null) {
|
|
2997
|
+
throw new Error(
|
|
2998
|
+
"useViewCanvasContext must be used within a <ViewCanvasProvider>"
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
3001
|
+
return ctx;
|
|
3002
|
+
}
|
|
3003
|
+
function useViewCanvasContextSafe() {
|
|
3004
|
+
return useContext(ViewCanvasContext);
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
// src/context/EditCanvasContext.tsx
|
|
3008
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
3009
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
3010
|
+
var EditCanvasContext = createContext2(null);
|
|
3011
|
+
function EditCanvasProvider({
|
|
3012
|
+
options,
|
|
3013
|
+
children
|
|
3014
|
+
}) {
|
|
3015
|
+
const canvas = useEditCanvas(options);
|
|
3016
|
+
return /* @__PURE__ */ jsx3(EditCanvasContext.Provider, { value: canvas, children });
|
|
3017
|
+
}
|
|
3018
|
+
function useEditCanvasContext() {
|
|
3019
|
+
const ctx = useContext2(EditCanvasContext);
|
|
3020
|
+
if (ctx === null) {
|
|
3021
|
+
throw new Error(
|
|
3022
|
+
"useEditCanvasContext must be used within an <EditCanvasProvider>"
|
|
3023
|
+
);
|
|
3024
|
+
}
|
|
3025
|
+
return ctx;
|
|
3026
|
+
}
|
|
3027
|
+
function useEditCanvasContextSafe() {
|
|
3028
|
+
return useContext2(EditCanvasContext);
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
// src/context/useCanvasRef.ts
|
|
3032
|
+
function useCanvasRef() {
|
|
3033
|
+
const viewCtx = useViewCanvasContextSafe();
|
|
3034
|
+
const editCtx = useEditCanvasContextSafe();
|
|
3035
|
+
return viewCtx?.canvasRef ?? editCtx?.canvasRef ?? null;
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
// src/hooks/useCanvasEvents.ts
|
|
3039
|
+
function useCanvasEvents(canvasRefOrEvents, maybeEvents) {
|
|
3040
|
+
const isContextOverload = maybeEvents === void 0;
|
|
3041
|
+
const contextCanvasRef = useCanvasRef();
|
|
3042
|
+
const resolvedCanvasRef = isContextOverload ? contextCanvasRef : canvasRefOrEvents;
|
|
3043
|
+
const events = isContextOverload ? canvasRefOrEvents : maybeEvents;
|
|
2862
3044
|
const eventsRef = useRef4(events);
|
|
2863
3045
|
eventsRef.current = events;
|
|
2864
|
-
|
|
2865
|
-
const canvas =
|
|
3046
|
+
useEffect4(() => {
|
|
3047
|
+
const canvas = resolvedCanvasRef?.current;
|
|
2866
3048
|
if (!canvas) return;
|
|
2867
3049
|
const wrappers = /* @__PURE__ */ new Map();
|
|
2868
3050
|
for (const key of Object.keys(eventsRef.current)) {
|
|
@@ -2878,12 +3060,16 @@ function useCanvasEvents(canvasRef, events) {
|
|
|
2878
3060
|
canvas.off(name, handler);
|
|
2879
3061
|
});
|
|
2880
3062
|
};
|
|
2881
|
-
}, [
|
|
3063
|
+
}, [resolvedCanvasRef]);
|
|
2882
3064
|
}
|
|
2883
3065
|
|
|
2884
3066
|
// src/hooks/useCanvasTooltip.ts
|
|
2885
|
-
import { useEffect as
|
|
2886
|
-
function useCanvasTooltip(
|
|
3067
|
+
import { useEffect as useEffect5, useRef as useRef5, useState as useState3 } from "react";
|
|
3068
|
+
function useCanvasTooltip(canvasRefOrOptions, maybeOptions) {
|
|
3069
|
+
const isContextOverload = maybeOptions === void 0;
|
|
3070
|
+
const contextCanvasRef = useCanvasRef();
|
|
3071
|
+
const resolvedCanvasRef = isContextOverload ? contextCanvasRef : canvasRefOrOptions;
|
|
3072
|
+
const options = isContextOverload ? canvasRefOrOptions : maybeOptions;
|
|
2887
3073
|
const [state, setState] = useState3({
|
|
2888
3074
|
visible: false,
|
|
2889
3075
|
content: null,
|
|
@@ -2892,8 +3078,8 @@ function useCanvasTooltip(canvasRef, options) {
|
|
|
2892
3078
|
const hoveredObjectRef = useRef5(null);
|
|
2893
3079
|
const optionsRef = useRef5(options);
|
|
2894
3080
|
optionsRef.current = options;
|
|
2895
|
-
|
|
2896
|
-
const canvas =
|
|
3081
|
+
useEffect5(() => {
|
|
3082
|
+
const canvas = resolvedCanvasRef?.current;
|
|
2897
3083
|
if (!canvas) return;
|
|
2898
3084
|
function calculatePosition(target) {
|
|
2899
3085
|
const bounds = target.getBoundingRect();
|
|
@@ -2941,19 +3127,24 @@ function useCanvasTooltip(canvasRef, options) {
|
|
|
2941
3127
|
canvas.off("after:render", updatePosition);
|
|
2942
3128
|
canvas.off("mouse:wheel", updatePosition);
|
|
2943
3129
|
};
|
|
2944
|
-
}, [
|
|
3130
|
+
}, [resolvedCanvasRef]);
|
|
2945
3131
|
return state;
|
|
2946
3132
|
}
|
|
2947
3133
|
|
|
2948
3134
|
// src/hooks/useCanvasClick.ts
|
|
2949
|
-
import { useEffect as
|
|
2950
|
-
function useCanvasClick(
|
|
3135
|
+
import { useEffect as useEffect6, useRef as useRef6 } from "react";
|
|
3136
|
+
function useCanvasClick(canvasRefOrOnClick, onClickOrOptions, maybeOptions) {
|
|
3137
|
+
const isContextOverload = typeof canvasRefOrOnClick === "function";
|
|
3138
|
+
const contextCanvasRef = useCanvasRef();
|
|
3139
|
+
const resolvedCanvasRef = isContextOverload ? contextCanvasRef : canvasRefOrOnClick;
|
|
3140
|
+
const onClick = isContextOverload ? canvasRefOrOnClick : onClickOrOptions;
|
|
3141
|
+
const options = isContextOverload ? onClickOrOptions : maybeOptions;
|
|
2951
3142
|
const onClickRef = useRef6(onClick);
|
|
2952
3143
|
onClickRef.current = onClick;
|
|
2953
3144
|
const optionsRef = useRef6(options);
|
|
2954
3145
|
optionsRef.current = options;
|
|
2955
|
-
|
|
2956
|
-
const canvas =
|
|
3146
|
+
useEffect6(() => {
|
|
3147
|
+
const canvas = resolvedCanvasRef?.current;
|
|
2957
3148
|
if (!canvas) return;
|
|
2958
3149
|
let mouseDown = null;
|
|
2959
3150
|
let isPanning = false;
|
|
@@ -2993,66 +3184,26 @@ function useCanvasClick(canvasRef, onClick, options) {
|
|
|
2993
3184
|
canvas.off("mouse:move", handleMouseMove);
|
|
2994
3185
|
canvas.off("mouse:up", handleMouseUp);
|
|
2995
3186
|
};
|
|
2996
|
-
}, [
|
|
2997
|
-
}
|
|
2998
|
-
|
|
2999
|
-
// src/context/EditCanvasContext.tsx
|
|
3000
|
-
import { createContext, useContext } from "react";
|
|
3001
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
3002
|
-
var EditCanvasContext = createContext(null);
|
|
3003
|
-
function EditCanvasProvider({
|
|
3004
|
-
options,
|
|
3005
|
-
children
|
|
3006
|
-
}) {
|
|
3007
|
-
const canvas = useEditCanvas(options);
|
|
3008
|
-
return /* @__PURE__ */ jsx2(EditCanvasContext.Provider, { value: canvas, children });
|
|
3009
|
-
}
|
|
3010
|
-
function useEditCanvasContext() {
|
|
3011
|
-
const ctx = useContext(EditCanvasContext);
|
|
3012
|
-
if (ctx === null) {
|
|
3013
|
-
throw new Error(
|
|
3014
|
-
"useEditCanvasContext must be used within an <EditCanvasProvider>"
|
|
3015
|
-
);
|
|
3016
|
-
}
|
|
3017
|
-
return ctx;
|
|
3018
|
-
}
|
|
3019
|
-
|
|
3020
|
-
// src/context/ViewCanvasContext.tsx
|
|
3021
|
-
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
3022
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
3023
|
-
var ViewCanvasContext = createContext2(null);
|
|
3024
|
-
function ViewCanvasProvider({
|
|
3025
|
-
options,
|
|
3026
|
-
children
|
|
3027
|
-
}) {
|
|
3028
|
-
const canvas = useViewCanvas(options);
|
|
3029
|
-
return /* @__PURE__ */ jsx3(ViewCanvasContext.Provider, { value: canvas, children });
|
|
3030
|
-
}
|
|
3031
|
-
function useViewCanvasContext() {
|
|
3032
|
-
const ctx = useContext2(ViewCanvasContext);
|
|
3033
|
-
if (ctx === null) {
|
|
3034
|
-
throw new Error(
|
|
3035
|
-
"useViewCanvasContext must be used within a <ViewCanvasProvider>"
|
|
3036
|
-
);
|
|
3037
|
-
}
|
|
3038
|
-
return ctx;
|
|
3187
|
+
}, [resolvedCanvasRef]);
|
|
3039
3188
|
}
|
|
3040
3189
|
|
|
3041
3190
|
// src/overlay/ObjectOverlay.tsx
|
|
3042
|
-
import { useEffect as
|
|
3191
|
+
import { useEffect as useEffect7, useRef as useRef7 } from "react";
|
|
3043
3192
|
import { Stack } from "@mui/material";
|
|
3044
3193
|
import { util as util4 } from "fabric";
|
|
3045
3194
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
3046
3195
|
function ObjectOverlay({
|
|
3047
|
-
canvasRef,
|
|
3196
|
+
canvasRef: canvasRefProp,
|
|
3048
3197
|
object,
|
|
3049
3198
|
sx,
|
|
3050
3199
|
children,
|
|
3051
3200
|
...rest
|
|
3052
3201
|
}) {
|
|
3202
|
+
const contextCanvasRef = useCanvasRef();
|
|
3203
|
+
const canvasRef = canvasRefProp ?? contextCanvasRef;
|
|
3053
3204
|
const stackRef = useRef7(null);
|
|
3054
|
-
|
|
3055
|
-
const canvas = canvasRef
|
|
3205
|
+
useEffect7(() => {
|
|
3206
|
+
const canvas = canvasRef?.current;
|
|
3056
3207
|
if (!canvas || !object) return;
|
|
3057
3208
|
function update() {
|
|
3058
3209
|
const el = stackRef.current;
|
|
@@ -3106,7 +3257,7 @@ function ObjectOverlay({
|
|
|
3106
3257
|
|
|
3107
3258
|
// src/overlay/OverlayContent.tsx
|
|
3108
3259
|
import { Stack as Stack2 } from "@mui/material";
|
|
3109
|
-
import { useEffect as
|
|
3260
|
+
import { useEffect as useEffect8, useRef as useRef8 } from "react";
|
|
3110
3261
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
3111
3262
|
function OverlayContent({
|
|
3112
3263
|
children,
|
|
@@ -3117,7 +3268,7 @@ function OverlayContent({
|
|
|
3117
3268
|
}) {
|
|
3118
3269
|
const outerRef = useRef8(null);
|
|
3119
3270
|
const innerRef = useRef8(null);
|
|
3120
|
-
|
|
3271
|
+
useEffect8(() => {
|
|
3121
3272
|
const outer = outerRef.current;
|
|
3122
3273
|
const inner = innerRef.current;
|
|
3123
3274
|
if (!outer || !inner) return;
|
|
@@ -3178,7 +3329,7 @@ function OverlayContent({
|
|
|
3178
3329
|
|
|
3179
3330
|
// src/overlay/FixedSizeContent.tsx
|
|
3180
3331
|
import { Stack as Stack3 } from "@mui/material";
|
|
3181
|
-
import { useEffect as
|
|
3332
|
+
import { useEffect as useEffect9, useRef as useRef9 } from "react";
|
|
3182
3333
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
3183
3334
|
function FixedSizeContent({
|
|
3184
3335
|
children,
|
|
@@ -3189,7 +3340,7 @@ function FixedSizeContent({
|
|
|
3189
3340
|
}) {
|
|
3190
3341
|
const ref = useRef9(null);
|
|
3191
3342
|
const totalContentHeightRef = useRef9(0);
|
|
3192
|
-
|
|
3343
|
+
useEffect9(() => {
|
|
3193
3344
|
const el = ref.current;
|
|
3194
3345
|
if (!el) return;
|
|
3195
3346
|
let clipAncestor = el.parentElement;
|
|
@@ -3252,7 +3403,7 @@ function FixedSizeContent({
|
|
|
3252
3403
|
|
|
3253
3404
|
// src/overlay/OverlayBadge.tsx
|
|
3254
3405
|
import { Stack as Stack4 } from "@mui/material";
|
|
3255
|
-
import { useEffect as
|
|
3406
|
+
import { useEffect as useEffect10, useRef as useRef10 } from "react";
|
|
3256
3407
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
3257
3408
|
function toPx(v) {
|
|
3258
3409
|
if (v === void 0) return void 0;
|
|
@@ -3297,7 +3448,7 @@ function OverlayBadge({
|
|
|
3297
3448
|
}) {
|
|
3298
3449
|
const ref = useRef10(null);
|
|
3299
3450
|
const baseSize = useRef10(null);
|
|
3300
|
-
|
|
3451
|
+
useEffect10(() => {
|
|
3301
3452
|
const el = ref.current;
|
|
3302
3453
|
if (!el) return;
|
|
3303
3454
|
const ancestor = el.parentElement;
|
|
@@ -3439,6 +3590,7 @@ export {
|
|
|
3439
3590
|
snapCursorPoint,
|
|
3440
3591
|
useCanvasClick,
|
|
3441
3592
|
useCanvasEvents,
|
|
3593
|
+
useCanvasRef,
|
|
3442
3594
|
useCanvasTooltip,
|
|
3443
3595
|
useEditCanvas,
|
|
3444
3596
|
useEditCanvasContext,
|