@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.cjs CHANGED
@@ -27,6 +27,7 @@ __export(index_exports, {
27
27
  DEFAULT_DRAG_SHAPE_STYLE: () => DEFAULT_DRAG_SHAPE_STYLE,
28
28
  DEFAULT_GUIDELINE_SHAPE_STYLE: () => DEFAULT_GUIDELINE_SHAPE_STYLE,
29
29
  DEFAULT_SHAPE_STYLE: () => DEFAULT_SHAPE_STYLE,
30
+ EditCanvasProvider: () => EditCanvasProvider,
30
31
  FabricCanvas: () => import_fabric19.Canvas,
31
32
  FabricImage: () => import_fabric19.FabricImage,
32
33
  FabricObject: () => import_fabric19.FabricObject,
@@ -37,6 +38,7 @@ __export(index_exports, {
37
38
  Point: () => import_fabric19.Point,
38
39
  Polygon: () => import_fabric19.Polygon,
39
40
  Rect: () => import_fabric19.Rect,
41
+ ViewCanvasProvider: () => ViewCanvasProvider,
40
42
  createCircle: () => createCircle,
41
43
  createCircleAtPoint: () => createCircleAtPoint,
42
44
  createHistoryTracker: () => createHistoryTracker,
@@ -77,9 +79,12 @@ __export(index_exports, {
77
79
  snapCursorPoint: () => snapCursorPoint,
78
80
  useCanvasClick: () => useCanvasClick,
79
81
  useCanvasEvents: () => useCanvasEvents,
82
+ useCanvasRef: () => useCanvasRef,
80
83
  useCanvasTooltip: () => useCanvasTooltip,
81
84
  useEditCanvas: () => useEditCanvas,
85
+ useEditCanvasContext: () => useEditCanvasContext,
82
86
  useViewCanvas: () => useViewCanvas,
87
+ useViewCanvasContext: () => useViewCanvasContext,
83
88
  util: () => import_fabric19.util
84
89
  });
85
90
  module.exports = __toCommonJS(index_exports);
@@ -2389,6 +2394,10 @@ async function loadCanvas(canvas, json, options) {
2389
2394
  });
2390
2395
  bg.setCoords();
2391
2396
  }
2397
+ if (bg.filters?.some((f) => f instanceof import_fabric16.filters.Invert)) {
2398
+ bg.filters = bg.filters.filter((f) => !(f instanceof import_fabric16.filters.Invert));
2399
+ bg.applyFilters();
2400
+ }
2392
2401
  }
2393
2402
  if (options?.filter) {
2394
2403
  const toRemove = [];
@@ -2520,6 +2529,7 @@ function useEditCanvas(options) {
2520
2529
  const vertexEditCleanupRef = (0, import_react3.useRef)(null);
2521
2530
  const keyboardCleanupRef = (0, import_react3.useRef)(null);
2522
2531
  const historyRef = (0, import_react3.useRef)(null);
2532
+ const isInitialLoadRef = (0, import_react3.useRef)(false);
2523
2533
  const optionsRef = (0, import_react3.useRef)(options);
2524
2534
  optionsRef.current = options;
2525
2535
  const savedSelectabilityRef = (0, import_react3.useRef)(
@@ -2532,6 +2542,11 @@ function useEditCanvas(options) {
2532
2542
  const [isDirty, setIsDirty] = (0, import_react3.useState)(false);
2533
2543
  const [canUndo, setCanUndo] = (0, import_react3.useState)(false);
2534
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
+ );
2535
2550
  const setMode = (0, import_react3.useCallback)((setup) => {
2536
2551
  vertexEditCleanupRef.current?.();
2537
2552
  vertexEditCleanupRef.current = null;
@@ -2616,11 +2631,14 @@ function useEditCanvas(options) {
2616
2631
  canvas.on("selection:created", (e) => setSelected(e.selected ?? []));
2617
2632
  canvas.on("selection:updated", (e) => setSelected(e.selected ?? []));
2618
2633
  canvas.on("selection:cleared", () => setSelected([]));
2619
- if (opts?.trackChanges) {
2620
- canvas.on("object:added", () => setIsDirty(true));
2621
- canvas.on("object:removed", () => setIsDirty(true));
2622
- canvas.on("object:modified", () => setIsDirty(true));
2623
- canvas.on("background:modified", () => setIsDirty(true));
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);
2624
2642
  }
2625
2643
  if (opts?.history) {
2626
2644
  const syncHistoryState = () => {
@@ -2658,8 +2676,31 @@ function useEditCanvas(options) {
2658
2676
  }
2659
2677
  }
2660
2678
  function invokeOnReady() {
2661
- const onReadyResult = opts?.onReady?.(canvas);
2662
- Promise.resolve(onReadyResult).then(() => {
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
+ }
2663
2704
  if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {
2664
2705
  fitViewportToBackground(canvas);
2665
2706
  syncZoom(canvasRef, setZoom);
@@ -2689,37 +2730,25 @@ function useEditCanvas(options) {
2689
2730
  alignmentCleanupRef.current = null;
2690
2731
  }
2691
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
+ }, []);
2692
2745
  const setViewportMode = (0, import_react3.useCallback)((mode) => {
2693
2746
  viewportRef.current?.setMode(mode);
2694
2747
  setViewportModeState(mode);
2695
2748
  }, []);
2696
2749
  const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
2697
- const setBackground = (0, import_react3.useCallback)(
2698
- async (url, bgOpts) => {
2699
- const canvas = canvasRef.current;
2700
- if (!canvas) throw new Error("Canvas not ready");
2701
- const opts = optionsRef.current;
2702
- const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
2703
- const img = await setBackgroundImage(canvas, url, resizeOpts);
2704
- if (opts?.autoFitToBackground !== false) {
2705
- fitViewportToBackground(canvas);
2706
- syncZoom(canvasRef, setZoom);
2707
- }
2708
- return img;
2709
- },
2710
- []
2711
- );
2712
- return {
2713
- /** Pass this to `<Canvas onReady={...} />` */
2714
- onReady,
2715
- /** Ref to the underlying Fabric canvas instance. */
2716
- canvasRef,
2717
- /** Current zoom level (reactive). */
2718
- zoom,
2719
- /** Currently selected objects (reactive). */
2720
- selected,
2721
- /** Viewport controls. */
2722
- viewport: {
2750
+ const viewport = (0, import_react3.useMemo)(
2751
+ () => ({
2723
2752
  /** Current viewport mode (reactive). */
2724
2753
  mode: viewportMode,
2725
2754
  /** Switch between 'select' and 'pan' viewport modes. */
@@ -2734,61 +2763,125 @@ function useEditCanvas(options) {
2734
2763
  panToObject,
2735
2764
  /** Zoom and pan to fit a specific object in the viewport. */
2736
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;
2737
2805
  },
2738
- /** Whether vertex edit mode is currently active (reactive). */
2739
- isEditingVertices,
2740
- /**
2741
- * Activate an interaction mode or return to select mode.
2742
- *
2743
- * Pass a setup function to activate a creation mode:
2744
- * ```ts
2745
- * canvas.setMode((c, viewport) =>
2746
- * enableClickToCreate(c, factory, { viewport })
2747
- * );
2748
- * ```
2749
- *
2750
- * Pass `null` to deactivate and return to select mode:
2751
- * ```ts
2752
- * canvas.setMode(null);
2753
- * ```
2754
- */
2755
- setMode,
2756
- /**
2757
- * Set a background image from a URL. Automatically resizes if the image
2758
- * exceeds the configured limits (opt out via `backgroundResize: false`),
2759
- * and fits the viewport after setting if `autoFitToBackground` is enabled.
2760
- *
2761
- * Pass `{ preserveContrast: true }` to keep the current background contrast
2762
- * when replacing the image.
2763
- */
2764
- setBackground,
2765
- /** Whether the canvas has been modified since the last `resetDirty()` call. Requires `trackChanges: true`. */
2766
- isDirty,
2767
- /** Reset the dirty flag (e.g., after a successful save). */
2768
- resetDirty: (0, import_react3.useCallback)(() => setIsDirty(false), []),
2769
- /** Manually mark the canvas as dirty (e.g., after a custom operation not tracked automatically). */
2770
- markDirty: (0, import_react3.useCallback)(() => setIsDirty(true), []),
2771
- /** Undo the last change. Requires `history: true`. */
2772
- undo: (0, import_react3.useCallback)(async () => {
2773
- const h = historyRef.current;
2774
- if (!h) return;
2775
- await h.undo();
2776
- setCanUndo(h.canUndo());
2777
- setCanRedo(h.canRedo());
2778
- }, []),
2779
- /** Redo a previously undone change. Requires `history: true`. */
2780
- redo: (0, import_react3.useCallback)(async () => {
2781
- const h = historyRef.current;
2782
- if (!h) return;
2783
- await h.redo();
2784
- setCanUndo(h.canUndo());
2785
- setCanRedo(h.canRedo());
2786
- }, []),
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
- };
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
+ );
2792
2885
  }
2793
2886
 
2794
2887
  // src/hooks/useViewCanvas.ts
@@ -2805,6 +2898,8 @@ function useViewCanvas(options) {
2805
2898
  const optionsRef = (0, import_react4.useRef)(options);
2806
2899
  optionsRef.current = options;
2807
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);
2808
2903
  const onReady = (0, import_react4.useCallback)(
2809
2904
  (canvas) => {
2810
2905
  canvasRef.current = canvas;
@@ -2831,20 +2926,57 @@ function useViewCanvas(options) {
2831
2926
  canvas.on("mouse:wheel", () => {
2832
2927
  setZoom(canvas.getZoom());
2833
2928
  });
2834
- const onReadyResult = opts?.onReady?.(canvas);
2835
- if (opts?.autoFitToBackground !== false) {
2836
- Promise.resolve(onReadyResult).then(() => {
2837
- if (canvas.backgroundImage) {
2838
- fitViewportToBackground(canvas);
2839
- syncZoom(canvasRef, setZoom);
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);
2840
2941
  }
2841
- });
2842
- }
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
+ });
2843
2955
  },
2844
2956
  // onReady and panAndZoom are intentionally excluded — we only initialize once
2845
2957
  []
2846
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]);
2847
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
+ );
2848
2980
  const findObject = (id) => {
2849
2981
  const c = canvasRef.current;
2850
2982
  if (!c) return void 0;
@@ -2860,9 +2992,9 @@ function useViewCanvas(options) {
2860
2992
  (styles) => {
2861
2993
  const c = canvasRef.current;
2862
2994
  if (!c) return;
2863
- const objects = c.getObjects();
2995
+ const objects2 = c.getObjects();
2864
2996
  const idMap = /* @__PURE__ */ new Map();
2865
- for (const obj of objects) {
2997
+ for (const obj of objects2) {
2866
2998
  if (obj.data?.id) idMap.set(obj.data.id, obj);
2867
2999
  }
2868
3000
  let updated = false;
@@ -2892,42 +3024,101 @@ function useViewCanvas(options) {
2892
3024
  },
2893
3025
  []
2894
3026
  );
2895
- return {
2896
- /** Pass this to `<Canvas onReady={...} />` */
2897
- onReady,
2898
- /** Ref to the underlying Fabric canvas instance. */
2899
- canvasRef,
2900
- /** Current zoom level (reactive). */
2901
- zoom,
2902
- /** Viewport controls. */
2903
- viewport: {
2904
- /** Reset viewport to default (no pan, zoom = 1), or fit to background if one is set. */
2905
- reset: resetViewport2,
2906
- /** Zoom in toward the canvas center. Default step: 0.2. */
2907
- zoomIn,
2908
- /** Zoom out from the canvas center. Default step: 0.2. */
2909
- zoomOut,
2910
- /** Pan the viewport to center on a specific object. */
2911
- panToObject,
2912
- /** Zoom and pan to fit a specific object in the viewport. */
2913
- zoomToFit
2914
- },
2915
- /** Update a single object's visual style by its `data.id`. */
2916
- setObjectStyle,
2917
- /** Batch-update multiple objects' visual styles in one render. Keyed by `data.id`. */
2918
- setObjectStyles,
2919
- /** Apply a visual style to all objects whose `data.type` matches. */
2920
- setObjectStyleByType
2921
- };
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
+ );
2922
3052
  }
2923
3053
 
2924
3054
  // src/hooks/useCanvasEvents.ts
3055
+ var import_react7 = require("react");
3056
+
3057
+ // src/context/ViewCanvasContext.tsx
2925
3058
  var import_react5 = require("react");
2926
- function useCanvasEvents(canvasRef, events) {
2927
- const eventsRef = (0, import_react5.useRef)(events);
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);
2928
3119
  eventsRef.current = events;
2929
- (0, import_react5.useEffect)(() => {
2930
- const canvas = canvasRef.current;
3120
+ (0, import_react7.useEffect)(() => {
3121
+ const canvas = resolvedCanvasRef?.current;
2931
3122
  if (!canvas) return;
2932
3123
  const wrappers = /* @__PURE__ */ new Map();
2933
3124
  for (const key of Object.keys(eventsRef.current)) {
@@ -2943,22 +3134,26 @@ function useCanvasEvents(canvasRef, events) {
2943
3134
  canvas.off(name, handler);
2944
3135
  });
2945
3136
  };
2946
- }, [canvasRef]);
3137
+ }, [resolvedCanvasRef]);
2947
3138
  }
2948
3139
 
2949
3140
  // src/hooks/useCanvasTooltip.ts
2950
- var import_react6 = require("react");
2951
- function useCanvasTooltip(canvasRef, options) {
2952
- const [state, setState] = (0, import_react6.useState)({
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)({
2953
3148
  visible: false,
2954
3149
  content: null,
2955
3150
  position: { x: 0, y: 0 }
2956
3151
  });
2957
- const hoveredObjectRef = (0, import_react6.useRef)(null);
2958
- const optionsRef = (0, import_react6.useRef)(options);
3152
+ const hoveredObjectRef = (0, import_react8.useRef)(null);
3153
+ const optionsRef = (0, import_react8.useRef)(options);
2959
3154
  optionsRef.current = options;
2960
- (0, import_react6.useEffect)(() => {
2961
- const canvas = canvasRef.current;
3155
+ (0, import_react8.useEffect)(() => {
3156
+ const canvas = resolvedCanvasRef?.current;
2962
3157
  if (!canvas) return;
2963
3158
  function calculatePosition(target) {
2964
3159
  const bounds = target.getBoundingRect();
@@ -3006,19 +3201,24 @@ function useCanvasTooltip(canvasRef, options) {
3006
3201
  canvas.off("after:render", updatePosition);
3007
3202
  canvas.off("mouse:wheel", updatePosition);
3008
3203
  };
3009
- }, [canvasRef]);
3204
+ }, [resolvedCanvasRef]);
3010
3205
  return state;
3011
3206
  }
3012
3207
 
3013
3208
  // src/hooks/useCanvasClick.ts
3014
- var import_react7 = require("react");
3015
- function useCanvasClick(canvasRef, onClick, options) {
3016
- const onClickRef = (0, import_react7.useRef)(onClick);
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);
3017
3217
  onClickRef.current = onClick;
3018
- const optionsRef = (0, import_react7.useRef)(options);
3218
+ const optionsRef = (0, import_react9.useRef)(options);
3019
3219
  optionsRef.current = options;
3020
- (0, import_react7.useEffect)(() => {
3021
- const canvas = canvasRef.current;
3220
+ (0, import_react9.useEffect)(() => {
3221
+ const canvas = resolvedCanvasRef?.current;
3022
3222
  if (!canvas) return;
3023
3223
  let mouseDown = null;
3024
3224
  let isPanning = false;
@@ -3058,24 +3258,26 @@ function useCanvasClick(canvasRef, onClick, options) {
3058
3258
  canvas.off("mouse:move", handleMouseMove);
3059
3259
  canvas.off("mouse:up", handleMouseUp);
3060
3260
  };
3061
- }, [canvasRef]);
3261
+ }, [resolvedCanvasRef]);
3062
3262
  }
3063
3263
 
3064
3264
  // src/overlay/ObjectOverlay.tsx
3065
- var import_react8 = require("react");
3265
+ var import_react10 = require("react");
3066
3266
  var import_material = require("@mui/material");
3067
3267
  var import_fabric18 = require("fabric");
3068
- var import_jsx_runtime2 = require("react/jsx-runtime");
3268
+ var import_jsx_runtime4 = require("react/jsx-runtime");
3069
3269
  function ObjectOverlay({
3070
- canvasRef,
3270
+ canvasRef: canvasRefProp,
3071
3271
  object,
3072
3272
  sx,
3073
3273
  children,
3074
3274
  ...rest
3075
3275
  }) {
3076
- const stackRef = (0, import_react8.useRef)(null);
3077
- (0, import_react8.useEffect)(() => {
3078
- const canvas = canvasRef.current;
3276
+ const contextCanvasRef = useCanvasRef();
3277
+ const canvasRef = canvasRefProp ?? contextCanvasRef;
3278
+ const stackRef = (0, import_react10.useRef)(null);
3279
+ (0, import_react10.useEffect)(() => {
3280
+ const canvas = canvasRef?.current;
3079
3281
  if (!canvas || !object) return;
3080
3282
  function update() {
3081
3283
  const el = stackRef.current;
@@ -3109,7 +3311,7 @@ function ObjectOverlay({
3109
3311
  };
3110
3312
  }, [canvasRef, object]);
3111
3313
  if (!object) return null;
3112
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
3314
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3113
3315
  import_material.Stack,
3114
3316
  {
3115
3317
  ref: stackRef,
@@ -3129,8 +3331,8 @@ function ObjectOverlay({
3129
3331
 
3130
3332
  // src/overlay/OverlayContent.tsx
3131
3333
  var import_material2 = require("@mui/material");
3132
- var import_react9 = require("react");
3133
- var import_jsx_runtime3 = require("react/jsx-runtime");
3334
+ var import_react11 = require("react");
3335
+ var import_jsx_runtime5 = require("react/jsx-runtime");
3134
3336
  function OverlayContent({
3135
3337
  children,
3136
3338
  padding = 4,
@@ -3138,9 +3340,9 @@ function OverlayContent({
3138
3340
  sx,
3139
3341
  ...rest
3140
3342
  }) {
3141
- const outerRef = (0, import_react9.useRef)(null);
3142
- const innerRef = (0, import_react9.useRef)(null);
3143
- (0, import_react9.useEffect)(() => {
3343
+ const outerRef = (0, import_react11.useRef)(null);
3344
+ const innerRef = (0, import_react11.useRef)(null);
3345
+ (0, import_react11.useEffect)(() => {
3144
3346
  const outer = outerRef.current;
3145
3347
  const inner = innerRef.current;
3146
3348
  if (!outer || !inner) return;
@@ -3169,7 +3371,7 @@ function OverlayContent({
3169
3371
  fit();
3170
3372
  return () => observer.disconnect();
3171
3373
  }, [padding, maxScale]);
3172
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3374
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3173
3375
  import_material2.Stack,
3174
3376
  {
3175
3377
  ref: outerRef,
@@ -3182,7 +3384,7 @@ function OverlayContent({
3182
3384
  ...sx
3183
3385
  },
3184
3386
  ...rest,
3185
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3387
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3186
3388
  import_material2.Stack,
3187
3389
  {
3188
3390
  ref: innerRef,
@@ -3201,8 +3403,8 @@ function OverlayContent({
3201
3403
 
3202
3404
  // src/overlay/FixedSizeContent.tsx
3203
3405
  var import_material3 = require("@mui/material");
3204
- var import_react10 = require("react");
3205
- var import_jsx_runtime4 = require("react/jsx-runtime");
3406
+ var import_react12 = require("react");
3407
+ var import_jsx_runtime6 = require("react/jsx-runtime");
3206
3408
  function FixedSizeContent({
3207
3409
  children,
3208
3410
  hideOnOverflow = true,
@@ -3210,9 +3412,9 @@ function FixedSizeContent({
3210
3412
  sx,
3211
3413
  ...rest
3212
3414
  }) {
3213
- const ref = (0, import_react10.useRef)(null);
3214
- const totalContentHeightRef = (0, import_react10.useRef)(0);
3215
- (0, import_react10.useEffect)(() => {
3415
+ const ref = (0, import_react12.useRef)(null);
3416
+ const totalContentHeightRef = (0, import_react12.useRef)(0);
3417
+ (0, import_react12.useEffect)(() => {
3216
3418
  const el = ref.current;
3217
3419
  if (!el) return;
3218
3420
  let clipAncestor = el.parentElement;
@@ -3249,7 +3451,7 @@ function FixedSizeContent({
3249
3451
  check();
3250
3452
  return () => observer.disconnect();
3251
3453
  }, [hideOnOverflow, truncationPadding]);
3252
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3454
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3253
3455
  import_material3.Stack,
3254
3456
  {
3255
3457
  ref,
@@ -3275,8 +3477,8 @@ function FixedSizeContent({
3275
3477
 
3276
3478
  // src/overlay/OverlayBadge.tsx
3277
3479
  var import_material4 = require("@mui/material");
3278
- var import_react11 = require("react");
3279
- var import_jsx_runtime5 = require("react/jsx-runtime");
3480
+ var import_react13 = require("react");
3481
+ var import_jsx_runtime7 = require("react/jsx-runtime");
3280
3482
  function toPx(v) {
3281
3483
  if (v === void 0) return void 0;
3282
3484
  return typeof v === "number" ? `${v}px` : v;
@@ -3318,9 +3520,9 @@ function OverlayBadge({
3318
3520
  sx,
3319
3521
  ...rest
3320
3522
  }) {
3321
- const ref = (0, import_react11.useRef)(null);
3322
- const baseSize = (0, import_react11.useRef)(null);
3323
- (0, import_react11.useEffect)(() => {
3523
+ const ref = (0, import_react13.useRef)(null);
3524
+ const baseSize = (0, import_react13.useRef)(null);
3525
+ (0, import_react13.useEffect)(() => {
3324
3526
  const el = ref.current;
3325
3527
  if (!el) return;
3326
3528
  const ancestor = el.parentElement;
@@ -3372,7 +3574,7 @@ function OverlayBadge({
3372
3574
  bottom: toPx(bottom),
3373
3575
  left: toPx(left)
3374
3576
  };
3375
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3577
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3376
3578
  import_material4.Stack,
3377
3579
  {
3378
3580
  ref,
@@ -3403,6 +3605,7 @@ var import_fabric19 = require("fabric");
3403
3605
  DEFAULT_DRAG_SHAPE_STYLE,
3404
3606
  DEFAULT_GUIDELINE_SHAPE_STYLE,
3405
3607
  DEFAULT_SHAPE_STYLE,
3608
+ EditCanvasProvider,
3406
3609
  FabricCanvas,
3407
3610
  FabricImage,
3408
3611
  FabricObject,
@@ -3413,6 +3616,7 @@ var import_fabric19 = require("fabric");
3413
3616
  Point,
3414
3617
  Polygon,
3415
3618
  Rect,
3619
+ ViewCanvasProvider,
3416
3620
  createCircle,
3417
3621
  createCircleAtPoint,
3418
3622
  createHistoryTracker,
@@ -3453,9 +3657,12 @@ var import_fabric19 = require("fabric");
3453
3657
  snapCursorPoint,
3454
3658
  useCanvasClick,
3455
3659
  useCanvasEvents,
3660
+ useCanvasRef,
3456
3661
  useCanvasTooltip,
3457
3662
  useEditCanvas,
3663
+ useEditCanvasContext,
3458
3664
  useViewCanvas,
3665
+ useViewCanvasContext,
3459
3666
  util
3460
3667
  });
3461
3668
  //# sourceMappingURL=index.cjs.map