diff --git a/public/assets/css/pagedjs-interface.css b/public/assets/css/pagedjs-interface.css index bcef24a..b4ad502 100644 --- a/public/assets/css/pagedjs-interface.css +++ b/public/assets/css/pagedjs-interface.css @@ -144,6 +144,38 @@ /*--------------------------------------------------------------------------------------*/ } +/* Hover and selection states for pages and elements */ +.page-hovered { + outline: 2px solid #ff8a5050 !important; +} + +.page-selected { + outline: 2px solid #ff8a50 !important; +} + +.element-hovered { + outline: 2px solid #7136ff50 !important; +} + +.element-selected { + outline: 2px dashed #7136ff !important; + background-color: #7136ff1a !important; +} + +.element-hover-label { + position: absolute; + background: #7136ff; + color: white; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.875rem; + font-weight: 600; + opacity: 0.3; + pointer-events: none; + z-index: 9999; + font-family: sans-serif; +} + /* Marks (to delete when merge in paged.js) */ .pagedjs_marks-crop { diff --git a/src/App.vue b/src/App.vue index 238a104..2826c7f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -65,6 +65,45 @@ const getPagesWithSameTemplate = (page, doc) => { ); }; +// Get selector for element (same logic as ElementPopup) +const getSelectorFromElement = (element) => { + if (element.id) { + return `#${element.id}`; + } + const tagName = element.tagName.toLowerCase(); + const classes = Array.from(element.classList); + if (classes.length > 0) { + return `${tagName}.${classes[0]}`; + } + return tagName; +}; + +// Create and position element label on hover +const createElementLabel = (element) => { + const doc = element.ownerDocument; + const existingLabel = doc.querySelector('.element-hover-label'); + if (existingLabel) { + existingLabel.remove(); + } + + 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`; + + doc.body.appendChild(label); + return label; +}; + +// Remove element label +const removeElementLabel = (doc) => { + const label = doc.querySelector('.element-hover-label'); + if (label) { + label.remove(); + } +}; + // Handle mouse movement in iframe const handleIframeMouseMove = (event) => { const pages = event.target.ownerDocument.querySelectorAll('.pagedjs_page'); @@ -82,12 +121,12 @@ const handleIframeMouseMove = (event) => { if (foundPage !== hoveredPage.value) { // Remove highlight from previous page (only if not in selectedPages) if (hoveredPage.value && !selectedPages.value.includes(hoveredPage.value)) { - hoveredPage.value.style.outline = ''; + hoveredPage.value.classList.remove('page-hovered'); } // Add highlight to new page (only if not already selected) if (foundPage && !selectedPages.value.includes(foundPage)) { - foundPage.style.outline = `2px solid ${PAGE_HIGHLIGHT_COLOR}50`; + foundPage.classList.add('page-hovered'); } hoveredPage.value = foundPage; @@ -96,16 +135,21 @@ const handleIframeMouseMove = (event) => { // If not near page edge, check for content element hover if (!foundPage) { const contentElement = getContentElement(event.target); + const doc = event.target.ownerDocument; if (contentElement !== hoveredElement.value) { // Remove highlight from previous element (only if not selected) if (hoveredElement.value && hoveredElement.value !== selectedElement.value) { - hoveredElement.value.style.outline = ''; + hoveredElement.value.classList.remove('element-hovered'); } + // Remove previous label + removeElementLabel(doc); + // Add highlight to new element (only if not already selected) if (contentElement && contentElement !== selectedElement.value) { - contentElement.style.outline = `2px solid ${ELEMENT_HIGHLIGHT_COLOR}50`; + contentElement.classList.add('element-hovered'); + createElementLabel(contentElement); } hoveredElement.value = contentElement; @@ -113,16 +157,18 @@ const handleIframeMouseMove = (event) => { } else { // Clear element hover when hovering page edge if (hoveredElement.value && hoveredElement.value !== selectedElement.value) { - hoveredElement.value.style.outline = ''; + hoveredElement.value.classList.remove('element-hovered'); hoveredElement.value = null; } + // Remove label when hovering page edge + removeElementLabel(event.target.ownerDocument); } }; // Clear selection highlight from all selected pages const clearSelectedPages = () => { selectedPages.value.forEach((page) => { - page.style.outline = ''; + page.classList.remove('page-selected'); }); selectedPages.value = []; }; @@ -153,8 +199,9 @@ const getContentElement = (element) => { // Clear selected element highlight const clearSelectedElement = () => { if (selectedElement.value) { - selectedElement.value.style.outline = ''; - selectedElement.value.style.backgroundColor = ''; + selectedElement.value.classList.remove('element-selected'); + const doc = selectedElement.value.ownerDocument; + removeElementLabel(doc); selectedElement.value = null; } }; @@ -182,7 +229,7 @@ const handleIframeClick = (event) => { const doc = event.target.ownerDocument; const sameTemplatePages = getPagesWithSameTemplate(hoveredPage.value, doc); sameTemplatePages.forEach((page) => { - page.style.outline = `2px solid ${PAGE_HIGHLIGHT_COLOR}`; + page.classList.add('page-selected'); }); selectedPages.value = sameTemplatePages; @@ -219,8 +266,7 @@ const handleIframeClick = (event) => { // Select the new element selectedElement.value = contentElement; - contentElement.style.outline = `2px dashed ${ELEMENT_HIGHLIGHT_COLOR}`; - contentElement.style.backgroundColor = `${ELEMENT_HIGHLIGHT_COLOR}1A`; // 10% opacity + contentElement.classList.add('element-selected'); // Get count of similar elements const doc = event.target.ownerDocument;