diff --git a/public/assets/css/pagedjs-interface.css b/public/assets/css/pagedjs-interface.css
index b4ad502..6a530f3 100644
--- a/public/assets/css/pagedjs-interface.css
+++ b/public/assets/css/pagedjs-interface.css
@@ -147,6 +147,7 @@
/* Hover and selection states for pages and elements */
.page-hovered {
outline: 2px solid #ff8a5050 !important;
+ cursor: pointer !important;
}
.page-selected {
@@ -155,6 +156,7 @@
.element-hovered {
outline: 2px solid #7136ff50 !important;
+ cursor: pointer !important;
}
.element-selected {
diff --git a/src/App.vue b/src/App.vue
index 2826c7f..d6db086 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -41,9 +41,11 @@ const isNearPageEdge = (pageElement, mouseX, mouseY) => {
const rect = pageElement.getBoundingClientRect();
const nearLeft = mouseX >= rect.left && mouseX <= rect.left + EDGE_THRESHOLD;
- const nearRight = mouseX >= rect.right - EDGE_THRESHOLD && mouseX <= rect.right;
+ const nearRight =
+ mouseX >= rect.right - EDGE_THRESHOLD && mouseX <= rect.right;
const nearTop = mouseY >= rect.top && mouseY <= rect.top + EDGE_THRESHOLD;
- const nearBottom = mouseY >= rect.bottom - EDGE_THRESHOLD && mouseY <= rect.bottom;
+ const nearBottom =
+ mouseY >= rect.bottom - EDGE_THRESHOLD && mouseY <= rect.bottom;
const inHorizontalRange = mouseY >= rect.top && mouseY <= rect.bottom;
const inVerticalRange = mouseX >= rect.left && mouseX <= rect.right;
@@ -71,7 +73,10 @@ const getSelectorFromElement = (element) => {
return `#${element.id}`;
}
const tagName = element.tagName.toLowerCase();
- const classes = Array.from(element.classList);
+ // Filter out state classes (element-hovered, element-selected, page-hovered, page-selected)
+ const classes = Array.from(element.classList).filter(
+ (cls) => !['element-hovered', 'element-selected', 'page-hovered', 'page-selected'].includes(cls)
+ );
if (classes.length > 0) {
return `${tagName}.${classes[0]}`;
}
@@ -86,11 +91,15 @@ const createElementLabel = (element) => {
existingLabel.remove();
}
+ const rect = element.getBoundingClientRect();
+ const scrollTop = doc.documentElement.scrollTop || doc.body.scrollTop;
+ const scrollLeft = doc.documentElement.scrollLeft || doc.body.scrollLeft;
+
const label = doc.createElement('div');
label.className = 'element-hover-label';
label.textContent = getSelectorFromElement(element);
- label.style.top = `${element.offsetTop}px`;
- label.style.left = `${element.offsetLeft}px`;
+ label.style.top = `${rect.top + scrollTop - 30}px`;
+ label.style.left = `${rect.left + scrollLeft}px`;
doc.body.appendChild(label);
return label;
@@ -139,7 +148,10 @@ const handleIframeMouseMove = (event) => {
if (contentElement !== hoveredElement.value) {
// Remove highlight from previous element (only if not selected)
- if (hoveredElement.value && hoveredElement.value !== selectedElement.value) {
+ if (
+ hoveredElement.value &&
+ hoveredElement.value !== selectedElement.value
+ ) {
hoveredElement.value.classList.remove('element-hovered');
}
@@ -156,7 +168,10 @@ const handleIframeMouseMove = (event) => {
}
} else {
// Clear element hover when hovering page edge
- if (hoveredElement.value && hoveredElement.value !== selectedElement.value) {
+ if (
+ hoveredElement.value &&
+ hoveredElement.value !== selectedElement.value
+ ) {
hoveredElement.value.classList.remove('element-hovered');
hoveredElement.value = null;
}
@@ -175,13 +190,41 @@ const clearSelectedPages = () => {
// Content elements that can trigger ElementPopup
const CONTENT_ELEMENTS = [
- 'P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6',
- 'IMG', 'FIGURE', 'FIGCAPTION',
- 'UL', 'OL', 'LI',
- 'BLOCKQUOTE', 'PRE', 'CODE',
- 'TABLE', 'THEAD', 'TBODY', 'TR', 'TH', 'TD',
- 'A', 'SPAN', 'STRONG', 'EM', 'B', 'I', 'U',
- 'ARTICLE', 'SECTION', 'ASIDE', 'HEADER', 'FOOTER', 'NAV'
+ 'P',
+ 'H1',
+ 'H2',
+ 'H3',
+ 'H4',
+ 'H5',
+ 'H6',
+ 'IMG',
+ 'FIGURE',
+ 'FIGCAPTION',
+ 'UL',
+ 'OL',
+ 'LI',
+ 'BLOCKQUOTE',
+ 'PRE',
+ 'CODE',
+ 'TABLE',
+ 'THEAD',
+ 'TBODY',
+ 'TR',
+ 'TH',
+ 'TD',
+ 'A',
+ 'SPAN',
+ 'STRONG',
+ 'EM',
+ 'B',
+ 'I',
+ 'U',
+ 'ARTICLE',
+ 'SECTION',
+ 'ASIDE',
+ 'HEADER',
+ 'FOOTER',
+ 'NAV',
];
// Check if element is a content element (or find closest content parent)
@@ -261,9 +304,25 @@ const handleIframeClick = (event) => {
// Clear page selections
clearSelectedPages();
+ // If popup is already open and we're clicking another element, close it
+ if (elementPopup.value.visible) {
+ clearSelectedElement();
+ elementPopup.value.close();
+ pagePopup.value.close();
+ return;
+ }
+
// Clear previous element selection
clearSelectedElement();
+ // Remove hovered class from the element we're about to select
+ contentElement.classList.remove('element-hovered');
+
+ // Clear the hoveredElement ref if it's the same as what we're selecting
+ if (hoveredElement.value === contentElement) {
+ hoveredElement.value = null;
+ }
+
// Select the new element
selectedElement.value = contentElement;
contentElement.classList.add('element-selected');
@@ -335,7 +394,10 @@ const renderPreview = async (shouldReloadFromFile = false) => {
hiddenFrame.onload = () => {
// Add event listeners for page and element interactions
- hiddenFrame.contentDocument.addEventListener('mousemove', handleIframeMouseMove);
+ hiddenFrame.contentDocument.addEventListener(
+ 'mousemove',
+ handleIframeMouseMove
+ );
hiddenFrame.contentDocument.addEventListener('click', handleIframeClick);
// Close Coloris when clicking in the iframe
@@ -469,12 +531,26 @@ onMounted(() => renderPreview(true));
-
-
+
+
@@ -524,7 +600,9 @@ onMounted(() => renderPreview(true));
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
- transition: transform 0.2s ease, box-shadow 0.2s ease;
+ transition:
+ transform 0.2s ease,
+ box-shadow 0.2s ease;
z-index: 1000;
}
diff --git a/src/components/ElementPopup.vue b/src/components/ElementPopup.vue
index a6ebd79..a8d9a73 100644
--- a/src/components/ElementPopup.vue
+++ b/src/components/ElementPopup.vue
@@ -337,8 +337,10 @@ const getSelectorFromElement = (element) => {
// Get tag name
const tagName = element.tagName.toLowerCase();
- // Get first class if available
- const classes = Array.from(element.classList);
+ // Get first class if available (filter out state classes)
+ const classes = Array.from(element.classList).filter(
+ (cls) => !['element-hovered', 'element-selected', 'page-hovered', 'page-selected'].includes(cls)
+ );
if (classes.length > 0) {
return `${tagName}.${classes[0]}`;
}