Skip to content

Commit e3bf8f1

Browse files
committed
refactor: calculate the position of the boxes
1 parent 72acb62 commit e3bf8f1

File tree

1 file changed

+49
-141
lines changed

1 file changed

+49
-141
lines changed

src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

Lines changed: 49 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,9 @@ function RemoteFunctions(config) {
6464
if (window.navigator.platform.substr(0, 3) === "Mac") {
6565
// Mac
6666
return event.metaKey;
67-
} else {
68-
// Windows
69-
return event.ctrlKey;
7067
}
68+
// Windows
69+
return event.ctrlKey;
7170
}
7271

7372

@@ -567,6 +566,20 @@ function RemoteFunctions(config) {
567566
});
568567
},
569568

569+
// note: this box width is the width of the more options box
570+
// we need this as the value is not consistent, it depends on the number of options we show in the box
571+
_getBoxPosition: function(boxWidth) {
572+
const elemBounds = this.element.getBoundingClientRect();
573+
574+
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
575+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
576+
577+
let topPos = elemBounds.top - 30 + scrollTop;
578+
let leftPos = elemBounds.right - boxWidth + scrollLeft;
579+
580+
return {topPos: topPos, leftPos: leftPos};
581+
},
582+
570583
_style: function() {
571584
this.body = window.document.createElement("div");
572585

@@ -575,9 +588,6 @@ function RemoteFunctions(config) {
575588
// {mode: "closed"} means that users will not be able to access the shadow DOM
576589
const shadow = this.body.attachShadow({ mode: "closed" });
577590

578-
// the element that was clicked
579-
let elemBounds = this.element.getBoundingClientRect();
580-
581591
// check which options should be shown to determine box width
582592
const showEditTextOption = _shouldShowEditTextOption(this.element);
583593
const showSelectParentOption = _shouldShowSelectParentOption(this.element);
@@ -601,43 +611,6 @@ function RemoteFunctions(config) {
601611
} else {
602612
boxWidth = 106;
603613
}
604-
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
605-
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
606-
607-
// get the ID and classes for the element
608-
// we need this to check for overlap issue between the info box and this box
609-
// because when we have classes and ids then the info box tends to stretch in width
610-
const id = this.element.id;
611-
const classes = this.element.className ? this.element.className.split(/\s+/).filter(Boolean) : [];
612-
const tagName = this.element.tagName.toLowerCase();
613-
614-
const isOverlap = checkOverlap(elemBounds.width, tagName, id, classes);
615-
616-
const viewportWidth = window.innerWidth;
617-
const idealLeftPos = elemBounds.right - boxWidth + scrollLeft;
618-
const maxLeftPos = viewportWidth - boxWidth - 10 + scrollLeft;
619-
// 10px is just the padding, because we want some space
620-
const minLeftPos = 10 + scrollLeft;
621-
622-
// we'll use the position that keeps the box within viewport bounds
623-
let leftPos = Math.min(idealLeftPos, maxLeftPos);
624-
leftPos = Math.max(leftPos, minLeftPos);
625-
let topPos;
626-
627-
if (isOverlap) {
628-
if (elemBounds.top > 40) { // check if there's enough space at the top
629-
// place at the top
630-
topPos = elemBounds.top - 30 + scrollTop;
631-
} else {
632-
// at the bottom
633-
topPos = elemBounds.top + elemBounds.height + 5 + scrollTop;
634-
}
635-
} else {
636-
// no overlap, so it comes just above the element
637-
topPos = (elemBounds.top - 30 < 0
638-
? elemBounds.top + elemBounds.height + 5
639-
: elemBounds.top - 30) + scrollTop;
640-
}
641614

642615
// the icons that is displayed in the box
643616
const ICONS = {
@@ -693,6 +666,8 @@ function RemoteFunctions(config) {
693666
</span>
694667
</div>`;
695668

669+
const boxPos = this._getBoxPosition(boxWidth);
670+
696671
const styles = `
697672
.box {
698673
background-color: #4285F4;
@@ -704,8 +679,8 @@ function RemoteFunctions(config) {
704679
font-family: Arial, sans-serif;
705680
z-index: 2147483647;
706681
position: absolute;
707-
left: ${leftPos}px;
708-
top: ${topPos}px;
682+
left: ${boxPos.leftPos}px;
683+
top: ${boxPos.topPos}px;
709684
width: ${boxWidth}px;
710685
box-sizing: border-box;
711686
}
@@ -774,6 +749,32 @@ function RemoteFunctions(config) {
774749
}
775750

776751
NodeInfoBox.prototype = {
752+
_calcHeight: function() {
753+
const element = this.element;
754+
755+
let baseHeight = 26.75;
756+
if(element.id) {
757+
baseHeight += 17.25;
758+
}
759+
if(element.className.length !== 0) {
760+
baseHeight += 17.25;
761+
}
762+
763+
return baseHeight;
764+
},
765+
766+
_getBoxPosition: function() {
767+
const elemBounds = this.element.getBoundingClientRect();
768+
769+
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
770+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
771+
772+
let topPos = elemBounds.top - this._calcHeight() + scrollTop;
773+
let leftPos = elemBounds.left + scrollLeft;
774+
775+
return {topPos: topPos, leftPos: leftPos};
776+
},
777+
777778
_style: function() {
778779
this.body = window.document.createElement("div");
779780

@@ -782,18 +783,6 @@ function RemoteFunctions(config) {
782783
// {mode: "closed"} means that users will not be able to access the shadow DOM
783784
const shadow = this.body.attachShadow({ mode: "closed" });
784785

785-
// the element that was clicked
786-
let elemBounds = this.element.getBoundingClientRect();
787-
788-
// the positions where it should be placed
789-
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
790-
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
791-
792-
// this value decides where we need to show the box in the UI
793-
// we are creating this here, because if the element has IDs and Classes then we need to increase the value
794-
// so that the box doesn't obscure the element
795-
let pushBoxUp = 32; // px value
796-
797786
// get the ID and classes for that element, as we need to display it in the box
798787
const id = this.element.id;
799788
const classes = this.element.className ? this.element.className.split(/\s+/).filter(Boolean) : [];
@@ -804,7 +793,6 @@ function RemoteFunctions(config) {
804793
// Add ID if present
805794
if (id) {
806795
content += "<div class='id-name'>#" + id + "</div>";
807-
pushBoxUp += 20;
808796
}
809797

810798
// Add classes (limit to 3 with dropdown indicator)
@@ -817,89 +805,9 @@ function RemoteFunctions(config) {
817805
content += "<span class='exceeded-classes'>+" + (classes.length - 3) + " more</span>";
818806
}
819807
content += "</div>";
820-
pushBoxUp += 20;
821808
}
822809

823-
let leftPos = elemBounds.left + scrollLeft;
824-
let topPos = (elemBounds.top - pushBoxUp < 0
825-
? elemBounds.top + elemBounds.height + 5
826-
: elemBounds.top - pushBoxUp) + scrollTop;
827-
828-
let avgCharWidth = 6;
829-
const basePadding = 16;
830-
831-
// Get the tag name
832-
const tagName = this.element.tagName.toLowerCase();
833-
834-
// Count characters in tag name, id, and classes
835-
let charCount = _calculateInfoBoxCharCount(tagName, id, classes);
836-
if(charCount <= 10) {
837-
avgCharWidth = 7.5;
838-
}
839-
840-
// Calculate estimated width based on character count
841-
// Formula: base padding + (character count * average character width)
842-
const boxWidth = basePadding + (charCount * avgCharWidth);
843-
844-
// we need to check for overlap if this is from a click
845-
if (this.isFromClick) {
846-
const isOverlap = checkOverlap(elemBounds.width, tagName, id, classes);
847-
848-
if (isOverlap) {
849-
const windowWidth = window.innerWidth;
850-
const viewportHeight = window.innerHeight;
851-
852-
// Estimate the height of the info box based on its content
853-
// base height for tag name + padding
854-
let estimatedHeight = 32;
855-
856-
// height adjustment if ID is present
857-
if (id) {
858-
estimatedHeight += 20;
859-
}
860-
861-
// height adjustment if classes are present
862-
if (classes.length > 0) {
863-
estimatedHeight += 20;
864-
}
865-
866-
// check if element is near bottom of viewport
867-
const elementBottomFromViewportTop = elemBounds.bottom;
868-
const availableSpaceBelow = viewportHeight - elementBottomFromViewportTop;
869-
870-
// align with the bottom of the info box (original behavior)
871-
topPos = (elemBounds.top + elemBounds.height - estimatedHeight) + scrollTop;
872-
873-
// If element is near bottom and there's not enough space below,
874-
// push the info box up a bit to prevent scrollbar
875-
if (availableSpaceBelow < estimatedHeight + 10) {
876-
// Push it up by the amount it would overflow
877-
const pushUpAmount = estimatedHeight - availableSpaceBelow;
878-
topPos -= pushUpAmount;
879-
}
880-
881-
// decide whether position at left or right based on available space
882-
// check if there's enough space on the left side
883-
if (elemBounds.left > boxWidth + 10) {
884-
leftPos = elemBounds.left - boxWidth - 10 + scrollLeft;
885-
} else if (windowWidth - elemBounds.right > boxWidth + 10) {
886-
// position on the right
887-
leftPos = elemBounds.right + 10 + scrollLeft;
888-
}
889-
}
890-
}
891-
892-
// to make sure that the info box stays under the viewport width
893-
const viewportWidth = window.innerWidth;
894-
const margin = 10;
895-
896-
// horizontal boundary checking
897-
if (leftPos + boxWidth + margin > viewportWidth + scrollLeft) {
898-
leftPos = viewportWidth + scrollLeft - boxWidth - margin;
899-
}
900-
if (leftPos < scrollLeft + margin) {
901-
leftPos = scrollLeft + margin;
902-
}
810+
const boxPos = this._getBoxPosition();
903811

904812
const styles = `
905813
.box {
@@ -912,8 +820,8 @@ function RemoteFunctions(config) {
912820
font-family: Arial, sans-serif;
913821
z-index: 2147483647;
914822
position: absolute;
915-
left: ${leftPos}px;
916-
top: ${topPos}px;
823+
left: ${boxPos.leftPos}px;
824+
top: ${boxPos.topPos}px;
917825
max-width: fit-content;
918826
box-sizing: border-box;
919827
pointer-events: none;

0 commit comments

Comments
 (0)