@bwp-web/canvas 0.8.1 → 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/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
@@ -2126,7 +2126,8 @@ function enableVertexEdit(canvas, polygon, options, onExit) {
2126
2126
  // src/serialization.ts
2127
2127
  import {
2128
2128
  FabricImage as FabricImage2,
2129
- Rect as Rect5
2129
+ Rect as Rect5,
2130
+ filters as filters2
2130
2131
  } from "fabric";
2131
2132
  var strokeBaseMap = /* @__PURE__ */ new WeakMap();
2132
2133
  var borderRadiusBaseMap = /* @__PURE__ */ new WeakMap();
@@ -2319,6 +2320,10 @@ async function loadCanvas(canvas, json, options) {
2319
2320
  });
2320
2321
  bg.setCoords();
2321
2322
  }
2323
+ if (bg.filters?.some((f) => f instanceof filters2.Invert)) {
2324
+ bg.filters = bg.filters.filter((f) => !(f instanceof filters2.Invert));
2325
+ bg.applyFilters();
2326
+ }
2322
2327
  }
2323
2328
  if (options?.filter) {
2324
2329
  const toRemove = [];
@@ -2450,6 +2455,7 @@ function useEditCanvas(options) {
2450
2455
  const vertexEditCleanupRef = useRef2(null);
2451
2456
  const keyboardCleanupRef = useRef2(null);
2452
2457
  const historyRef = useRef2(null);
2458
+ const isInitialLoadRef = useRef2(false);
2453
2459
  const optionsRef = useRef2(options);
2454
2460
  optionsRef.current = options;
2455
2461
  const savedSelectabilityRef = useRef2(
@@ -2462,6 +2468,11 @@ function useEditCanvas(options) {
2462
2468
  const [isDirty, setIsDirty] = useState(false);
2463
2469
  const [canUndo, setCanUndo] = useState(false);
2464
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
+ );
2465
2476
  const setMode = useCallback((setup) => {
2466
2477
  vertexEditCleanupRef.current?.();
2467
2478
  vertexEditCleanupRef.current = null;
@@ -2546,11 +2557,14 @@ function useEditCanvas(options) {
2546
2557
  canvas.on("selection:created", (e) => setSelected(e.selected ?? []));
2547
2558
  canvas.on("selection:updated", (e) => setSelected(e.selected ?? []));
2548
2559
  canvas.on("selection:cleared", () => setSelected([]));
2549
- if (opts?.trackChanges) {
2550
- canvas.on("object:added", () => setIsDirty(true));
2551
- canvas.on("object:removed", () => setIsDirty(true));
2552
- canvas.on("object:modified", () => setIsDirty(true));
2553
- canvas.on("background:modified", () => setIsDirty(true));
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);
2554
2568
  }
2555
2569
  if (opts?.history) {
2556
2570
  const syncHistoryState = () => {
@@ -2588,8 +2602,31 @@ function useEditCanvas(options) {
2588
2602
  }
2589
2603
  }
2590
2604
  function invokeOnReady() {
2591
- const onReadyResult = opts?.onReady?.(canvas);
2592
- Promise.resolve(onReadyResult).then(() => {
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
+ }
2593
2630
  if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {
2594
2631
  fitViewportToBackground(canvas);
2595
2632
  syncZoom(canvasRef, setZoom);
@@ -2619,37 +2656,25 @@ function useEditCanvas(options) {
2619
2656
  alignmentCleanupRef.current = null;
2620
2657
  }
2621
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
+ }, []);
2622
2671
  const setViewportMode = useCallback((mode) => {
2623
2672
  viewportRef.current?.setMode(mode);
2624
2673
  setViewportModeState(mode);
2625
2674
  }, []);
2626
2675
  const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
2627
- const setBackground = useCallback(
2628
- async (url, bgOpts) => {
2629
- const canvas = canvasRef.current;
2630
- if (!canvas) throw new Error("Canvas not ready");
2631
- const opts = optionsRef.current;
2632
- const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
2633
- const img = await setBackgroundImage(canvas, url, resizeOpts);
2634
- if (opts?.autoFitToBackground !== false) {
2635
- fitViewportToBackground(canvas);
2636
- syncZoom(canvasRef, setZoom);
2637
- }
2638
- return img;
2639
- },
2640
- []
2641
- );
2642
- return {
2643
- /** Pass this to `<Canvas onReady={...} />` */
2644
- onReady,
2645
- /** Ref to the underlying Fabric canvas instance. */
2646
- canvasRef,
2647
- /** Current zoom level (reactive). */
2648
- zoom,
2649
- /** Currently selected objects (reactive). */
2650
- selected,
2651
- /** Viewport controls. */
2652
- viewport: {
2676
+ const viewport = useMemo2(
2677
+ () => ({
2653
2678
  /** Current viewport mode (reactive). */
2654
2679
  mode: viewportMode,
2655
2680
  /** Switch between 'select' and 'pan' viewport modes. */
@@ -2664,65 +2689,129 @@ function useEditCanvas(options) {
2664
2689
  panToObject,
2665
2690
  /** Zoom and pan to fit a specific object in the viewport. */
2666
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;
2667
2731
  },
2668
- /** Whether vertex edit mode is currently active (reactive). */
2669
- isEditingVertices,
2670
- /**
2671
- * Activate an interaction mode or return to select mode.
2672
- *
2673
- * Pass a setup function to activate a creation mode:
2674
- * ```ts
2675
- * canvas.setMode((c, viewport) =>
2676
- * enableClickToCreate(c, factory, { viewport })
2677
- * );
2678
- * ```
2679
- *
2680
- * Pass `null` to deactivate and return to select mode:
2681
- * ```ts
2682
- * canvas.setMode(null);
2683
- * ```
2684
- */
2685
- setMode,
2686
- /**
2687
- * Set a background image from a URL. Automatically resizes if the image
2688
- * exceeds the configured limits (opt out via `backgroundResize: false`),
2689
- * and fits the viewport after setting if `autoFitToBackground` is enabled.
2690
- *
2691
- * Pass `{ preserveContrast: true }` to keep the current background contrast
2692
- * when replacing the image.
2693
- */
2694
- setBackground,
2695
- /** Whether the canvas has been modified since the last `resetDirty()` call. Requires `trackChanges: true`. */
2696
- isDirty,
2697
- /** Reset the dirty flag (e.g., after a successful save). */
2698
- resetDirty: useCallback(() => setIsDirty(false), []),
2699
- /** Manually mark the canvas as dirty (e.g., after a custom operation not tracked automatically). */
2700
- markDirty: useCallback(() => setIsDirty(true), []),
2701
- /** Undo the last change. Requires `history: true`. */
2702
- undo: useCallback(async () => {
2703
- const h = historyRef.current;
2704
- if (!h) return;
2705
- await h.undo();
2706
- setCanUndo(h.canUndo());
2707
- setCanRedo(h.canRedo());
2708
- }, []),
2709
- /** Redo a previously undone change. Requires `history: true`. */
2710
- redo: useCallback(async () => {
2711
- const h = historyRef.current;
2712
- if (!h) return;
2713
- await h.redo();
2714
- setCanUndo(h.canUndo());
2715
- setCanRedo(h.canRedo());
2716
- }, []),
2717
- /** Whether an undo operation is available (reactive). Requires `history: true`. */
2718
- canUndo,
2719
- /** Whether a redo operation is available (reactive). Requires `history: true`. */
2720
- canRedo
2721
- };
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
+ );
2722
2811
  }
2723
2812
 
2724
2813
  // src/hooks/useViewCanvas.ts
2725
- 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";
2726
2815
  function lockCanvas(canvas) {
2727
2816
  canvas.selection = false;
2728
2817
  canvas.forEachObject((obj) => {
@@ -2735,6 +2824,8 @@ function useViewCanvas(options) {
2735
2824
  const optionsRef = useRef3(options);
2736
2825
  optionsRef.current = options;
2737
2826
  const [zoom, setZoom] = useState2(1);
2827
+ const [objects, setObjects] = useState2([]);
2828
+ const [isLoading, setIsLoading] = useState2(false);
2738
2829
  const onReady = useCallback2(
2739
2830
  (canvas) => {
2740
2831
  canvasRef.current = canvas;
@@ -2761,20 +2852,57 @@ function useViewCanvas(options) {
2761
2852
  canvas.on("mouse:wheel", () => {
2762
2853
  setZoom(canvas.getZoom());
2763
2854
  });
2764
- const onReadyResult = opts?.onReady?.(canvas);
2765
- if (opts?.autoFitToBackground !== false) {
2766
- Promise.resolve(onReadyResult).then(() => {
2767
- if (canvas.backgroundImage) {
2768
- fitViewportToBackground(canvas);
2769
- syncZoom(canvasRef, setZoom);
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);
2770
2867
  }
2771
- });
2772
- }
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
+ });
2773
2881
  },
2774
2882
  // onReady and panAndZoom are intentionally excluded — we only initialize once
2775
2883
  []
2776
2884
  );
2885
+ useEffect3(() => {
2886
+ const canvas = canvasRef.current;
2887
+ if (!canvas || options?.invertBackground === void 0) return;
2888
+ setBackgroundInverted(canvas, options.invertBackground);
2889
+ }, [options?.invertBackground]);
2777
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
+ );
2778
2906
  const findObject = (id) => {
2779
2907
  const c = canvasRef.current;
2780
2908
  if (!c) return void 0;
@@ -2790,9 +2918,9 @@ function useViewCanvas(options) {
2790
2918
  (styles) => {
2791
2919
  const c = canvasRef.current;
2792
2920
  if (!c) return;
2793
- const objects = c.getObjects();
2921
+ const objects2 = c.getObjects();
2794
2922
  const idMap = /* @__PURE__ */ new Map();
2795
- for (const obj of objects) {
2923
+ for (const obj of objects2) {
2796
2924
  if (obj.data?.id) idMap.set(obj.data.id, obj);
2797
2925
  }
2798
2926
  let updated = false;
@@ -2822,42 +2950,101 @@ function useViewCanvas(options) {
2822
2950
  },
2823
2951
  []
2824
2952
  );
2825
- return {
2826
- /** Pass this to `<Canvas onReady={...} />` */
2827
- onReady,
2828
- /** Ref to the underlying Fabric canvas instance. */
2829
- canvasRef,
2830
- /** Current zoom level (reactive). */
2831
- zoom,
2832
- /** Viewport controls. */
2833
- viewport: {
2834
- /** Reset viewport to default (no pan, zoom = 1), or fit to background if one is set. */
2835
- reset: resetViewport2,
2836
- /** Zoom in toward the canvas center. Default step: 0.2. */
2837
- zoomIn,
2838
- /** Zoom out from the canvas center. Default step: 0.2. */
2839
- zoomOut,
2840
- /** Pan the viewport to center on a specific object. */
2841
- panToObject,
2842
- /** Zoom and pan to fit a specific object in the viewport. */
2843
- zoomToFit
2844
- },
2845
- /** Update a single object's visual style by its `data.id`. */
2846
- setObjectStyle,
2847
- /** Batch-update multiple objects' visual styles in one render. Keyed by `data.id`. */
2848
- setObjectStyles,
2849
- /** Apply a visual style to all objects whose `data.type` matches. */
2850
- setObjectStyleByType
2851
- };
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
+ );
2978
+ }
2979
+
2980
+ // src/hooks/useCanvasEvents.ts
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;
2852
3036
  }
2853
3037
 
2854
3038
  // src/hooks/useCanvasEvents.ts
2855
- import { useEffect as useEffect3, useRef as useRef4 } from "react";
2856
- function useCanvasEvents(canvasRef, events) {
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;
2857
3044
  const eventsRef = useRef4(events);
2858
3045
  eventsRef.current = events;
2859
- useEffect3(() => {
2860
- const canvas = canvasRef.current;
3046
+ useEffect4(() => {
3047
+ const canvas = resolvedCanvasRef?.current;
2861
3048
  if (!canvas) return;
2862
3049
  const wrappers = /* @__PURE__ */ new Map();
2863
3050
  for (const key of Object.keys(eventsRef.current)) {
@@ -2873,12 +3060,16 @@ function useCanvasEvents(canvasRef, events) {
2873
3060
  canvas.off(name, handler);
2874
3061
  });
2875
3062
  };
2876
- }, [canvasRef]);
3063
+ }, [resolvedCanvasRef]);
2877
3064
  }
2878
3065
 
2879
3066
  // src/hooks/useCanvasTooltip.ts
2880
- import { useEffect as useEffect4, useRef as useRef5, useState as useState3 } from "react";
2881
- function useCanvasTooltip(canvasRef, options) {
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;
2882
3073
  const [state, setState] = useState3({
2883
3074
  visible: false,
2884
3075
  content: null,
@@ -2887,8 +3078,8 @@ function useCanvasTooltip(canvasRef, options) {
2887
3078
  const hoveredObjectRef = useRef5(null);
2888
3079
  const optionsRef = useRef5(options);
2889
3080
  optionsRef.current = options;
2890
- useEffect4(() => {
2891
- const canvas = canvasRef.current;
3081
+ useEffect5(() => {
3082
+ const canvas = resolvedCanvasRef?.current;
2892
3083
  if (!canvas) return;
2893
3084
  function calculatePosition(target) {
2894
3085
  const bounds = target.getBoundingRect();
@@ -2936,19 +3127,24 @@ function useCanvasTooltip(canvasRef, options) {
2936
3127
  canvas.off("after:render", updatePosition);
2937
3128
  canvas.off("mouse:wheel", updatePosition);
2938
3129
  };
2939
- }, [canvasRef]);
3130
+ }, [resolvedCanvasRef]);
2940
3131
  return state;
2941
3132
  }
2942
3133
 
2943
3134
  // src/hooks/useCanvasClick.ts
2944
- import { useEffect as useEffect5, useRef as useRef6 } from "react";
2945
- function useCanvasClick(canvasRef, onClick, options) {
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;
2946
3142
  const onClickRef = useRef6(onClick);
2947
3143
  onClickRef.current = onClick;
2948
3144
  const optionsRef = useRef6(options);
2949
3145
  optionsRef.current = options;
2950
- useEffect5(() => {
2951
- const canvas = canvasRef.current;
3146
+ useEffect6(() => {
3147
+ const canvas = resolvedCanvasRef?.current;
2952
3148
  if (!canvas) return;
2953
3149
  let mouseDown = null;
2954
3150
  let isPanning = false;
@@ -2988,24 +3184,26 @@ function useCanvasClick(canvasRef, onClick, options) {
2988
3184
  canvas.off("mouse:move", handleMouseMove);
2989
3185
  canvas.off("mouse:up", handleMouseUp);
2990
3186
  };
2991
- }, [canvasRef]);
3187
+ }, [resolvedCanvasRef]);
2992
3188
  }
2993
3189
 
2994
3190
  // src/overlay/ObjectOverlay.tsx
2995
- import { useEffect as useEffect6, useRef as useRef7 } from "react";
3191
+ import { useEffect as useEffect7, useRef as useRef7 } from "react";
2996
3192
  import { Stack } from "@mui/material";
2997
3193
  import { util as util4 } from "fabric";
2998
- import { jsx as jsx2 } from "react/jsx-runtime";
3194
+ import { jsx as jsx4 } from "react/jsx-runtime";
2999
3195
  function ObjectOverlay({
3000
- canvasRef,
3196
+ canvasRef: canvasRefProp,
3001
3197
  object,
3002
3198
  sx,
3003
3199
  children,
3004
3200
  ...rest
3005
3201
  }) {
3202
+ const contextCanvasRef = useCanvasRef();
3203
+ const canvasRef = canvasRefProp ?? contextCanvasRef;
3006
3204
  const stackRef = useRef7(null);
3007
- useEffect6(() => {
3008
- const canvas = canvasRef.current;
3205
+ useEffect7(() => {
3206
+ const canvas = canvasRef?.current;
3009
3207
  if (!canvas || !object) return;
3010
3208
  function update() {
3011
3209
  const el = stackRef.current;
@@ -3039,7 +3237,7 @@ function ObjectOverlay({
3039
3237
  };
3040
3238
  }, [canvasRef, object]);
3041
3239
  if (!object) return null;
3042
- return /* @__PURE__ */ jsx2(
3240
+ return /* @__PURE__ */ jsx4(
3043
3241
  Stack,
3044
3242
  {
3045
3243
  ref: stackRef,
@@ -3059,8 +3257,8 @@ function ObjectOverlay({
3059
3257
 
3060
3258
  // src/overlay/OverlayContent.tsx
3061
3259
  import { Stack as Stack2 } from "@mui/material";
3062
- import { useEffect as useEffect7, useRef as useRef8 } from "react";
3063
- import { jsx as jsx3 } from "react/jsx-runtime";
3260
+ import { useEffect as useEffect8, useRef as useRef8 } from "react";
3261
+ import { jsx as jsx5 } from "react/jsx-runtime";
3064
3262
  function OverlayContent({
3065
3263
  children,
3066
3264
  padding = 4,
@@ -3070,7 +3268,7 @@ function OverlayContent({
3070
3268
  }) {
3071
3269
  const outerRef = useRef8(null);
3072
3270
  const innerRef = useRef8(null);
3073
- useEffect7(() => {
3271
+ useEffect8(() => {
3074
3272
  const outer = outerRef.current;
3075
3273
  const inner = innerRef.current;
3076
3274
  if (!outer || !inner) return;
@@ -3099,7 +3297,7 @@ function OverlayContent({
3099
3297
  fit();
3100
3298
  return () => observer.disconnect();
3101
3299
  }, [padding, maxScale]);
3102
- return /* @__PURE__ */ jsx3(
3300
+ return /* @__PURE__ */ jsx5(
3103
3301
  Stack2,
3104
3302
  {
3105
3303
  ref: outerRef,
@@ -3112,7 +3310,7 @@ function OverlayContent({
3112
3310
  ...sx
3113
3311
  },
3114
3312
  ...rest,
3115
- children: /* @__PURE__ */ jsx3(
3313
+ children: /* @__PURE__ */ jsx5(
3116
3314
  Stack2,
3117
3315
  {
3118
3316
  ref: innerRef,
@@ -3131,8 +3329,8 @@ function OverlayContent({
3131
3329
 
3132
3330
  // src/overlay/FixedSizeContent.tsx
3133
3331
  import { Stack as Stack3 } from "@mui/material";
3134
- import { useEffect as useEffect8, useRef as useRef9 } from "react";
3135
- import { jsx as jsx4 } from "react/jsx-runtime";
3332
+ import { useEffect as useEffect9, useRef as useRef9 } from "react";
3333
+ import { jsx as jsx6 } from "react/jsx-runtime";
3136
3334
  function FixedSizeContent({
3137
3335
  children,
3138
3336
  hideOnOverflow = true,
@@ -3142,7 +3340,7 @@ function FixedSizeContent({
3142
3340
  }) {
3143
3341
  const ref = useRef9(null);
3144
3342
  const totalContentHeightRef = useRef9(0);
3145
- useEffect8(() => {
3343
+ useEffect9(() => {
3146
3344
  const el = ref.current;
3147
3345
  if (!el) return;
3148
3346
  let clipAncestor = el.parentElement;
@@ -3179,7 +3377,7 @@ function FixedSizeContent({
3179
3377
  check();
3180
3378
  return () => observer.disconnect();
3181
3379
  }, [hideOnOverflow, truncationPadding]);
3182
- return /* @__PURE__ */ jsx4(
3380
+ return /* @__PURE__ */ jsx6(
3183
3381
  Stack3,
3184
3382
  {
3185
3383
  ref,
@@ -3205,8 +3403,8 @@ function FixedSizeContent({
3205
3403
 
3206
3404
  // src/overlay/OverlayBadge.tsx
3207
3405
  import { Stack as Stack4 } from "@mui/material";
3208
- import { useEffect as useEffect9, useRef as useRef10 } from "react";
3209
- import { jsx as jsx5 } from "react/jsx-runtime";
3406
+ import { useEffect as useEffect10, useRef as useRef10 } from "react";
3407
+ import { jsx as jsx7 } from "react/jsx-runtime";
3210
3408
  function toPx(v) {
3211
3409
  if (v === void 0) return void 0;
3212
3410
  return typeof v === "number" ? `${v}px` : v;
@@ -3250,7 +3448,7 @@ function OverlayBadge({
3250
3448
  }) {
3251
3449
  const ref = useRef10(null);
3252
3450
  const baseSize = useRef10(null);
3253
- useEffect9(() => {
3451
+ useEffect10(() => {
3254
3452
  const el = ref.current;
3255
3453
  if (!el) return;
3256
3454
  const ancestor = el.parentElement;
@@ -3302,7 +3500,7 @@ function OverlayBadge({
3302
3500
  bottom: toPx(bottom),
3303
3501
  left: toPx(left)
3304
3502
  };
3305
- return /* @__PURE__ */ jsx5(
3503
+ return /* @__PURE__ */ jsx7(
3306
3504
  Stack4,
3307
3505
  {
3308
3506
  ref,
@@ -3340,6 +3538,7 @@ export {
3340
3538
  DEFAULT_DRAG_SHAPE_STYLE,
3341
3539
  DEFAULT_GUIDELINE_SHAPE_STYLE,
3342
3540
  DEFAULT_SHAPE_STYLE,
3541
+ EditCanvasProvider,
3343
3542
  Canvas2 as FabricCanvas,
3344
3543
  FabricImage3 as FabricImage,
3345
3544
  FabricObject5 as FabricObject,
@@ -3350,6 +3549,7 @@ export {
3350
3549
  Point9 as Point,
3351
3550
  Polygon5 as Polygon,
3352
3551
  Rect6 as Rect,
3552
+ ViewCanvasProvider,
3353
3553
  createCircle,
3354
3554
  createCircleAtPoint,
3355
3555
  createHistoryTracker,
@@ -3390,9 +3590,12 @@ export {
3390
3590
  snapCursorPoint,
3391
3591
  useCanvasClick,
3392
3592
  useCanvasEvents,
3593
+ useCanvasRef,
3393
3594
  useCanvasTooltip,
3394
3595
  useEditCanvas,
3596
+ useEditCanvasContext,
3395
3597
  useViewCanvas,
3598
+ useViewCanvasContext,
3396
3599
  util5 as util
3397
3600
  };
3398
3601
  //# sourceMappingURL=index.js.map