@bwp-web/canvas 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  FixedSizeContent: () => FixedSizeContent,
35
35
  ObjectOverlay: () => ObjectOverlay,
36
36
  OverlayBadge: () => OverlayBadge,
37
+ OverlayContainer: () => OverlayContainer,
37
38
  OverlayContent: () => OverlayContent,
38
39
  Point: () => import_fabric19.Point,
39
40
  Polygon: () => import_fabric19.Polygon,
@@ -3528,10 +3529,61 @@ function useViewCanvasState() {
3528
3529
  }
3529
3530
 
3530
3531
  // src/overlay/ObjectOverlay.tsx
3532
+ var import_react12 = require("react");
3533
+ var import_material2 = require("@mui/material");
3534
+ var import_fabric18 = require("fabric");
3535
+
3536
+ // src/overlay/OverlayContainer.tsx
3531
3537
  var import_react11 = require("react");
3532
3538
  var import_material = require("@mui/material");
3533
- var import_fabric18 = require("fabric");
3534
3539
  var import_jsx_runtime4 = require("react/jsx-runtime");
3540
+ var OverlayContainerContext = (0, import_react11.createContext)(false);
3541
+ function useIsInsideOverlayContainer() {
3542
+ return (0, import_react11.useContext)(OverlayContainerContext);
3543
+ }
3544
+ function OverlayContainer({
3545
+ canvasRef: canvasRefProp,
3546
+ children
3547
+ }) {
3548
+ const contextCanvasRef = useCanvasRef();
3549
+ const canvasRef = canvasRefProp ?? contextCanvasRef;
3550
+ const containerRef = (0, import_react11.useRef)(null);
3551
+ (0, import_react11.useEffect)(() => {
3552
+ const canvas = canvasRef?.current;
3553
+ const el = containerRef.current;
3554
+ if (!canvas || !el) return;
3555
+ function updateContainer() {
3556
+ if (!canvas || !el) return;
3557
+ const vt = canvas.viewportTransform;
3558
+ if (!vt) return;
3559
+ el.style.transform = `translate(${vt[4]}px, ${vt[5]}px)`;
3560
+ }
3561
+ updateContainer();
3562
+ canvas.on("after:render", updateContainer);
3563
+ return () => {
3564
+ canvas.off("after:render", updateContainer);
3565
+ };
3566
+ }, [canvasRef]);
3567
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(OverlayContainerContext.Provider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3568
+ import_material.Stack,
3569
+ {
3570
+ ref: containerRef,
3571
+ sx: {
3572
+ position: "absolute",
3573
+ left: 0,
3574
+ top: 0,
3575
+ width: "100%",
3576
+ height: "100%",
3577
+ pointerEvents: "none",
3578
+ willChange: "transform"
3579
+ },
3580
+ children
3581
+ }
3582
+ ) });
3583
+ }
3584
+
3585
+ // src/overlay/ObjectOverlay.tsx
3586
+ var import_jsx_runtime5 = require("react/jsx-runtime");
3535
3587
  function ObjectOverlay({
3536
3588
  canvasRef: canvasRefProp,
3537
3589
  object,
@@ -3541,8 +3593,10 @@ function ObjectOverlay({
3541
3593
  }) {
3542
3594
  const contextCanvasRef = useCanvasRef();
3543
3595
  const canvasRef = canvasRefProp ?? contextCanvasRef;
3544
- const stackRef = (0, import_react11.useRef)(null);
3545
- (0, import_react11.useEffect)(() => {
3596
+ const insideContainer = useIsInsideOverlayContainer();
3597
+ const stackRef = (0, import_react12.useRef)(null);
3598
+ const prev = (0, import_react12.useRef)({ transform: "", w: "", h: "" });
3599
+ (0, import_react12.useEffect)(() => {
3546
3600
  const canvas = canvasRef?.current;
3547
3601
  if (!canvas || !object) return;
3548
3602
  function update() {
@@ -3554,15 +3608,32 @@ function ObjectOverlay({
3554
3608
  const center = object.getCenterPoint();
3555
3609
  const actualWidth = (object.width ?? 0) * (object.scaleX ?? 1);
3556
3610
  const actualHeight = (object.height ?? 0) * (object.scaleY ?? 1);
3557
- const screenCoords = import_fabric18.util.transformPoint(center, vt);
3558
3611
  const screenWidth = actualWidth * zoom;
3559
3612
  const screenHeight = actualHeight * zoom;
3560
3613
  const angle = object.angle ?? 0;
3561
- el.style.left = `${screenCoords.x - screenWidth / 2}px`;
3562
- el.style.top = `${screenCoords.y - screenHeight / 2}px`;
3563
- el.style.width = `${screenWidth}px`;
3564
- el.style.height = `${screenHeight}px`;
3565
- el.style.transform = angle !== 0 ? `rotate(${angle}deg)` : "";
3614
+ let tx;
3615
+ let ty;
3616
+ if (insideContainer) {
3617
+ tx = center.x * zoom - screenWidth / 2;
3618
+ ty = center.y * zoom - screenHeight / 2;
3619
+ } else {
3620
+ const screenCoords = import_fabric18.util.transformPoint(center, vt);
3621
+ tx = screenCoords.x - screenWidth / 2;
3622
+ ty = screenCoords.y - screenHeight / 2;
3623
+ }
3624
+ const transform = angle !== 0 ? `translate(${tx}px, ${ty}px) rotate(${angle}deg)` : `translate(${tx}px, ${ty}px)`;
3625
+ if (transform !== prev.current.transform) {
3626
+ el.style.transform = transform;
3627
+ prev.current.transform = transform;
3628
+ }
3629
+ const w = `${screenWidth}px`;
3630
+ const h = `${screenHeight}px`;
3631
+ if (prev.current.w !== w || prev.current.h !== h) {
3632
+ el.style.width = w;
3633
+ el.style.height = h;
3634
+ prev.current.w = w;
3635
+ prev.current.h = h;
3636
+ }
3566
3637
  }
3567
3638
  update();
3568
3639
  canvas.on("after:render", update);
@@ -3574,19 +3645,23 @@ function ObjectOverlay({
3574
3645
  object.off("moving", update);
3575
3646
  object.off("scaling", update);
3576
3647
  object.off("rotating", update);
3648
+ prev.current = { transform: "", w: "", h: "" };
3577
3649
  };
3578
- }, [canvasRef, object]);
3650
+ }, [canvasRef, object, insideContainer]);
3579
3651
  if (!object) return null;
3580
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3581
- import_material.Stack,
3652
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3653
+ import_material2.Stack,
3582
3654
  {
3583
3655
  ref: stackRef,
3584
3656
  sx: {
3585
3657
  position: "absolute",
3658
+ left: 0,
3659
+ top: 0,
3586
3660
  pointerEvents: "none",
3587
3661
  alignItems: "center",
3588
3662
  justifyContent: "center",
3589
3663
  zIndex: 1,
3664
+ willChange: "transform",
3590
3665
  ...sx
3591
3666
  },
3592
3667
  ...rest,
@@ -3596,28 +3671,28 @@ function ObjectOverlay({
3596
3671
  }
3597
3672
 
3598
3673
  // src/overlay/OverlayContent.tsx
3599
- var import_material2 = require("@mui/material");
3600
- var import_react12 = require("react");
3601
- var import_jsx_runtime5 = require("react/jsx-runtime");
3674
+ var import_material3 = require("@mui/material");
3675
+ var import_react13 = require("react");
3676
+ var import_jsx_runtime6 = require("react/jsx-runtime");
3602
3677
  function OverlayContent({
3603
3678
  children,
3604
- padding = 4,
3679
+ padding = 6,
3605
3680
  maxScale = 2,
3606
3681
  sx,
3607
3682
  ...rest
3608
3683
  }) {
3609
- const outerRef = (0, import_react12.useRef)(null);
3610
- const innerRef = (0, import_react12.useRef)(null);
3611
- (0, import_react12.useEffect)(() => {
3684
+ const outerRef = (0, import_react13.useRef)(null);
3685
+ const innerRef = (0, import_react13.useRef)(null);
3686
+ (0, import_react13.useEffect)(() => {
3612
3687
  const outer = outerRef.current;
3613
3688
  const inner = innerRef.current;
3614
3689
  if (!outer || !inner) return;
3690
+ let natW = 0;
3691
+ let natH = 0;
3615
3692
  function fit() {
3616
3693
  if (!outer || !inner) return;
3617
3694
  const containerW = outer.clientWidth;
3618
3695
  const containerH = outer.clientHeight;
3619
- const natW = inner.scrollWidth;
3620
- const natH = inner.scrollHeight;
3621
3696
  if (natW === 0 || natH === 0 || containerW <= 0 || containerH <= 0) {
3622
3697
  inner.style.transform = "";
3623
3698
  inner.style.removeProperty("--overlay-scale");
@@ -3631,14 +3706,25 @@ function OverlayContent({
3631
3706
  inner.style.transform = `scale(${scale})`;
3632
3707
  inner.style.setProperty("--overlay-scale", String(scale));
3633
3708
  }
3634
- const observer = new ResizeObserver(fit);
3709
+ const observer = new ResizeObserver((entries) => {
3710
+ for (const entry of entries) {
3711
+ if (entry.target === inner) {
3712
+ natW = inner.scrollWidth;
3713
+ natH = inner.scrollHeight;
3714
+ break;
3715
+ }
3716
+ }
3717
+ fit();
3718
+ });
3635
3719
  observer.observe(outer);
3636
3720
  observer.observe(inner);
3721
+ natW = inner.scrollWidth;
3722
+ natH = inner.scrollHeight;
3637
3723
  fit();
3638
3724
  return () => observer.disconnect();
3639
3725
  }, [padding, maxScale]);
3640
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3641
- import_material2.Stack,
3726
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3727
+ import_material3.Stack,
3642
3728
  {
3643
3729
  ref: outerRef,
3644
3730
  sx: {
@@ -3650,8 +3736,8 @@ function OverlayContent({
3650
3736
  ...sx
3651
3737
  },
3652
3738
  ...rest,
3653
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3654
- import_material2.Stack,
3739
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3740
+ import_material3.Stack,
3655
3741
  {
3656
3742
  ref: innerRef,
3657
3743
  sx: {
@@ -3668,9 +3754,9 @@ function OverlayContent({
3668
3754
  }
3669
3755
 
3670
3756
  // src/overlay/FixedSizeContent.tsx
3671
- var import_material3 = require("@mui/material");
3672
- var import_react13 = require("react");
3673
- var import_jsx_runtime6 = require("react/jsx-runtime");
3757
+ var import_material4 = require("@mui/material");
3758
+ var import_react14 = require("react");
3759
+ var import_jsx_runtime7 = require("react/jsx-runtime");
3674
3760
  function FixedSizeContent({
3675
3761
  children,
3676
3762
  hideOnOverflow = true,
@@ -3678,9 +3764,9 @@ function FixedSizeContent({
3678
3764
  sx,
3679
3765
  ...rest
3680
3766
  }) {
3681
- const ref = (0, import_react13.useRef)(null);
3682
- const totalContentHeightRef = (0, import_react13.useRef)(0);
3683
- (0, import_react13.useEffect)(() => {
3767
+ const ref = (0, import_react14.useRef)(null);
3768
+ const totalContentHeightRef = (0, import_react14.useRef)(0);
3769
+ (0, import_react14.useEffect)(() => {
3684
3770
  const el = ref.current;
3685
3771
  if (!el) return;
3686
3772
  let clipAncestor = el.parentElement;
@@ -3691,34 +3777,45 @@ function FixedSizeContent({
3691
3777
  if (!clipAncestor) return;
3692
3778
  const ancestor = clipAncestor;
3693
3779
  totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
3694
- function check() {
3695
- requestAnimationFrame(() => {
3696
- if (!el) return;
3697
- const containerRect = ancestor.getBoundingClientRect();
3698
- el.style.maxWidth = `${Math.max(0, containerRect.width - truncationPadding * 2)}px`;
3699
- if (!hideOnOverflow) return;
3700
- const fits = containerRect.height >= totalContentHeightRef.current;
3701
- if (fits && el.style.display === "none") {
3702
- el.style.display = "";
3703
- totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
3704
- if (containerRect.height < totalContentHeightRef.current) {
3705
- el.style.display = "none";
3706
- }
3707
- } else if (!fits && el.style.display !== "none") {
3780
+ let rafId = 0;
3781
+ function doCheck() {
3782
+ if (!el) return;
3783
+ const containerW = ancestor.clientWidth;
3784
+ const containerH = ancestor.clientHeight;
3785
+ el.style.maxWidth = `${Math.max(0, containerW - truncationPadding * 2)}px`;
3786
+ if (!hideOnOverflow) return;
3787
+ const fits = containerH >= totalContentHeightRef.current;
3788
+ if (fits && el.style.display === "none") {
3789
+ el.style.display = "";
3790
+ totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
3791
+ if (containerH < totalContentHeightRef.current) {
3708
3792
  el.style.display = "none";
3709
3793
  }
3710
- if (el.style.display !== "none") {
3711
- totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
3712
- }
3713
- });
3794
+ } else if (!fits && el.style.display !== "none") {
3795
+ el.style.display = "none";
3796
+ }
3797
+ if (el.style.display !== "none") {
3798
+ totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
3799
+ }
3800
+ }
3801
+ function scheduleCheck() {
3802
+ if (!rafId) {
3803
+ rafId = requestAnimationFrame(() => {
3804
+ rafId = 0;
3805
+ doCheck();
3806
+ });
3807
+ }
3714
3808
  }
3715
- const observer = new ResizeObserver(check);
3809
+ const observer = new ResizeObserver(scheduleCheck);
3716
3810
  observer.observe(ancestor);
3717
- check();
3718
- return () => observer.disconnect();
3811
+ scheduleCheck();
3812
+ return () => {
3813
+ if (rafId) cancelAnimationFrame(rafId);
3814
+ observer.disconnect();
3815
+ };
3719
3816
  }, [hideOnOverflow, truncationPadding]);
3720
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3721
- import_material3.Stack,
3817
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3818
+ import_material4.Stack,
3722
3819
  {
3723
3820
  ref,
3724
3821
  sx: {
@@ -3742,9 +3839,9 @@ function FixedSizeContent({
3742
3839
  }
3743
3840
 
3744
3841
  // src/overlay/OverlayBadge.tsx
3745
- var import_material4 = require("@mui/material");
3746
- var import_react14 = require("react");
3747
- var import_jsx_runtime7 = require("react/jsx-runtime");
3842
+ var import_material5 = require("@mui/material");
3843
+ var import_react15 = require("react");
3844
+ var import_jsx_runtime8 = require("react/jsx-runtime");
3748
3845
  function toPx(v) {
3749
3846
  if (v === void 0) return void 0;
3750
3847
  return typeof v === "number" ? `${v}px` : v;
@@ -3774,10 +3871,19 @@ function ellipsePosition(angleDeg) {
3774
3871
  function toNum(v) {
3775
3872
  return typeof v === "number" ? v : 0;
3776
3873
  }
3874
+ function readOverlayScale(el) {
3875
+ let node = el.parentElement;
3876
+ while (node) {
3877
+ const val = node.style.getPropertyValue("--overlay-scale");
3878
+ if (val) return parseFloat(val) || 1;
3879
+ node = node.parentElement;
3880
+ }
3881
+ return 1;
3882
+ }
3777
3883
  function OverlayBadge({
3778
3884
  children,
3779
- maxScale = 2,
3780
- minScale = 0.75,
3885
+ maxScale = 1.5,
3886
+ minScale = 0.5,
3781
3887
  top,
3782
3888
  right,
3783
3889
  bottom,
@@ -3786,41 +3892,47 @@ function OverlayBadge({
3786
3892
  sx,
3787
3893
  ...rest
3788
3894
  }) {
3789
- const ref = (0, import_react14.useRef)(null);
3790
- const baseSize = (0, import_react14.useRef)(null);
3791
- (0, import_react14.useEffect)(() => {
3895
+ const ref = (0, import_react15.useRef)(null);
3896
+ const baseSize = (0, import_react15.useRef)(null);
3897
+ (0, import_react15.useEffect)(() => {
3792
3898
  const el = ref.current;
3793
3899
  if (!el) return;
3794
3900
  const ancestor = el.parentElement;
3795
3901
  if (!ancestor) return;
3796
- function update() {
3797
- requestAnimationFrame(() => {
3798
- if (!el || !ancestor) return;
3799
- const containerW = ancestor.clientWidth;
3800
- const containerH = ancestor.clientHeight;
3801
- if (containerW <= 0 || containerH <= 0) {
3802
- el.style.transform = "";
3803
- return;
3804
- }
3805
- if (!baseSize.current) {
3806
- baseSize.current = { w: containerW, h: containerH };
3807
- }
3808
- const ratio = Math.min(
3809
- containerW / baseSize.current.w,
3810
- containerH / baseSize.current.h
3811
- );
3812
- const ownScale = Math.max(minScale, Math.min(ratio, maxScale));
3813
- const overlayScale = parseFloat(
3814
- getComputedStyle(el).getPropertyValue("--overlay-scale")
3815
- ) || 1;
3816
- const scale = `scale(${ownScale / overlayScale})`;
3817
- el.style.transform = circular ? `translate(-50%, -50%) ${scale}` : scale;
3818
- });
3902
+ let rafId = 0;
3903
+ function doUpdate() {
3904
+ if (!el || !ancestor) return;
3905
+ const containerW = ancestor.clientWidth;
3906
+ const containerH = ancestor.clientHeight;
3907
+ if (containerW <= 0 || containerH <= 0) {
3908
+ el.style.transform = "";
3909
+ return;
3910
+ }
3911
+ if (!baseSize.current) {
3912
+ baseSize.current = { w: containerW, h: containerH };
3913
+ }
3914
+ const ratio = Math.min(
3915
+ containerW / baseSize.current.w,
3916
+ containerH / baseSize.current.h
3917
+ );
3918
+ const ownScale = Math.max(minScale, Math.min(ratio, maxScale));
3919
+ const overlayScale = readOverlayScale(el);
3920
+ const scale = `scale(${ownScale / overlayScale})`;
3921
+ el.style.transform = circular ? `translate(-50%, -50%) ${scale}` : scale;
3819
3922
  }
3820
- const observer = new ResizeObserver(update);
3923
+ function scheduleUpdate() {
3924
+ if (!rafId) {
3925
+ rafId = requestAnimationFrame(() => {
3926
+ rafId = 0;
3927
+ doUpdate();
3928
+ });
3929
+ }
3930
+ }
3931
+ const observer = new ResizeObserver(scheduleUpdate);
3821
3932
  observer.observe(ancestor);
3822
- update();
3933
+ scheduleUpdate();
3823
3934
  return () => {
3935
+ if (rafId) cancelAnimationFrame(rafId);
3824
3936
  observer.disconnect();
3825
3937
  baseSize.current = null;
3826
3938
  };
@@ -3840,8 +3952,8 @@ function OverlayBadge({
3840
3952
  bottom: toPx(bottom),
3841
3953
  left: toPx(left)
3842
3954
  };
3843
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3844
- import_material4.Stack,
3955
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3956
+ import_material5.Stack,
3845
3957
  {
3846
3958
  ref,
3847
3959
  sx: {
@@ -3878,6 +3990,7 @@ var import_fabric19 = require("fabric");
3878
3990
  FixedSizeContent,
3879
3991
  ObjectOverlay,
3880
3992
  OverlayBadge,
3993
+ OverlayContainer,
3881
3994
  OverlayContent,
3882
3995
  Point,
3883
3996
  Polygon,