@teachinglab/omd 0.7.10 → 0.7.11

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.
@@ -663,11 +663,11 @@ export class SelectTool extends Tool {
663
663
 
664
664
  /**
665
665
  * Gets the bounds of an OMD element including transform
666
+ * Uses clip paths for accurate bounds when present (e.g., coordinate planes)
666
667
  * @private
667
668
  */
668
669
  _getOMDElementBounds(item) {
669
670
  try {
670
- const bbox = item.getBBox();
671
671
  const transform = item.getAttribute('transform') || '';
672
672
  let offsetX = 0, offsetY = 0, scaleX = 1, scaleY = 1;
673
673
 
@@ -683,6 +683,68 @@ export class SelectTool extends Tool {
683
683
  scaleY = scaleMatch[2] ? parseFloat(scaleMatch[2]) : scaleX;
684
684
  }
685
685
 
686
+ // Get the content element (usually an SVG inside the wrapper)
687
+ const content = item.firstElementChild;
688
+ let bbox;
689
+
690
+ if (content) {
691
+ // Check for clip paths to get accurate visible bounds
692
+ // This is important for coordinate planes where graph lines extend beyond visible area
693
+ const clipPaths = content.querySelectorAll('clipPath');
694
+
695
+ if (clipPaths.length > 0) {
696
+ let maxArea = 0;
697
+ let clipBounds = null;
698
+
699
+ for (const clipPath of clipPaths) {
700
+ const rect = clipPath.querySelector('rect');
701
+ if (rect) {
702
+ const w = parseFloat(rect.getAttribute('width')) || 0;
703
+ const h = parseFloat(rect.getAttribute('height')) || 0;
704
+ const x = parseFloat(rect.getAttribute('x')) || 0;
705
+ const y = parseFloat(rect.getAttribute('y')) || 0;
706
+
707
+ const area = w * h;
708
+ if (area > maxArea) {
709
+ maxArea = area;
710
+
711
+ // Find the transform on the content group
712
+ const contentGroup = content.firstElementChild;
713
+ let tx = 0, ty = 0;
714
+ if (contentGroup) {
715
+ const contentTransform = contentGroup.getAttribute('transform');
716
+ if (contentTransform) {
717
+ const contentTranslateMatch = contentTransform.match(/translate\(\s*([^,]+)(?:,\s*([^)]+))?\s*\)/);
718
+ if (contentTranslateMatch) {
719
+ tx = parseFloat(contentTranslateMatch[1]) || 0;
720
+ ty = parseFloat(contentTranslateMatch[2]) || 0;
721
+ }
722
+ }
723
+ }
724
+
725
+ clipBounds = {
726
+ x: x + tx,
727
+ y: y + ty,
728
+ width: w,
729
+ height: h
730
+ };
731
+ }
732
+ }
733
+ }
734
+
735
+ if (clipBounds) {
736
+ bbox = clipBounds;
737
+ }
738
+ }
739
+
740
+ // Fallback to getBBox if no clip path found
741
+ if (!bbox) {
742
+ bbox = content.getBBox();
743
+ }
744
+ } else {
745
+ bbox = item.getBBox();
746
+ }
747
+
686
748
  return {
687
749
  x: offsetX + (bbox.x * scaleX),
688
750
  y: offsetY + (bbox.y * scaleY),
@@ -738,42 +800,20 @@ export class SelectTool extends Tool {
738
800
  continue;
739
801
  }
740
802
  try {
741
- // Get the bounding box of the item
742
- const bbox = item.getBBox();
743
-
744
- // Parse transform to get the actual position
745
- const transform = item.getAttribute('transform') || '';
746
- let offsetX = 0, offsetY = 0, scaleX = 1, scaleY = 1;
747
-
748
- // Parse translate
749
- const translateMatch = transform.match(/translate\(\s*([^,]+)\s*,\s*([^)]+)\s*\)/);
750
- if (translateMatch) {
751
- offsetX = parseFloat(translateMatch[1]) || 0;
752
- offsetY = parseFloat(translateMatch[2]) || 0;
753
- }
754
-
755
- // Parse scale
756
- const scaleMatch = transform.match(/scale\(\s*([^,)]+)(?:\s*,\s*([^)]+))?\s*\)/);
757
- if (scaleMatch) {
758
- scaleX = parseFloat(scaleMatch[1]) || 1;
759
- scaleY = scaleMatch[2] ? parseFloat(scaleMatch[2]) : scaleX;
760
- }
761
-
762
- // Calculate the actual bounds including transform
763
- const actualX = offsetX + (bbox.x * scaleX);
764
- const actualY = offsetY + (bbox.y * scaleY);
765
- const actualWidth = bbox.width * scaleX;
766
- const actualHeight = bbox.height * scaleY;
767
-
768
- // Add some padding for easier clicking
769
- const padding = 10;
803
+ // Use the shared bounds calculation method (handles clip paths correctly)
804
+ const itemBounds = this._getOMDElementBounds(item);
770
805
 
771
- // Check if point is within the bounds (with padding)
772
- if (x >= actualX - padding &&
773
- x <= actualX + actualWidth + padding &&
774
- y >= actualY - padding &&
775
- y <= actualY + actualHeight + padding) {
776
- return item;
806
+ if (itemBounds) {
807
+ // Add some padding for easier clicking
808
+ const padding = 10;
809
+
810
+ // Check if point is within the bounds (with padding)
811
+ if (x >= itemBounds.x - padding &&
812
+ x <= itemBounds.x + itemBounds.width + padding &&
813
+ y >= itemBounds.y - padding &&
814
+ y <= itemBounds.y + itemBounds.height + padding) {
815
+ return item;
816
+ }
777
817
  }
778
818
  } catch (error) {
779
819
  // Skip items that can't be measured (continue with next)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teachinglab/omd",
3
- "version": "0.7.10",
3
+ "version": "0.7.11",
4
4
  "description": "omd",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",