feat: add page template hover label
Add visual feedback for hovered page templates:
- Display "@page {templateName}" label on page edge hover
- Label positioned at top-left of page with 30% opacity
- Orange background matching page highlight color
- Automatically removed when hovering elements or clicking
Ensures consistent UX between page and element hover states.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5b5c65722b
commit
446b6cd9e7
2 changed files with 70 additions and 5 deletions
|
|
@ -178,6 +178,20 @@
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-hover-label {
|
||||||
|
position: absolute;
|
||||||
|
background: #ff8a50;
|
||||||
|
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) */
|
/* Marks (to delete when merge in paged.js) */
|
||||||
|
|
||||||
.pagedjs_marks-crop {
|
.pagedjs_marks-crop {
|
||||||
|
|
|
||||||
61
src/App.vue
61
src/App.vue
|
|
@ -75,7 +75,13 @@ const getSelectorFromElement = (element) => {
|
||||||
const tagName = element.tagName.toLowerCase();
|
const tagName = element.tagName.toLowerCase();
|
||||||
// Filter out state classes (element-hovered, element-selected, page-hovered, page-selected)
|
// Filter out state classes (element-hovered, element-selected, page-hovered, page-selected)
|
||||||
const classes = Array.from(element.classList).filter(
|
const classes = Array.from(element.classList).filter(
|
||||||
(cls) => !['element-hovered', 'element-selected', 'page-hovered', 'page-selected'].includes(cls)
|
(cls) =>
|
||||||
|
![
|
||||||
|
'element-hovered',
|
||||||
|
'element-selected',
|
||||||
|
'page-hovered',
|
||||||
|
'page-selected',
|
||||||
|
].includes(cls)
|
||||||
);
|
);
|
||||||
if (classes.length > 0) {
|
if (classes.length > 0) {
|
||||||
return `${tagName}.${classes[0]}`;
|
return `${tagName}.${classes[0]}`;
|
||||||
|
|
@ -98,7 +104,7 @@ const createElementLabel = (element) => {
|
||||||
const label = doc.createElement('div');
|
const label = doc.createElement('div');
|
||||||
label.className = 'element-hover-label';
|
label.className = 'element-hover-label';
|
||||||
label.textContent = getSelectorFromElement(element);
|
label.textContent = getSelectorFromElement(element);
|
||||||
label.style.top = `${rect.top + scrollTop - 30}px`;
|
label.style.top = `${rect.top + scrollTop - 32}px`;
|
||||||
label.style.left = `${rect.left + scrollLeft}px`;
|
label.style.left = `${rect.left + scrollLeft}px`;
|
||||||
|
|
||||||
doc.body.appendChild(label);
|
doc.body.appendChild(label);
|
||||||
|
|
@ -113,6 +119,38 @@ const removeElementLabel = (doc) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Create and position page label on hover
|
||||||
|
const createPageLabel = (page) => {
|
||||||
|
const doc = page.ownerDocument;
|
||||||
|
const existingLabel = doc.querySelector('.page-hover-label');
|
||||||
|
if (existingLabel) {
|
||||||
|
existingLabel.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = page.getBoundingClientRect();
|
||||||
|
const scrollTop = doc.documentElement.scrollTop || doc.body.scrollTop;
|
||||||
|
const scrollLeft = doc.documentElement.scrollLeft || doc.body.scrollLeft;
|
||||||
|
|
||||||
|
const templateName = page.getAttribute('data-page-type') || 'default';
|
||||||
|
|
||||||
|
const label = doc.createElement('div');
|
||||||
|
label.className = 'page-hover-label';
|
||||||
|
label.textContent = `@page ${templateName}`;
|
||||||
|
label.style.top = `${rect.top + scrollTop - 32}px`;
|
||||||
|
label.style.left = `${rect.left + scrollLeft}px`;
|
||||||
|
|
||||||
|
doc.body.appendChild(label);
|
||||||
|
return label;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove page label
|
||||||
|
const removePageLabel = (doc) => {
|
||||||
|
const label = doc.querySelector('.page-hover-label');
|
||||||
|
if (label) {
|
||||||
|
label.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Handle mouse movement in iframe
|
// Handle mouse movement in iframe
|
||||||
const handleIframeMouseMove = (event) => {
|
const handleIframeMouseMove = (event) => {
|
||||||
const pages = event.target.ownerDocument.querySelectorAll('.pagedjs_page');
|
const pages = event.target.ownerDocument.querySelectorAll('.pagedjs_page');
|
||||||
|
|
@ -133,9 +171,13 @@ const handleIframeMouseMove = (event) => {
|
||||||
hoveredPage.value.classList.remove('page-hovered');
|
hoveredPage.value.classList.remove('page-hovered');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove previous page label
|
||||||
|
removePageLabel(event.target.ownerDocument);
|
||||||
|
|
||||||
// Add highlight to new page (only if not already selected)
|
// Add highlight to new page (only if not already selected)
|
||||||
if (foundPage && !selectedPages.value.includes(foundPage)) {
|
if (foundPage && !selectedPages.value.includes(foundPage)) {
|
||||||
foundPage.classList.add('page-hovered');
|
foundPage.classList.add('page-hovered');
|
||||||
|
createPageLabel(foundPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
hoveredPage.value = foundPage;
|
hoveredPage.value = foundPage;
|
||||||
|
|
@ -155,8 +197,9 @@ const handleIframeMouseMove = (event) => {
|
||||||
hoveredElement.value.classList.remove('element-hovered');
|
hoveredElement.value.classList.remove('element-hovered');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove previous label
|
// Remove previous labels
|
||||||
removeElementLabel(doc);
|
removeElementLabel(doc);
|
||||||
|
removePageLabel(doc);
|
||||||
|
|
||||||
// Add highlight to new element (only if not already selected)
|
// Add highlight to new element (only if not already selected)
|
||||||
if (contentElement && contentElement !== selectedElement.value) {
|
if (contentElement && contentElement !== selectedElement.value) {
|
||||||
|
|
@ -175,7 +218,7 @@ const handleIframeMouseMove = (event) => {
|
||||||
hoveredElement.value.classList.remove('element-hovered');
|
hoveredElement.value.classList.remove('element-hovered');
|
||||||
hoveredElement.value = null;
|
hoveredElement.value = null;
|
||||||
}
|
}
|
||||||
// Remove label when hovering page edge
|
// Remove element label when hovering page edge
|
||||||
removeElementLabel(event.target.ownerDocument);
|
removeElementLabel(event.target.ownerDocument);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -276,6 +319,10 @@ const handleIframeClick = (event) => {
|
||||||
});
|
});
|
||||||
selectedPages.value = sameTemplatePages;
|
selectedPages.value = sameTemplatePages;
|
||||||
|
|
||||||
|
// Remove labels when opening popup
|
||||||
|
removePageLabel(doc);
|
||||||
|
removeElementLabel(doc);
|
||||||
|
|
||||||
pagePopup.value.open(hoveredPage.value, event, sameTemplatePages.length);
|
pagePopup.value.open(hoveredPage.value, event, sameTemplatePages.length);
|
||||||
elementPopup.value.close();
|
elementPopup.value.close();
|
||||||
return;
|
return;
|
||||||
|
|
@ -323,12 +370,16 @@ const handleIframeClick = (event) => {
|
||||||
hoveredElement.value = null;
|
hoveredElement.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get document and remove labels when opening popup
|
||||||
|
const doc = event.target.ownerDocument;
|
||||||
|
removeElementLabel(doc);
|
||||||
|
removePageLabel(doc);
|
||||||
|
|
||||||
// Select the new element
|
// Select the new element
|
||||||
selectedElement.value = contentElement;
|
selectedElement.value = contentElement;
|
||||||
contentElement.classList.add('element-selected');
|
contentElement.classList.add('element-selected');
|
||||||
|
|
||||||
// Get count of similar elements
|
// Get count of similar elements
|
||||||
const doc = event.target.ownerDocument;
|
|
||||||
const count = getSimilarElementsCount(contentElement, doc);
|
const count = getSimilarElementsCount(contentElement, doc);
|
||||||
|
|
||||||
elementPopup.value.handleIframeClick(event, contentElement, count);
|
elementPopup.value.handleIframeClick(event, contentElement, count);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue