@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.cjs
CHANGED
|
@@ -79,6 +79,7 @@ __export(index_exports, {
|
|
|
79
79
|
snapCursorPoint: () => snapCursorPoint,
|
|
80
80
|
useCanvasClick: () => useCanvasClick,
|
|
81
81
|
useCanvasEvents: () => useCanvasEvents,
|
|
82
|
+
useCanvasRef: () => useCanvasRef,
|
|
82
83
|
useCanvasTooltip: () => useCanvasTooltip,
|
|
83
84
|
useEditCanvas: () => useEditCanvas,
|
|
84
85
|
useEditCanvasContext: () => useEditCanvasContext,
|
|
@@ -2528,6 +2529,7 @@ function useEditCanvas(options) {
|
|
|
2528
2529
|
const vertexEditCleanupRef = (0, import_react3.useRef)(null);
|
|
2529
2530
|
const keyboardCleanupRef = (0, import_react3.useRef)(null);
|
|
2530
2531
|
const historyRef = (0, import_react3.useRef)(null);
|
|
2532
|
+
const isInitialLoadRef = (0, import_react3.useRef)(false);
|
|
2531
2533
|
const optionsRef = (0, import_react3.useRef)(options);
|
|
2532
2534
|
optionsRef.current = options;
|
|
2533
2535
|
const savedSelectabilityRef = (0, import_react3.useRef)(
|
|
@@ -2540,6 +2542,11 @@ function useEditCanvas(options) {
|
|
|
2540
2542
|
const [isDirty, setIsDirty] = (0, import_react3.useState)(false);
|
|
2541
2543
|
const [canUndo, setCanUndo] = (0, import_react3.useState)(false);
|
|
2542
2544
|
const [canRedo, setCanRedo] = (0, import_react3.useState)(false);
|
|
2545
|
+
const [objects, setObjects] = (0, import_react3.useState)([]);
|
|
2546
|
+
const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
|
|
2547
|
+
const [lockLightMode, setLockLightModeState] = (0, import_react3.useState)(
|
|
2548
|
+
void 0
|
|
2549
|
+
);
|
|
2543
2550
|
const setMode = (0, import_react3.useCallback)((setup) => {
|
|
2544
2551
|
vertexEditCleanupRef.current?.();
|
|
2545
2552
|
vertexEditCleanupRef.current = null;
|
|
@@ -2624,11 +2631,14 @@ function useEditCanvas(options) {
|
|
|
2624
2631
|
canvas.on("selection:created", (e) => setSelected(e.selected ?? []));
|
|
2625
2632
|
canvas.on("selection:updated", (e) => setSelected(e.selected ?? []));
|
|
2626
2633
|
canvas.on("selection:cleared", () => setSelected([]));
|
|
2627
|
-
if (opts?.trackChanges) {
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
canvas.on("
|
|
2634
|
+
if (opts?.trackChanges !== false) {
|
|
2635
|
+
const markDirtyIfNotLoading = () => {
|
|
2636
|
+
if (!isInitialLoadRef.current) setIsDirty(true);
|
|
2637
|
+
};
|
|
2638
|
+
canvas.on("object:added", markDirtyIfNotLoading);
|
|
2639
|
+
canvas.on("object:removed", markDirtyIfNotLoading);
|
|
2640
|
+
canvas.on("object:modified", markDirtyIfNotLoading);
|
|
2641
|
+
canvas.on("background:modified", markDirtyIfNotLoading);
|
|
2632
2642
|
}
|
|
2633
2643
|
if (opts?.history) {
|
|
2634
2644
|
const syncHistoryState = () => {
|
|
@@ -2666,8 +2676,31 @@ function useEditCanvas(options) {
|
|
|
2666
2676
|
}
|
|
2667
2677
|
}
|
|
2668
2678
|
function invokeOnReady() {
|
|
2669
|
-
const
|
|
2670
|
-
|
|
2679
|
+
const initPromise = (async () => {
|
|
2680
|
+
if (opts?.canvasData) {
|
|
2681
|
+
setIsLoading(true);
|
|
2682
|
+
isInitialLoadRef.current = true;
|
|
2683
|
+
try {
|
|
2684
|
+
const loaded = await loadCanvas(canvas, opts.canvasData, {
|
|
2685
|
+
filter: opts.filter,
|
|
2686
|
+
borderRadius: opts.borderRadius
|
|
2687
|
+
});
|
|
2688
|
+
setObjects(loaded);
|
|
2689
|
+
} finally {
|
|
2690
|
+
isInitialLoadRef.current = false;
|
|
2691
|
+
setIsLoading(false);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
})();
|
|
2695
|
+
initPromise.then(async () => {
|
|
2696
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2697
|
+
await Promise.resolve(onReadyResult);
|
|
2698
|
+
if (opts?.invertBackground !== void 0) {
|
|
2699
|
+
setBackgroundInverted(canvas, opts.invertBackground);
|
|
2700
|
+
}
|
|
2701
|
+
if (canvas.lockLightMode !== void 0) {
|
|
2702
|
+
setLockLightModeState(canvas.lockLightMode);
|
|
2703
|
+
}
|
|
2671
2704
|
if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {
|
|
2672
2705
|
fitViewportToBackground(canvas);
|
|
2673
2706
|
syncZoom(canvasRef, setZoom);
|
|
@@ -2697,37 +2730,25 @@ function useEditCanvas(options) {
|
|
|
2697
2730
|
alignmentCleanupRef.current = null;
|
|
2698
2731
|
}
|
|
2699
2732
|
}, [options?.enableAlignment]);
|
|
2733
|
+
(0, import_react3.useEffect)(() => {
|
|
2734
|
+
const canvas = canvasRef.current;
|
|
2735
|
+
if (!canvas || options?.invertBackground === void 0) return;
|
|
2736
|
+
setBackgroundInverted(canvas, options.invertBackground);
|
|
2737
|
+
}, [options?.invertBackground]);
|
|
2738
|
+
const setLockLightMode = (0, import_react3.useCallback)((value) => {
|
|
2739
|
+
const canvas = canvasRef.current;
|
|
2740
|
+
if (canvas) {
|
|
2741
|
+
canvas.lockLightMode = value;
|
|
2742
|
+
}
|
|
2743
|
+
setLockLightModeState(value);
|
|
2744
|
+
}, []);
|
|
2700
2745
|
const setViewportMode = (0, import_react3.useCallback)((mode) => {
|
|
2701
2746
|
viewportRef.current?.setMode(mode);
|
|
2702
2747
|
setViewportModeState(mode);
|
|
2703
2748
|
}, []);
|
|
2704
2749
|
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2705
|
-
const
|
|
2706
|
-
|
|
2707
|
-
const canvas = canvasRef.current;
|
|
2708
|
-
if (!canvas) throw new Error("Canvas not ready");
|
|
2709
|
-
const opts = optionsRef.current;
|
|
2710
|
-
const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
|
|
2711
|
-
const img = await setBackgroundImage(canvas, url, resizeOpts);
|
|
2712
|
-
if (opts?.autoFitToBackground !== false) {
|
|
2713
|
-
fitViewportToBackground(canvas);
|
|
2714
|
-
syncZoom(canvasRef, setZoom);
|
|
2715
|
-
}
|
|
2716
|
-
return img;
|
|
2717
|
-
},
|
|
2718
|
-
[]
|
|
2719
|
-
);
|
|
2720
|
-
return {
|
|
2721
|
-
/** Pass this to `<Canvas onReady={...} />` */
|
|
2722
|
-
onReady,
|
|
2723
|
-
/** Ref to the underlying Fabric canvas instance. */
|
|
2724
|
-
canvasRef,
|
|
2725
|
-
/** Current zoom level (reactive). */
|
|
2726
|
-
zoom,
|
|
2727
|
-
/** Currently selected objects (reactive). */
|
|
2728
|
-
selected,
|
|
2729
|
-
/** Viewport controls. */
|
|
2730
|
-
viewport: {
|
|
2750
|
+
const viewport = (0, import_react3.useMemo)(
|
|
2751
|
+
() => ({
|
|
2731
2752
|
/** Current viewport mode (reactive). */
|
|
2732
2753
|
mode: viewportMode,
|
|
2733
2754
|
/** Switch between 'select' and 'pan' viewport modes. */
|
|
@@ -2742,61 +2763,125 @@ function useEditCanvas(options) {
|
|
|
2742
2763
|
panToObject,
|
|
2743
2764
|
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2744
2765
|
zoomToFit
|
|
2766
|
+
}),
|
|
2767
|
+
[
|
|
2768
|
+
viewportMode,
|
|
2769
|
+
setViewportMode,
|
|
2770
|
+
resetViewport2,
|
|
2771
|
+
zoomIn,
|
|
2772
|
+
zoomOut,
|
|
2773
|
+
panToObject,
|
|
2774
|
+
zoomToFit
|
|
2775
|
+
]
|
|
2776
|
+
);
|
|
2777
|
+
const resetDirty = (0, import_react3.useCallback)(() => setIsDirty(false), []);
|
|
2778
|
+
const markDirty = (0, import_react3.useCallback)(() => setIsDirty(true), []);
|
|
2779
|
+
const undo = (0, import_react3.useCallback)(async () => {
|
|
2780
|
+
const h = historyRef.current;
|
|
2781
|
+
if (!h) return;
|
|
2782
|
+
await h.undo();
|
|
2783
|
+
setCanUndo(h.canUndo());
|
|
2784
|
+
setCanRedo(h.canRedo());
|
|
2785
|
+
}, []);
|
|
2786
|
+
const redo = (0, import_react3.useCallback)(async () => {
|
|
2787
|
+
const h = historyRef.current;
|
|
2788
|
+
if (!h) return;
|
|
2789
|
+
await h.redo();
|
|
2790
|
+
setCanUndo(h.canUndo());
|
|
2791
|
+
setCanRedo(h.canRedo());
|
|
2792
|
+
}, []);
|
|
2793
|
+
const setBackground = (0, import_react3.useCallback)(
|
|
2794
|
+
async (url, bgOpts) => {
|
|
2795
|
+
const canvas = canvasRef.current;
|
|
2796
|
+
if (!canvas) throw new Error("Canvas not ready");
|
|
2797
|
+
const opts = optionsRef.current;
|
|
2798
|
+
const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
|
|
2799
|
+
const img = await setBackgroundImage(canvas, url, resizeOpts);
|
|
2800
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2801
|
+
fitViewportToBackground(canvas);
|
|
2802
|
+
syncZoom(canvasRef, setZoom);
|
|
2803
|
+
}
|
|
2804
|
+
return img;
|
|
2745
2805
|
},
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2806
|
+
[]
|
|
2807
|
+
);
|
|
2808
|
+
return (0, import_react3.useMemo)(
|
|
2809
|
+
() => ({
|
|
2810
|
+
/** Pass this to `<Canvas onReady={...} />` */
|
|
2811
|
+
onReady,
|
|
2812
|
+
/** Ref to the underlying Fabric canvas instance. */
|
|
2813
|
+
canvasRef,
|
|
2814
|
+
/** Current zoom level (reactive). */
|
|
2815
|
+
zoom,
|
|
2816
|
+
/** Loaded objects (reactive). Populated when `canvasData` is provided. */
|
|
2817
|
+
objects,
|
|
2818
|
+
/** Whether canvas data is currently being loaded. */
|
|
2819
|
+
isLoading,
|
|
2820
|
+
/** Currently selected objects (reactive). */
|
|
2821
|
+
selected,
|
|
2822
|
+
/** Viewport controls. */
|
|
2823
|
+
viewport,
|
|
2824
|
+
/** Whether vertex edit mode is currently active (reactive). */
|
|
2825
|
+
isEditingVertices,
|
|
2826
|
+
/**
|
|
2827
|
+
* Activate an interaction mode or return to select mode.
|
|
2828
|
+
*
|
|
2829
|
+
* Pass a setup function to activate a creation mode:
|
|
2830
|
+
* ```ts
|
|
2831
|
+
* canvas.setMode((c, viewport) =>
|
|
2832
|
+
* enableClickToCreate(c, factory, { viewport })
|
|
2833
|
+
* );
|
|
2834
|
+
* ```
|
|
2835
|
+
*
|
|
2836
|
+
* Pass `null` to deactivate and return to select mode:
|
|
2837
|
+
* ```ts
|
|
2838
|
+
* canvas.setMode(null);
|
|
2839
|
+
* ```
|
|
2840
|
+
*/
|
|
2841
|
+
setMode,
|
|
2842
|
+
/**
|
|
2843
|
+
* Set a background image from a URL. Automatically resizes if the image
|
|
2844
|
+
* exceeds the configured limits (opt out via `backgroundResize: false`),
|
|
2845
|
+
* and fits the viewport after setting if `autoFitToBackground` is enabled.
|
|
2846
|
+
*
|
|
2847
|
+
* Pass `{ preserveContrast: true }` to keep the current background contrast
|
|
2848
|
+
* when replacing the image.
|
|
2849
|
+
*/
|
|
2850
|
+
setBackground,
|
|
2851
|
+
/** Whether the canvas has been modified since the last `resetDirty()` call. Enabled by default (disable via `trackChanges: false`). */
|
|
2852
|
+
isDirty,
|
|
2853
|
+
/** Reset the dirty flag (e.g., after a successful save). */
|
|
2854
|
+
resetDirty,
|
|
2855
|
+
/** Manually mark the canvas as dirty (e.g., after a custom operation not tracked automatically). */
|
|
2856
|
+
markDirty,
|
|
2857
|
+
/** Undo the last change. Requires `history: true`. */
|
|
2858
|
+
undo,
|
|
2859
|
+
/** Redo a previously undone change. Requires `history: true`. */
|
|
2860
|
+
redo,
|
|
2861
|
+
/** Whether an undo operation is available (reactive). Requires `history: true`. */
|
|
2862
|
+
canUndo,
|
|
2863
|
+
/** Whether a redo operation is available (reactive). Requires `history: true`. */
|
|
2864
|
+
canRedo,
|
|
2865
|
+
/** Whether the canvas is locked to light mode. Read from loaded canvas data. */
|
|
2866
|
+
lockLightMode,
|
|
2867
|
+
/** Update lockLightMode on both the canvas instance and React state. */
|
|
2868
|
+
setLockLightMode
|
|
2869
|
+
}),
|
|
2870
|
+
// Only reactive state in deps — refs and stable callbacks are omitted
|
|
2871
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2872
|
+
[
|
|
2873
|
+
zoom,
|
|
2874
|
+
objects,
|
|
2875
|
+
isLoading,
|
|
2876
|
+
selected,
|
|
2877
|
+
viewport,
|
|
2878
|
+
isEditingVertices,
|
|
2879
|
+
isDirty,
|
|
2880
|
+
canUndo,
|
|
2881
|
+
canRedo,
|
|
2882
|
+
lockLightMode
|
|
2883
|
+
]
|
|
2884
|
+
);
|
|
2800
2885
|
}
|
|
2801
2886
|
|
|
2802
2887
|
// src/hooks/useViewCanvas.ts
|
|
@@ -2813,6 +2898,8 @@ function useViewCanvas(options) {
|
|
|
2813
2898
|
const optionsRef = (0, import_react4.useRef)(options);
|
|
2814
2899
|
optionsRef.current = options;
|
|
2815
2900
|
const [zoom, setZoom] = (0, import_react4.useState)(1);
|
|
2901
|
+
const [objects, setObjects] = (0, import_react4.useState)([]);
|
|
2902
|
+
const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
|
|
2816
2903
|
const onReady = (0, import_react4.useCallback)(
|
|
2817
2904
|
(canvas) => {
|
|
2818
2905
|
canvasRef.current = canvas;
|
|
@@ -2839,20 +2926,57 @@ function useViewCanvas(options) {
|
|
|
2839
2926
|
canvas.on("mouse:wheel", () => {
|
|
2840
2927
|
setZoom(canvas.getZoom());
|
|
2841
2928
|
});
|
|
2842
|
-
const
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2929
|
+
const initPromise = (async () => {
|
|
2930
|
+
if (opts?.canvasData) {
|
|
2931
|
+
setIsLoading(true);
|
|
2932
|
+
try {
|
|
2933
|
+
const loaded = await loadCanvas(canvas, opts.canvasData, {
|
|
2934
|
+
filter: opts.filter,
|
|
2935
|
+
borderRadius: opts.borderRadius
|
|
2936
|
+
});
|
|
2937
|
+
lockCanvas(canvas);
|
|
2938
|
+
setObjects(loaded);
|
|
2939
|
+
} finally {
|
|
2940
|
+
setIsLoading(false);
|
|
2848
2941
|
}
|
|
2849
|
-
}
|
|
2850
|
-
}
|
|
2942
|
+
}
|
|
2943
|
+
})();
|
|
2944
|
+
initPromise.then(async () => {
|
|
2945
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2946
|
+
await Promise.resolve(onReadyResult);
|
|
2947
|
+
if (opts?.invertBackground !== void 0) {
|
|
2948
|
+
setBackgroundInverted(canvas, opts.invertBackground);
|
|
2949
|
+
}
|
|
2950
|
+
if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {
|
|
2951
|
+
fitViewportToBackground(canvas);
|
|
2952
|
+
syncZoom(canvasRef, setZoom);
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2851
2955
|
},
|
|
2852
2956
|
// onReady and panAndZoom are intentionally excluded — we only initialize once
|
|
2853
2957
|
[]
|
|
2854
2958
|
);
|
|
2959
|
+
(0, import_react4.useEffect)(() => {
|
|
2960
|
+
const canvas = canvasRef.current;
|
|
2961
|
+
if (!canvas || options?.invertBackground === void 0) return;
|
|
2962
|
+
setBackgroundInverted(canvas, options.invertBackground);
|
|
2963
|
+
}, [options?.invertBackground]);
|
|
2855
2964
|
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2965
|
+
const viewport = (0, import_react4.useMemo)(
|
|
2966
|
+
() => ({
|
|
2967
|
+
/** Reset viewport to default (no pan, zoom = 1), or fit to background if one is set. */
|
|
2968
|
+
reset: resetViewport2,
|
|
2969
|
+
/** Zoom in toward the canvas center. Default step: 0.2. */
|
|
2970
|
+
zoomIn,
|
|
2971
|
+
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2972
|
+
zoomOut,
|
|
2973
|
+
/** Pan the viewport to center on a specific object. */
|
|
2974
|
+
panToObject,
|
|
2975
|
+
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2976
|
+
zoomToFit
|
|
2977
|
+
}),
|
|
2978
|
+
[resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit]
|
|
2979
|
+
);
|
|
2856
2980
|
const findObject = (id) => {
|
|
2857
2981
|
const c = canvasRef.current;
|
|
2858
2982
|
if (!c) return void 0;
|
|
@@ -2868,9 +2992,9 @@ function useViewCanvas(options) {
|
|
|
2868
2992
|
(styles) => {
|
|
2869
2993
|
const c = canvasRef.current;
|
|
2870
2994
|
if (!c) return;
|
|
2871
|
-
const
|
|
2995
|
+
const objects2 = c.getObjects();
|
|
2872
2996
|
const idMap = /* @__PURE__ */ new Map();
|
|
2873
|
-
for (const obj of
|
|
2997
|
+
for (const obj of objects2) {
|
|
2874
2998
|
if (obj.data?.id) idMap.set(obj.data.id, obj);
|
|
2875
2999
|
}
|
|
2876
3000
|
let updated = false;
|
|
@@ -2900,42 +3024,101 @@ function useViewCanvas(options) {
|
|
|
2900
3024
|
},
|
|
2901
3025
|
[]
|
|
2902
3026
|
);
|
|
2903
|
-
return
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
setObjectStyleByType
|
|
2929
|
-
};
|
|
3027
|
+
return (0, import_react4.useMemo)(
|
|
3028
|
+
() => ({
|
|
3029
|
+
/** Pass this to `<Canvas onReady={...} />` */
|
|
3030
|
+
onReady,
|
|
3031
|
+
/** Ref to the underlying Fabric canvas instance. */
|
|
3032
|
+
canvasRef,
|
|
3033
|
+
/** Current zoom level (reactive). */
|
|
3034
|
+
zoom,
|
|
3035
|
+
/** Loaded objects (reactive). Populated when `canvasData` is provided. */
|
|
3036
|
+
objects,
|
|
3037
|
+
/** Whether canvas data is currently being loaded. */
|
|
3038
|
+
isLoading,
|
|
3039
|
+
/** Viewport controls. */
|
|
3040
|
+
viewport,
|
|
3041
|
+
/** Update a single object's visual style by its `data.id`. */
|
|
3042
|
+
setObjectStyle,
|
|
3043
|
+
/** Batch-update multiple objects' visual styles in one render. Keyed by `data.id`. */
|
|
3044
|
+
setObjectStyles,
|
|
3045
|
+
/** Apply a visual style to all objects whose `data.type` matches. */
|
|
3046
|
+
setObjectStyleByType
|
|
3047
|
+
}),
|
|
3048
|
+
// Only reactive state in deps — refs and stable callbacks are omitted
|
|
3049
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3050
|
+
[zoom, objects, isLoading, viewport]
|
|
3051
|
+
);
|
|
2930
3052
|
}
|
|
2931
3053
|
|
|
2932
3054
|
// src/hooks/useCanvasEvents.ts
|
|
3055
|
+
var import_react7 = require("react");
|
|
3056
|
+
|
|
3057
|
+
// src/context/ViewCanvasContext.tsx
|
|
2933
3058
|
var import_react5 = require("react");
|
|
2934
|
-
|
|
2935
|
-
|
|
3059
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
3060
|
+
var ViewCanvasContext = (0, import_react5.createContext)(null);
|
|
3061
|
+
function ViewCanvasProvider({
|
|
3062
|
+
options,
|
|
3063
|
+
children
|
|
3064
|
+
}) {
|
|
3065
|
+
const canvas = useViewCanvas(options);
|
|
3066
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ViewCanvasContext.Provider, { value: canvas, children });
|
|
3067
|
+
}
|
|
3068
|
+
function useViewCanvasContext() {
|
|
3069
|
+
const ctx = (0, import_react5.useContext)(ViewCanvasContext);
|
|
3070
|
+
if (ctx === null) {
|
|
3071
|
+
throw new Error(
|
|
3072
|
+
"useViewCanvasContext must be used within a <ViewCanvasProvider>"
|
|
3073
|
+
);
|
|
3074
|
+
}
|
|
3075
|
+
return ctx;
|
|
3076
|
+
}
|
|
3077
|
+
function useViewCanvasContextSafe() {
|
|
3078
|
+
return (0, import_react5.useContext)(ViewCanvasContext);
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
// src/context/EditCanvasContext.tsx
|
|
3082
|
+
var import_react6 = require("react");
|
|
3083
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
3084
|
+
var EditCanvasContext = (0, import_react6.createContext)(null);
|
|
3085
|
+
function EditCanvasProvider({
|
|
3086
|
+
options,
|
|
3087
|
+
children
|
|
3088
|
+
}) {
|
|
3089
|
+
const canvas = useEditCanvas(options);
|
|
3090
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(EditCanvasContext.Provider, { value: canvas, children });
|
|
3091
|
+
}
|
|
3092
|
+
function useEditCanvasContext() {
|
|
3093
|
+
const ctx = (0, import_react6.useContext)(EditCanvasContext);
|
|
3094
|
+
if (ctx === null) {
|
|
3095
|
+
throw new Error(
|
|
3096
|
+
"useEditCanvasContext must be used within an <EditCanvasProvider>"
|
|
3097
|
+
);
|
|
3098
|
+
}
|
|
3099
|
+
return ctx;
|
|
3100
|
+
}
|
|
3101
|
+
function useEditCanvasContextSafe() {
|
|
3102
|
+
return (0, import_react6.useContext)(EditCanvasContext);
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
// src/context/useCanvasRef.ts
|
|
3106
|
+
function useCanvasRef() {
|
|
3107
|
+
const viewCtx = useViewCanvasContextSafe();
|
|
3108
|
+
const editCtx = useEditCanvasContextSafe();
|
|
3109
|
+
return viewCtx?.canvasRef ?? editCtx?.canvasRef ?? null;
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
// src/hooks/useCanvasEvents.ts
|
|
3113
|
+
function useCanvasEvents(canvasRefOrEvents, maybeEvents) {
|
|
3114
|
+
const isContextOverload = maybeEvents === void 0;
|
|
3115
|
+
const contextCanvasRef = useCanvasRef();
|
|
3116
|
+
const resolvedCanvasRef = isContextOverload ? contextCanvasRef : canvasRefOrEvents;
|
|
3117
|
+
const events = isContextOverload ? canvasRefOrEvents : maybeEvents;
|
|
3118
|
+
const eventsRef = (0, import_react7.useRef)(events);
|
|
2936
3119
|
eventsRef.current = events;
|
|
2937
|
-
(0,
|
|
2938
|
-
const canvas =
|
|
3120
|
+
(0, import_react7.useEffect)(() => {
|
|
3121
|
+
const canvas = resolvedCanvasRef?.current;
|
|
2939
3122
|
if (!canvas) return;
|
|
2940
3123
|
const wrappers = /* @__PURE__ */ new Map();
|
|
2941
3124
|
for (const key of Object.keys(eventsRef.current)) {
|
|
@@ -2951,22 +3134,26 @@ function useCanvasEvents(canvasRef, events) {
|
|
|
2951
3134
|
canvas.off(name, handler);
|
|
2952
3135
|
});
|
|
2953
3136
|
};
|
|
2954
|
-
}, [
|
|
3137
|
+
}, [resolvedCanvasRef]);
|
|
2955
3138
|
}
|
|
2956
3139
|
|
|
2957
3140
|
// src/hooks/useCanvasTooltip.ts
|
|
2958
|
-
var
|
|
2959
|
-
function useCanvasTooltip(
|
|
2960
|
-
const
|
|
3141
|
+
var import_react8 = require("react");
|
|
3142
|
+
function useCanvasTooltip(canvasRefOrOptions, maybeOptions) {
|
|
3143
|
+
const isContextOverload = maybeOptions === void 0;
|
|
3144
|
+
const contextCanvasRef = useCanvasRef();
|
|
3145
|
+
const resolvedCanvasRef = isContextOverload ? contextCanvasRef : canvasRefOrOptions;
|
|
3146
|
+
const options = isContextOverload ? canvasRefOrOptions : maybeOptions;
|
|
3147
|
+
const [state, setState] = (0, import_react8.useState)({
|
|
2961
3148
|
visible: false,
|
|
2962
3149
|
content: null,
|
|
2963
3150
|
position: { x: 0, y: 0 }
|
|
2964
3151
|
});
|
|
2965
|
-
const hoveredObjectRef = (0,
|
|
2966
|
-
const optionsRef = (0,
|
|
3152
|
+
const hoveredObjectRef = (0, import_react8.useRef)(null);
|
|
3153
|
+
const optionsRef = (0, import_react8.useRef)(options);
|
|
2967
3154
|
optionsRef.current = options;
|
|
2968
|
-
(0,
|
|
2969
|
-
const canvas =
|
|
3155
|
+
(0, import_react8.useEffect)(() => {
|
|
3156
|
+
const canvas = resolvedCanvasRef?.current;
|
|
2970
3157
|
if (!canvas) return;
|
|
2971
3158
|
function calculatePosition(target) {
|
|
2972
3159
|
const bounds = target.getBoundingRect();
|
|
@@ -3014,19 +3201,24 @@ function useCanvasTooltip(canvasRef, options) {
|
|
|
3014
3201
|
canvas.off("after:render", updatePosition);
|
|
3015
3202
|
canvas.off("mouse:wheel", updatePosition);
|
|
3016
3203
|
};
|
|
3017
|
-
}, [
|
|
3204
|
+
}, [resolvedCanvasRef]);
|
|
3018
3205
|
return state;
|
|
3019
3206
|
}
|
|
3020
3207
|
|
|
3021
3208
|
// src/hooks/useCanvasClick.ts
|
|
3022
|
-
var
|
|
3023
|
-
function useCanvasClick(
|
|
3024
|
-
const
|
|
3209
|
+
var import_react9 = require("react");
|
|
3210
|
+
function useCanvasClick(canvasRefOrOnClick, onClickOrOptions, maybeOptions) {
|
|
3211
|
+
const isContextOverload = typeof canvasRefOrOnClick === "function";
|
|
3212
|
+
const contextCanvasRef = useCanvasRef();
|
|
3213
|
+
const resolvedCanvasRef = isContextOverload ? contextCanvasRef : canvasRefOrOnClick;
|
|
3214
|
+
const onClick = isContextOverload ? canvasRefOrOnClick : onClickOrOptions;
|
|
3215
|
+
const options = isContextOverload ? onClickOrOptions : maybeOptions;
|
|
3216
|
+
const onClickRef = (0, import_react9.useRef)(onClick);
|
|
3025
3217
|
onClickRef.current = onClick;
|
|
3026
|
-
const optionsRef = (0,
|
|
3218
|
+
const optionsRef = (0, import_react9.useRef)(options);
|
|
3027
3219
|
optionsRef.current = options;
|
|
3028
|
-
(0,
|
|
3029
|
-
const canvas =
|
|
3220
|
+
(0, import_react9.useEffect)(() => {
|
|
3221
|
+
const canvas = resolvedCanvasRef?.current;
|
|
3030
3222
|
if (!canvas) return;
|
|
3031
3223
|
let mouseDown = null;
|
|
3032
3224
|
let isPanning = false;
|
|
@@ -3066,49 +3258,7 @@ function useCanvasClick(canvasRef, onClick, options) {
|
|
|
3066
3258
|
canvas.off("mouse:move", handleMouseMove);
|
|
3067
3259
|
canvas.off("mouse:up", handleMouseUp);
|
|
3068
3260
|
};
|
|
3069
|
-
}, [
|
|
3070
|
-
}
|
|
3071
|
-
|
|
3072
|
-
// src/context/EditCanvasContext.tsx
|
|
3073
|
-
var import_react8 = require("react");
|
|
3074
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
3075
|
-
var EditCanvasContext = (0, import_react8.createContext)(null);
|
|
3076
|
-
function EditCanvasProvider({
|
|
3077
|
-
options,
|
|
3078
|
-
children
|
|
3079
|
-
}) {
|
|
3080
|
-
const canvas = useEditCanvas(options);
|
|
3081
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EditCanvasContext.Provider, { value: canvas, children });
|
|
3082
|
-
}
|
|
3083
|
-
function useEditCanvasContext() {
|
|
3084
|
-
const ctx = (0, import_react8.useContext)(EditCanvasContext);
|
|
3085
|
-
if (ctx === null) {
|
|
3086
|
-
throw new Error(
|
|
3087
|
-
"useEditCanvasContext must be used within an <EditCanvasProvider>"
|
|
3088
|
-
);
|
|
3089
|
-
}
|
|
3090
|
-
return ctx;
|
|
3091
|
-
}
|
|
3092
|
-
|
|
3093
|
-
// src/context/ViewCanvasContext.tsx
|
|
3094
|
-
var import_react9 = require("react");
|
|
3095
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
3096
|
-
var ViewCanvasContext = (0, import_react9.createContext)(null);
|
|
3097
|
-
function ViewCanvasProvider({
|
|
3098
|
-
options,
|
|
3099
|
-
children
|
|
3100
|
-
}) {
|
|
3101
|
-
const canvas = useViewCanvas(options);
|
|
3102
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ViewCanvasContext.Provider, { value: canvas, children });
|
|
3103
|
-
}
|
|
3104
|
-
function useViewCanvasContext() {
|
|
3105
|
-
const ctx = (0, import_react9.useContext)(ViewCanvasContext);
|
|
3106
|
-
if (ctx === null) {
|
|
3107
|
-
throw new Error(
|
|
3108
|
-
"useViewCanvasContext must be used within a <ViewCanvasProvider>"
|
|
3109
|
-
);
|
|
3110
|
-
}
|
|
3111
|
-
return ctx;
|
|
3261
|
+
}, [resolvedCanvasRef]);
|
|
3112
3262
|
}
|
|
3113
3263
|
|
|
3114
3264
|
// src/overlay/ObjectOverlay.tsx
|
|
@@ -3117,15 +3267,17 @@ var import_material = require("@mui/material");
|
|
|
3117
3267
|
var import_fabric18 = require("fabric");
|
|
3118
3268
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
3119
3269
|
function ObjectOverlay({
|
|
3120
|
-
canvasRef,
|
|
3270
|
+
canvasRef: canvasRefProp,
|
|
3121
3271
|
object,
|
|
3122
3272
|
sx,
|
|
3123
3273
|
children,
|
|
3124
3274
|
...rest
|
|
3125
3275
|
}) {
|
|
3276
|
+
const contextCanvasRef = useCanvasRef();
|
|
3277
|
+
const canvasRef = canvasRefProp ?? contextCanvasRef;
|
|
3126
3278
|
const stackRef = (0, import_react10.useRef)(null);
|
|
3127
3279
|
(0, import_react10.useEffect)(() => {
|
|
3128
|
-
const canvas = canvasRef
|
|
3280
|
+
const canvas = canvasRef?.current;
|
|
3129
3281
|
if (!canvas || !object) return;
|
|
3130
3282
|
function update() {
|
|
3131
3283
|
const el = stackRef.current;
|
|
@@ -3505,6 +3657,7 @@ var import_fabric19 = require("fabric");
|
|
|
3505
3657
|
snapCursorPoint,
|
|
3506
3658
|
useCanvasClick,
|
|
3507
3659
|
useCanvasEvents,
|
|
3660
|
+
useCanvasRef,
|
|
3508
3661
|
useCanvasTooltip,
|
|
3509
3662
|
useEditCanvas,
|
|
3510
3663
|
useEditCanvasContext,
|