@bwp-web/canvas 0.8.2 → 0.9.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/dist/context/CanvasRefContext.d.ts +17 -0
- package/dist/context/CanvasRefContext.d.ts.map +1 -0
- package/dist/context/EditCanvasContext.d.ts +53 -5
- package/dist/context/EditCanvasContext.d.ts.map +1 -1
- package/dist/context/ViewCanvasContext.d.ts +53 -6
- package/dist/context/ViewCanvasContext.d.ts.map +1 -1
- package/dist/context/index.d.ts +5 -4
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/useCanvasRef.d.ts +15 -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 +432 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +417 -161
- 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,58 @@ 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/CanvasRefContext.ts
|
|
2984
|
+
import { createContext, useContext } from "react";
|
|
2985
|
+
var CanvasRefContext = createContext(null);
|
|
2986
|
+
function useCanvasRefContext() {
|
|
2987
|
+
return useContext(CanvasRefContext);
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
// src/context/useCanvasRef.ts
|
|
2991
|
+
function useCanvasRef() {
|
|
2992
|
+
return useCanvasRefContext();
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
// src/hooks/useCanvasEvents.ts
|
|
2996
|
+
function useCanvasEvents(canvasRefOrEvents, maybeEvents) {
|
|
2997
|
+
const isContextOverload = maybeEvents === void 0;
|
|
2998
|
+
const contextCanvasRef = useCanvasRef();
|
|
2999
|
+
const resolvedCanvasRef = isContextOverload ? contextCanvasRef : canvasRefOrEvents;
|
|
3000
|
+
const events = isContextOverload ? canvasRefOrEvents : maybeEvents;
|
|
2862
3001
|
const eventsRef = useRef4(events);
|
|
2863
3002
|
eventsRef.current = events;
|
|
2864
|
-
|
|
2865
|
-
const canvas =
|
|
3003
|
+
useEffect4(() => {
|
|
3004
|
+
const canvas = resolvedCanvasRef?.current;
|
|
2866
3005
|
if (!canvas) return;
|
|
2867
3006
|
const wrappers = /* @__PURE__ */ new Map();
|
|
2868
3007
|
for (const key of Object.keys(eventsRef.current)) {
|
|
@@ -2878,12 +3017,16 @@ function useCanvasEvents(canvasRef, events) {
|
|
|
2878
3017
|
canvas.off(name, handler);
|
|
2879
3018
|
});
|
|
2880
3019
|
};
|
|
2881
|
-
}, [
|
|
3020
|
+
}, [resolvedCanvasRef]);
|
|
2882
3021
|
}
|
|
2883
3022
|
|
|
2884
3023
|
// src/hooks/useCanvasTooltip.ts
|
|
2885
|
-
import { useEffect as
|
|
2886
|
-
function useCanvasTooltip(
|
|
3024
|
+
import { useEffect as useEffect5, useRef as useRef5, useState as useState3 } from "react";
|
|
3025
|
+
function useCanvasTooltip(canvasRefOrOptions, maybeOptions) {
|
|
3026
|
+
const isContextOverload = maybeOptions === void 0;
|
|
3027
|
+
const contextCanvasRef = useCanvasRef();
|
|
3028
|
+
const resolvedCanvasRef = isContextOverload ? contextCanvasRef : canvasRefOrOptions;
|
|
3029
|
+
const options = isContextOverload ? canvasRefOrOptions : maybeOptions;
|
|
2887
3030
|
const [state, setState] = useState3({
|
|
2888
3031
|
visible: false,
|
|
2889
3032
|
content: null,
|
|
@@ -2892,8 +3035,8 @@ function useCanvasTooltip(canvasRef, options) {
|
|
|
2892
3035
|
const hoveredObjectRef = useRef5(null);
|
|
2893
3036
|
const optionsRef = useRef5(options);
|
|
2894
3037
|
optionsRef.current = options;
|
|
2895
|
-
|
|
2896
|
-
const canvas =
|
|
3038
|
+
useEffect5(() => {
|
|
3039
|
+
const canvas = resolvedCanvasRef?.current;
|
|
2897
3040
|
if (!canvas) return;
|
|
2898
3041
|
function calculatePosition(target) {
|
|
2899
3042
|
const bounds = target.getBoundingRect();
|
|
@@ -2941,19 +3084,24 @@ function useCanvasTooltip(canvasRef, options) {
|
|
|
2941
3084
|
canvas.off("after:render", updatePosition);
|
|
2942
3085
|
canvas.off("mouse:wheel", updatePosition);
|
|
2943
3086
|
};
|
|
2944
|
-
}, [
|
|
3087
|
+
}, [resolvedCanvasRef]);
|
|
2945
3088
|
return state;
|
|
2946
3089
|
}
|
|
2947
3090
|
|
|
2948
3091
|
// src/hooks/useCanvasClick.ts
|
|
2949
|
-
import { useEffect as
|
|
2950
|
-
function useCanvasClick(
|
|
3092
|
+
import { useEffect as useEffect6, useRef as useRef6 } from "react";
|
|
3093
|
+
function useCanvasClick(canvasRefOrOnClick, onClickOrOptions, maybeOptions) {
|
|
3094
|
+
const isContextOverload = typeof canvasRefOrOnClick === "function";
|
|
3095
|
+
const contextCanvasRef = useCanvasRef();
|
|
3096
|
+
const resolvedCanvasRef = isContextOverload ? contextCanvasRef : canvasRefOrOnClick;
|
|
3097
|
+
const onClick = isContextOverload ? canvasRefOrOnClick : onClickOrOptions;
|
|
3098
|
+
const options = isContextOverload ? onClickOrOptions : maybeOptions;
|
|
2951
3099
|
const onClickRef = useRef6(onClick);
|
|
2952
3100
|
onClickRef.current = onClick;
|
|
2953
3101
|
const optionsRef = useRef6(options);
|
|
2954
3102
|
optionsRef.current = options;
|
|
2955
|
-
|
|
2956
|
-
const canvas =
|
|
3103
|
+
useEffect6(() => {
|
|
3104
|
+
const canvas = resolvedCanvasRef?.current;
|
|
2957
3105
|
if (!canvas) return;
|
|
2958
3106
|
let mouseDown = null;
|
|
2959
3107
|
let isPanning = false;
|
|
@@ -2993,66 +3141,169 @@ function useCanvasClick(canvasRef, onClick, options) {
|
|
|
2993
3141
|
canvas.off("mouse:move", handleMouseMove);
|
|
2994
3142
|
canvas.off("mouse:up", handleMouseUp);
|
|
2995
3143
|
};
|
|
2996
|
-
}, [
|
|
3144
|
+
}, [resolvedCanvasRef]);
|
|
2997
3145
|
}
|
|
2998
3146
|
|
|
2999
3147
|
// src/context/EditCanvasContext.tsx
|
|
3000
|
-
import { createContext, useContext } from "react";
|
|
3148
|
+
import { createContext as createContext2, useContext as useContext2, useMemo as useMemo4 } from "react";
|
|
3001
3149
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
3002
|
-
var
|
|
3150
|
+
var EditViewportContext = createContext2(null);
|
|
3151
|
+
var EditStateContext = createContext2(null);
|
|
3003
3152
|
function EditCanvasProvider({
|
|
3004
3153
|
options,
|
|
3005
3154
|
children
|
|
3006
3155
|
}) {
|
|
3007
3156
|
const canvas = useEditCanvas(options);
|
|
3008
|
-
|
|
3157
|
+
const viewportValue = useMemo4(
|
|
3158
|
+
() => ({ zoom: canvas.zoom, viewport: canvas.viewport }),
|
|
3159
|
+
[canvas.zoom, canvas.viewport]
|
|
3160
|
+
);
|
|
3161
|
+
const stateValue = useMemo4(
|
|
3162
|
+
() => ({
|
|
3163
|
+
onReady: canvas.onReady,
|
|
3164
|
+
objects: canvas.objects,
|
|
3165
|
+
isLoading: canvas.isLoading,
|
|
3166
|
+
selected: canvas.selected,
|
|
3167
|
+
isEditingVertices: canvas.isEditingVertices,
|
|
3168
|
+
setMode: canvas.setMode,
|
|
3169
|
+
setBackground: canvas.setBackground,
|
|
3170
|
+
isDirty: canvas.isDirty,
|
|
3171
|
+
resetDirty: canvas.resetDirty,
|
|
3172
|
+
markDirty: canvas.markDirty,
|
|
3173
|
+
undo: canvas.undo,
|
|
3174
|
+
redo: canvas.redo,
|
|
3175
|
+
canUndo: canvas.canUndo,
|
|
3176
|
+
canRedo: canvas.canRedo,
|
|
3177
|
+
lockLightMode: canvas.lockLightMode,
|
|
3178
|
+
setLockLightMode: canvas.setLockLightMode
|
|
3179
|
+
}),
|
|
3180
|
+
// Only reactive state — stable callbacks omitted
|
|
3181
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3182
|
+
[
|
|
3183
|
+
canvas.objects,
|
|
3184
|
+
canvas.isLoading,
|
|
3185
|
+
canvas.selected,
|
|
3186
|
+
canvas.isEditingVertices,
|
|
3187
|
+
canvas.isDirty,
|
|
3188
|
+
canvas.canUndo,
|
|
3189
|
+
canvas.canRedo,
|
|
3190
|
+
canvas.lockLightMode
|
|
3191
|
+
]
|
|
3192
|
+
);
|
|
3193
|
+
return /* @__PURE__ */ jsx2(CanvasRefContext.Provider, { value: canvas.canvasRef, children: /* @__PURE__ */ jsx2(EditViewportContext.Provider, { value: viewportValue, children: /* @__PURE__ */ jsx2(EditStateContext.Provider, { value: stateValue, children }) }) });
|
|
3009
3194
|
}
|
|
3010
3195
|
function useEditCanvasContext() {
|
|
3011
|
-
const
|
|
3012
|
-
|
|
3196
|
+
const canvasRef = useContext2(CanvasRefContext);
|
|
3197
|
+
const viewport = useContext2(EditViewportContext);
|
|
3198
|
+
const state = useContext2(EditStateContext);
|
|
3199
|
+
if (canvasRef === null || viewport === null || state === null) {
|
|
3013
3200
|
throw new Error(
|
|
3014
3201
|
"useEditCanvasContext must be used within an <EditCanvasProvider>"
|
|
3015
3202
|
);
|
|
3016
3203
|
}
|
|
3204
|
+
return useMemo4(
|
|
3205
|
+
() => ({ canvasRef, ...viewport, ...state }),
|
|
3206
|
+
[canvasRef, viewport, state]
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
function useEditCanvasViewport() {
|
|
3210
|
+
const ctx = useContext2(EditViewportContext);
|
|
3211
|
+
if (ctx === null) {
|
|
3212
|
+
throw new Error(
|
|
3213
|
+
"useEditCanvasViewport must be used within an <EditCanvasProvider>"
|
|
3214
|
+
);
|
|
3215
|
+
}
|
|
3216
|
+
return ctx;
|
|
3217
|
+
}
|
|
3218
|
+
function useEditCanvasState() {
|
|
3219
|
+
const ctx = useContext2(EditStateContext);
|
|
3220
|
+
if (ctx === null) {
|
|
3221
|
+
throw new Error(
|
|
3222
|
+
"useEditCanvasState must be used within an <EditCanvasProvider>"
|
|
3223
|
+
);
|
|
3224
|
+
}
|
|
3017
3225
|
return ctx;
|
|
3018
3226
|
}
|
|
3019
3227
|
|
|
3020
3228
|
// src/context/ViewCanvasContext.tsx
|
|
3021
|
-
import { createContext as
|
|
3229
|
+
import { createContext as createContext3, useContext as useContext3, useMemo as useMemo5 } from "react";
|
|
3022
3230
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
3023
|
-
var
|
|
3231
|
+
var ViewViewportContext = createContext3(null);
|
|
3232
|
+
var ViewStateContext = createContext3(null);
|
|
3024
3233
|
function ViewCanvasProvider({
|
|
3025
3234
|
options,
|
|
3026
3235
|
children
|
|
3027
3236
|
}) {
|
|
3028
3237
|
const canvas = useViewCanvas(options);
|
|
3029
|
-
|
|
3238
|
+
const viewportValue = useMemo5(
|
|
3239
|
+
() => ({ zoom: canvas.zoom, viewport: canvas.viewport }),
|
|
3240
|
+
[canvas.zoom, canvas.viewport]
|
|
3241
|
+
);
|
|
3242
|
+
const stateValue = useMemo5(
|
|
3243
|
+
() => ({
|
|
3244
|
+
onReady: canvas.onReady,
|
|
3245
|
+
objects: canvas.objects,
|
|
3246
|
+
isLoading: canvas.isLoading,
|
|
3247
|
+
setObjectStyle: canvas.setObjectStyle,
|
|
3248
|
+
setObjectStyles: canvas.setObjectStyles,
|
|
3249
|
+
setObjectStyleByType: canvas.setObjectStyleByType
|
|
3250
|
+
}),
|
|
3251
|
+
// Only reactive state — stable callbacks omitted
|
|
3252
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3253
|
+
[canvas.objects, canvas.isLoading]
|
|
3254
|
+
);
|
|
3255
|
+
return /* @__PURE__ */ jsx3(CanvasRefContext.Provider, { value: canvas.canvasRef, children: /* @__PURE__ */ jsx3(ViewViewportContext.Provider, { value: viewportValue, children: /* @__PURE__ */ jsx3(ViewStateContext.Provider, { value: stateValue, children }) }) });
|
|
3030
3256
|
}
|
|
3031
3257
|
function useViewCanvasContext() {
|
|
3032
|
-
const
|
|
3033
|
-
|
|
3258
|
+
const canvasRef = useContext3(CanvasRefContext);
|
|
3259
|
+
const viewport = useContext3(ViewViewportContext);
|
|
3260
|
+
const state = useContext3(ViewStateContext);
|
|
3261
|
+
if (canvasRef === null || viewport === null || state === null) {
|
|
3034
3262
|
throw new Error(
|
|
3035
3263
|
"useViewCanvasContext must be used within a <ViewCanvasProvider>"
|
|
3036
3264
|
);
|
|
3037
3265
|
}
|
|
3266
|
+
return useMemo5(
|
|
3267
|
+
() => ({ canvasRef, ...viewport, ...state }),
|
|
3268
|
+
[canvasRef, viewport, state]
|
|
3269
|
+
);
|
|
3270
|
+
}
|
|
3271
|
+
function useViewCanvasViewport() {
|
|
3272
|
+
const ctx = useContext3(ViewViewportContext);
|
|
3273
|
+
if (ctx === null) {
|
|
3274
|
+
throw new Error(
|
|
3275
|
+
"useViewCanvasViewport must be used within a <ViewCanvasProvider>"
|
|
3276
|
+
);
|
|
3277
|
+
}
|
|
3278
|
+
return ctx;
|
|
3279
|
+
}
|
|
3280
|
+
function useViewCanvasState() {
|
|
3281
|
+
const ctx = useContext3(ViewStateContext);
|
|
3282
|
+
if (ctx === null) {
|
|
3283
|
+
throw new Error(
|
|
3284
|
+
"useViewCanvasState must be used within a <ViewCanvasProvider>"
|
|
3285
|
+
);
|
|
3286
|
+
}
|
|
3038
3287
|
return ctx;
|
|
3039
3288
|
}
|
|
3040
3289
|
|
|
3041
3290
|
// src/overlay/ObjectOverlay.tsx
|
|
3042
|
-
import { useEffect as
|
|
3291
|
+
import { useEffect as useEffect7, useRef as useRef7 } from "react";
|
|
3043
3292
|
import { Stack } from "@mui/material";
|
|
3044
3293
|
import { util as util4 } from "fabric";
|
|
3045
3294
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
3046
3295
|
function ObjectOverlay({
|
|
3047
|
-
canvasRef,
|
|
3296
|
+
canvasRef: canvasRefProp,
|
|
3048
3297
|
object,
|
|
3049
3298
|
sx,
|
|
3050
3299
|
children,
|
|
3051
3300
|
...rest
|
|
3052
3301
|
}) {
|
|
3302
|
+
const contextCanvasRef = useCanvasRef();
|
|
3303
|
+
const canvasRef = canvasRefProp ?? contextCanvasRef;
|
|
3053
3304
|
const stackRef = useRef7(null);
|
|
3054
|
-
|
|
3055
|
-
const canvas = canvasRef
|
|
3305
|
+
useEffect7(() => {
|
|
3306
|
+
const canvas = canvasRef?.current;
|
|
3056
3307
|
if (!canvas || !object) return;
|
|
3057
3308
|
function update() {
|
|
3058
3309
|
const el = stackRef.current;
|
|
@@ -3106,7 +3357,7 @@ function ObjectOverlay({
|
|
|
3106
3357
|
|
|
3107
3358
|
// src/overlay/OverlayContent.tsx
|
|
3108
3359
|
import { Stack as Stack2 } from "@mui/material";
|
|
3109
|
-
import { useEffect as
|
|
3360
|
+
import { useEffect as useEffect8, useRef as useRef8 } from "react";
|
|
3110
3361
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
3111
3362
|
function OverlayContent({
|
|
3112
3363
|
children,
|
|
@@ -3117,7 +3368,7 @@ function OverlayContent({
|
|
|
3117
3368
|
}) {
|
|
3118
3369
|
const outerRef = useRef8(null);
|
|
3119
3370
|
const innerRef = useRef8(null);
|
|
3120
|
-
|
|
3371
|
+
useEffect8(() => {
|
|
3121
3372
|
const outer = outerRef.current;
|
|
3122
3373
|
const inner = innerRef.current;
|
|
3123
3374
|
if (!outer || !inner) return;
|
|
@@ -3178,7 +3429,7 @@ function OverlayContent({
|
|
|
3178
3429
|
|
|
3179
3430
|
// src/overlay/FixedSizeContent.tsx
|
|
3180
3431
|
import { Stack as Stack3 } from "@mui/material";
|
|
3181
|
-
import { useEffect as
|
|
3432
|
+
import { useEffect as useEffect9, useRef as useRef9 } from "react";
|
|
3182
3433
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
3183
3434
|
function FixedSizeContent({
|
|
3184
3435
|
children,
|
|
@@ -3189,7 +3440,7 @@ function FixedSizeContent({
|
|
|
3189
3440
|
}) {
|
|
3190
3441
|
const ref = useRef9(null);
|
|
3191
3442
|
const totalContentHeightRef = useRef9(0);
|
|
3192
|
-
|
|
3443
|
+
useEffect9(() => {
|
|
3193
3444
|
const el = ref.current;
|
|
3194
3445
|
if (!el) return;
|
|
3195
3446
|
let clipAncestor = el.parentElement;
|
|
@@ -3252,7 +3503,7 @@ function FixedSizeContent({
|
|
|
3252
3503
|
|
|
3253
3504
|
// src/overlay/OverlayBadge.tsx
|
|
3254
3505
|
import { Stack as Stack4 } from "@mui/material";
|
|
3255
|
-
import { useEffect as
|
|
3506
|
+
import { useEffect as useEffect10, useRef as useRef10 } from "react";
|
|
3256
3507
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
3257
3508
|
function toPx(v) {
|
|
3258
3509
|
if (v === void 0) return void 0;
|
|
@@ -3297,7 +3548,7 @@ function OverlayBadge({
|
|
|
3297
3548
|
}) {
|
|
3298
3549
|
const ref = useRef10(null);
|
|
3299
3550
|
const baseSize = useRef10(null);
|
|
3300
|
-
|
|
3551
|
+
useEffect10(() => {
|
|
3301
3552
|
const el = ref.current;
|
|
3302
3553
|
if (!el) return;
|
|
3303
3554
|
const ancestor = el.parentElement;
|
|
@@ -3439,11 +3690,16 @@ export {
|
|
|
3439
3690
|
snapCursorPoint,
|
|
3440
3691
|
useCanvasClick,
|
|
3441
3692
|
useCanvasEvents,
|
|
3693
|
+
useCanvasRef,
|
|
3442
3694
|
useCanvasTooltip,
|
|
3443
3695
|
useEditCanvas,
|
|
3444
3696
|
useEditCanvasContext,
|
|
3697
|
+
useEditCanvasState,
|
|
3698
|
+
useEditCanvasViewport,
|
|
3445
3699
|
useViewCanvas,
|
|
3446
3700
|
useViewCanvasContext,
|
|
3701
|
+
useViewCanvasState,
|
|
3702
|
+
useViewCanvasViewport,
|
|
3447
3703
|
util5 as util
|
|
3448
3704
|
};
|
|
3449
3705
|
//# sourceMappingURL=index.js.map
|