@bwp-web/canvas 1.0.0 → 1.1.1

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: "", z: "" });
3599
+ (0, import_react12.useEffect)(() => {
3546
3600
  const canvas = canvasRef?.current;
3547
3601
  if (!canvas || !object) return;
3548
3602
  function update() {
@@ -3554,15 +3608,37 @@ 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 z = String(zoom);
3630
+ if (prev.current.z !== z) {
3631
+ el.style.setProperty("--overlay-zoom", z);
3632
+ prev.current.z = z;
3633
+ }
3634
+ const w = `${screenWidth}px`;
3635
+ const h = `${screenHeight}px`;
3636
+ if (prev.current.w !== w || prev.current.h !== h) {
3637
+ el.style.width = w;
3638
+ el.style.height = h;
3639
+ prev.current.w = w;
3640
+ prev.current.h = h;
3641
+ }
3566
3642
  }
3567
3643
  update();
3568
3644
  canvas.on("after:render", update);
@@ -3574,19 +3650,23 @@ function ObjectOverlay({
3574
3650
  object.off("moving", update);
3575
3651
  object.off("scaling", update);
3576
3652
  object.off("rotating", update);
3653
+ prev.current = { transform: "", w: "", h: "", z: "" };
3577
3654
  };
3578
- }, [canvasRef, object]);
3655
+ }, [canvasRef, object, insideContainer]);
3579
3656
  if (!object) return null;
3580
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3581
- import_material.Stack,
3657
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3658
+ import_material2.Stack,
3582
3659
  {
3583
3660
  ref: stackRef,
3584
3661
  sx: {
3585
3662
  position: "absolute",
3663
+ left: 0,
3664
+ top: 0,
3586
3665
  pointerEvents: "none",
3587
3666
  alignItems: "center",
3588
3667
  justifyContent: "center",
3589
3668
  zIndex: 1,
3669
+ willChange: "transform",
3590
3670
  ...sx
3591
3671
  },
3592
3672
  ...rest,
@@ -3596,28 +3676,28 @@ function ObjectOverlay({
3596
3676
  }
3597
3677
 
3598
3678
  // 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");
3679
+ var import_material3 = require("@mui/material");
3680
+ var import_react13 = require("react");
3681
+ var import_jsx_runtime6 = require("react/jsx-runtime");
3602
3682
  function OverlayContent({
3603
3683
  children,
3604
- padding = 4,
3684
+ padding = 6,
3605
3685
  maxScale = 2,
3606
3686
  sx,
3607
3687
  ...rest
3608
3688
  }) {
3609
- const outerRef = (0, import_react12.useRef)(null);
3610
- const innerRef = (0, import_react12.useRef)(null);
3611
- (0, import_react12.useEffect)(() => {
3689
+ const outerRef = (0, import_react13.useRef)(null);
3690
+ const innerRef = (0, import_react13.useRef)(null);
3691
+ (0, import_react13.useEffect)(() => {
3612
3692
  const outer = outerRef.current;
3613
3693
  const inner = innerRef.current;
3614
3694
  if (!outer || !inner) return;
3695
+ let natW = 0;
3696
+ let natH = 0;
3615
3697
  function fit() {
3616
3698
  if (!outer || !inner) return;
3617
3699
  const containerW = outer.clientWidth;
3618
3700
  const containerH = outer.clientHeight;
3619
- const natW = inner.scrollWidth;
3620
- const natH = inner.scrollHeight;
3621
3701
  if (natW === 0 || natH === 0 || containerW <= 0 || containerH <= 0) {
3622
3702
  inner.style.transform = "";
3623
3703
  inner.style.removeProperty("--overlay-scale");
@@ -3631,14 +3711,25 @@ function OverlayContent({
3631
3711
  inner.style.transform = `scale(${scale})`;
3632
3712
  inner.style.setProperty("--overlay-scale", String(scale));
3633
3713
  }
3634
- const observer = new ResizeObserver(fit);
3714
+ const observer = new ResizeObserver((entries) => {
3715
+ for (const entry of entries) {
3716
+ if (entry.target === inner) {
3717
+ natW = inner.scrollWidth;
3718
+ natH = inner.scrollHeight;
3719
+ break;
3720
+ }
3721
+ }
3722
+ fit();
3723
+ });
3635
3724
  observer.observe(outer);
3636
3725
  observer.observe(inner);
3726
+ natW = inner.scrollWidth;
3727
+ natH = inner.scrollHeight;
3637
3728
  fit();
3638
3729
  return () => observer.disconnect();
3639
3730
  }, [padding, maxScale]);
3640
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3641
- import_material2.Stack,
3731
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3732
+ import_material3.Stack,
3642
3733
  {
3643
3734
  ref: outerRef,
3644
3735
  sx: {
@@ -3650,8 +3741,8 @@ function OverlayContent({
3650
3741
  ...sx
3651
3742
  },
3652
3743
  ...rest,
3653
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3654
- import_material2.Stack,
3744
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3745
+ import_material3.Stack,
3655
3746
  {
3656
3747
  ref: innerRef,
3657
3748
  sx: {
@@ -3668,9 +3759,9 @@ function OverlayContent({
3668
3759
  }
3669
3760
 
3670
3761
  // 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");
3762
+ var import_material4 = require("@mui/material");
3763
+ var import_react14 = require("react");
3764
+ var import_jsx_runtime7 = require("react/jsx-runtime");
3674
3765
  function FixedSizeContent({
3675
3766
  children,
3676
3767
  hideOnOverflow = true,
@@ -3678,9 +3769,9 @@ function FixedSizeContent({
3678
3769
  sx,
3679
3770
  ...rest
3680
3771
  }) {
3681
- const ref = (0, import_react13.useRef)(null);
3682
- const totalContentHeightRef = (0, import_react13.useRef)(0);
3683
- (0, import_react13.useEffect)(() => {
3772
+ const ref = (0, import_react14.useRef)(null);
3773
+ const totalContentHeightRef = (0, import_react14.useRef)(0);
3774
+ (0, import_react14.useEffect)(() => {
3684
3775
  const el = ref.current;
3685
3776
  if (!el) return;
3686
3777
  let clipAncestor = el.parentElement;
@@ -3691,34 +3782,45 @@ function FixedSizeContent({
3691
3782
  if (!clipAncestor) return;
3692
3783
  const ancestor = clipAncestor;
3693
3784
  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") {
3785
+ let rafId = 0;
3786
+ function doCheck() {
3787
+ if (!el) return;
3788
+ const containerW = ancestor.clientWidth;
3789
+ const containerH = ancestor.clientHeight;
3790
+ el.style.maxWidth = `${Math.max(0, containerW - truncationPadding * 2)}px`;
3791
+ if (!hideOnOverflow) return;
3792
+ const fits = containerH >= totalContentHeightRef.current;
3793
+ if (fits && el.style.display === "none") {
3794
+ el.style.display = "";
3795
+ totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
3796
+ if (containerH < totalContentHeightRef.current) {
3708
3797
  el.style.display = "none";
3709
3798
  }
3710
- if (el.style.display !== "none") {
3711
- totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
3712
- }
3713
- });
3799
+ } else if (!fits && el.style.display !== "none") {
3800
+ el.style.display = "none";
3801
+ }
3802
+ if (el.style.display !== "none") {
3803
+ totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
3804
+ }
3714
3805
  }
3715
- const observer = new ResizeObserver(check);
3806
+ function scheduleCheck() {
3807
+ if (!rafId) {
3808
+ rafId = requestAnimationFrame(() => {
3809
+ rafId = 0;
3810
+ doCheck();
3811
+ });
3812
+ }
3813
+ }
3814
+ const observer = new ResizeObserver(scheduleCheck);
3716
3815
  observer.observe(ancestor);
3717
- check();
3718
- return () => observer.disconnect();
3816
+ scheduleCheck();
3817
+ return () => {
3818
+ if (rafId) cancelAnimationFrame(rafId);
3819
+ observer.disconnect();
3820
+ };
3719
3821
  }, [hideOnOverflow, truncationPadding]);
3720
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3721
- import_material3.Stack,
3822
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3823
+ import_material4.Stack,
3722
3824
  {
3723
3825
  ref,
3724
3826
  sx: {
@@ -3742,9 +3844,9 @@ function FixedSizeContent({
3742
3844
  }
3743
3845
 
3744
3846
  // 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");
3847
+ var import_material5 = require("@mui/material");
3848
+ var import_react15 = require("react");
3849
+ var import_jsx_runtime8 = require("react/jsx-runtime");
3748
3850
  function toPx(v) {
3749
3851
  if (v === void 0) return void 0;
3750
3852
  return typeof v === "number" ? `${v}px` : v;
@@ -3774,10 +3876,19 @@ function ellipsePosition(angleDeg) {
3774
3876
  function toNum(v) {
3775
3877
  return typeof v === "number" ? v : 0;
3776
3878
  }
3879
+ function readInlineProperty(el, prop) {
3880
+ let node = el.parentElement;
3881
+ while (node) {
3882
+ const val = node.style.getPropertyValue(prop);
3883
+ if (val) return parseFloat(val) || 1;
3884
+ node = node.parentElement;
3885
+ }
3886
+ return 1;
3887
+ }
3777
3888
  function OverlayBadge({
3778
3889
  children,
3779
- maxScale = 2,
3780
- minScale = 0.75,
3890
+ maxScale = 1.5,
3891
+ minScale = 0.5,
3781
3892
  top,
3782
3893
  right,
3783
3894
  bottom,
@@ -3786,43 +3897,39 @@ function OverlayBadge({
3786
3897
  sx,
3787
3898
  ...rest
3788
3899
  }) {
3789
- const ref = (0, import_react14.useRef)(null);
3790
- const baseSize = (0, import_react14.useRef)(null);
3791
- (0, import_react14.useEffect)(() => {
3900
+ const ref = (0, import_react15.useRef)(null);
3901
+ (0, import_react15.useEffect)(() => {
3792
3902
  const el = ref.current;
3793
3903
  if (!el) return;
3794
3904
  const ancestor = el.parentElement;
3795
3905
  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
- });
3906
+ let rafId = 0;
3907
+ function doUpdate() {
3908
+ if (!el || !ancestor) return;
3909
+ if (ancestor.clientWidth <= 0 || ancestor.clientHeight <= 0) {
3910
+ el.style.transform = "";
3911
+ return;
3912
+ }
3913
+ const zoom = readInlineProperty(el, "--overlay-zoom");
3914
+ const ownScale = Math.max(minScale, Math.min(zoom, maxScale));
3915
+ const overlayScale = readInlineProperty(el, "--overlay-scale");
3916
+ const scale = `scale(${ownScale / overlayScale})`;
3917
+ el.style.transform = circular ? `translate(-50%, -50%) ${scale}` : scale;
3918
+ }
3919
+ function scheduleUpdate() {
3920
+ if (!rafId) {
3921
+ rafId = requestAnimationFrame(() => {
3922
+ rafId = 0;
3923
+ doUpdate();
3924
+ });
3925
+ }
3819
3926
  }
3820
- const observer = new ResizeObserver(update);
3927
+ const observer = new ResizeObserver(scheduleUpdate);
3821
3928
  observer.observe(ancestor);
3822
- update();
3929
+ scheduleUpdate();
3823
3930
  return () => {
3931
+ if (rafId) cancelAnimationFrame(rafId);
3824
3932
  observer.disconnect();
3825
- baseSize.current = null;
3826
3933
  };
3827
3934
  }, [maxScale, minScale, circular]);
3828
3935
  const positionSx = circular ? (() => {
@@ -3840,8 +3947,8 @@ function OverlayBadge({
3840
3947
  bottom: toPx(bottom),
3841
3948
  left: toPx(left)
3842
3949
  };
3843
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3844
- import_material4.Stack,
3950
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3951
+ import_material5.Stack,
3845
3952
  {
3846
3953
  ref,
3847
3954
  sx: {
@@ -3878,6 +3985,7 @@ var import_fabric19 = require("fabric");
3878
3985
  FixedSizeContent,
3879
3986
  ObjectOverlay,
3880
3987
  OverlayBadge,
3988
+ OverlayContainer,
3881
3989
  OverlayContent,
3882
3990
  Point,
3883
3991
  Polygon,