@bwp-web/canvas 0.8.2 → 0.8.3

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