feat: improve page highlight with orange color and template grouping
- Add --color-page-highlight CSS variable (#ff8a50) - Change page edge highlight from blue to orange - Keep border visible while PagePopup is open - Highlight all pages using the same template (data-page-type) - Display dynamic page count in PagePopup header - Emit close event from PagePopup for proper cleanup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ee849dab8e
commit
7647aadb63
3 changed files with 57 additions and 19 deletions
|
|
@ -4,6 +4,8 @@
|
||||||
--color-browngray-200: #d0c4ba;
|
--color-browngray-200: #d0c4ba;
|
||||||
--color-browngray-300: #b5a9a1;
|
--color-browngray-300: #b5a9a1;
|
||||||
|
|
||||||
|
--color-page-highlight: #ff8a50;
|
||||||
|
|
||||||
--border-radius: 0.2rem;
|
--border-radius: 0.2rem;
|
||||||
|
|
||||||
--space-xs: 0.5rem;
|
--space-xs: 0.5rem;
|
||||||
|
|
|
||||||
52
src/App.vue
52
src/App.vue
|
|
@ -19,7 +19,9 @@ provide('activeTab', activeTab);
|
||||||
|
|
||||||
// Page interaction state
|
// Page interaction state
|
||||||
const hoveredPage = ref(null);
|
const hoveredPage = ref(null);
|
||||||
|
const selectedPages = ref([]); // Pages with active border (when popup is open)
|
||||||
const EDGE_THRESHOLD = 30; // px from edge to trigger hover
|
const EDGE_THRESHOLD = 30; // px from edge to trigger hover
|
||||||
|
const PAGE_HIGHLIGHT_COLOR = '#ff8a50';
|
||||||
|
|
||||||
let savedScrollPercentage = 0;
|
let savedScrollPercentage = 0;
|
||||||
const currentFrameIndex = ref(1); // 1 or 2, which iframe is currently visible
|
const currentFrameIndex = ref(1); // 1 or 2, which iframe is currently visible
|
||||||
|
|
@ -51,6 +53,15 @@ const isNearPageEdge = (pageElement, mouseX, mouseY) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Get all pages using the same template as the given page
|
||||||
|
const getPagesWithSameTemplate = (page, doc) => {
|
||||||
|
const pageType = page.getAttribute('data-page-type') || 'default';
|
||||||
|
const allPages = doc.querySelectorAll('.pagedjs_page');
|
||||||
|
return Array.from(allPages).filter(
|
||||||
|
(p) => (p.getAttribute('data-page-type') || 'default') === pageType
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// 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');
|
||||||
|
|
@ -65,20 +76,28 @@ const handleIframeMouseMove = (event) => {
|
||||||
|
|
||||||
// Update hover state
|
// Update hover state
|
||||||
if (foundPage !== hoveredPage.value) {
|
if (foundPage !== hoveredPage.value) {
|
||||||
// Remove highlight from previous page
|
// Remove highlight from previous page (only if not in selectedPages)
|
||||||
if (hoveredPage.value) {
|
if (hoveredPage.value && !selectedPages.value.includes(hoveredPage.value)) {
|
||||||
hoveredPage.value.style.outline = '';
|
hoveredPage.value.style.outline = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add highlight to new page
|
// Add highlight to new page (only if not already selected)
|
||||||
if (foundPage) {
|
if (foundPage && !selectedPages.value.includes(foundPage)) {
|
||||||
foundPage.style.outline = '2px solid rgba(97, 175, 239, 0.3)';
|
foundPage.style.outline = `2px solid ${PAGE_HIGHLIGHT_COLOR}50`;
|
||||||
}
|
}
|
||||||
|
|
||||||
hoveredPage.value = foundPage;
|
hoveredPage.value = foundPage;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Clear selection highlight from all selected pages
|
||||||
|
const clearSelectedPages = () => {
|
||||||
|
selectedPages.value.forEach((page) => {
|
||||||
|
page.style.outline = '';
|
||||||
|
});
|
||||||
|
selectedPages.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
// Handle click in iframe
|
// Handle click in iframe
|
||||||
const handleIframeClick = (event) => {
|
const handleIframeClick = (event) => {
|
||||||
const element = event.target;
|
const element = event.target;
|
||||||
|
|
@ -86,7 +105,19 @@ const handleIframeClick = (event) => {
|
||||||
// Check if clicking near a page edge
|
// Check if clicking near a page edge
|
||||||
if (hoveredPage.value) {
|
if (hoveredPage.value) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
pagePopup.value.open(hoveredPage.value, event);
|
|
||||||
|
// Clear previous selection
|
||||||
|
clearSelectedPages();
|
||||||
|
|
||||||
|
// Get all pages with same template and highlight them
|
||||||
|
const doc = event.target.ownerDocument;
|
||||||
|
const sameTemplatePages = getPagesWithSameTemplate(hoveredPage.value, doc);
|
||||||
|
sameTemplatePages.forEach((page) => {
|
||||||
|
page.style.outline = `2px solid ${PAGE_HIGHLIGHT_COLOR}`;
|
||||||
|
});
|
||||||
|
selectedPages.value = sameTemplatePages;
|
||||||
|
|
||||||
|
pagePopup.value.open(hoveredPage.value, event, sameTemplatePages.length);
|
||||||
elementPopup.value.close();
|
elementPopup.value.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -94,15 +125,22 @@ const handleIframeClick = (event) => {
|
||||||
// Only show popup for elements inside the page template
|
// Only show popup for elements inside the page template
|
||||||
const isInsidePage = element.closest('.pagedjs_page');
|
const isInsidePage = element.closest('.pagedjs_page');
|
||||||
if (!isInsidePage) {
|
if (!isInsidePage) {
|
||||||
|
clearSelectedPages();
|
||||||
elementPopup.value.close();
|
elementPopup.value.close();
|
||||||
pagePopup.value.close();
|
pagePopup.value.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearSelectedPages();
|
||||||
elementPopup.value.handleIframeClick(event);
|
elementPopup.value.handleIframeClick(event);
|
||||||
pagePopup.value.close();
|
pagePopup.value.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Expose clearSelectedPages for PagePopup to call when closing
|
||||||
|
const handlePagePopupClose = () => {
|
||||||
|
clearSelectedPages();
|
||||||
|
};
|
||||||
|
|
||||||
const renderPreview = async (shouldReloadFromFile = false) => {
|
const renderPreview = async (shouldReloadFromFile = false) => {
|
||||||
if (isTransitioning.value) return;
|
if (isTransitioning.value) return;
|
||||||
isTransitioning.value = true;
|
isTransitioning.value = true;
|
||||||
|
|
@ -228,7 +266,7 @@ onMounted(() => renderPreview(true));
|
||||||
<PreviewLoader :isLoading="isTransitioning" :shifted="activeTab.length > 0" />
|
<PreviewLoader :isLoading="isTransitioning" :shifted="activeTab.length > 0" />
|
||||||
|
|
||||||
<ElementPopup ref="elementPopup" :iframeRef="activeFrame" />
|
<ElementPopup ref="elementPopup" :iframeRef="activeFrame" />
|
||||||
<PagePopup ref="pagePopup" :iframeRef="activeFrame" />
|
<PagePopup ref="pagePopup" :iframeRef="activeFrame" @close="handlePagePopupClose" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<span class="page-label">@page</span>
|
<span class="page-label">@page</span>
|
||||||
<span class="page-name">geoformat</span>
|
<span class="page-name">geoformat</span>
|
||||||
<span class="page-count">6 pages</span>
|
<span class="page-count">{{ pageCount }} page{{ pageCount > 1 ? 's' : '' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="close-btn" @click="close">×</button>
|
<button class="close-btn" @click="close">×</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -298,9 +298,12 @@ const props = defineProps({
|
||||||
iframeRef: Object,
|
iframeRef: Object,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const position = ref({ x: 0, y: 0 });
|
const position = ref({ x: 0, y: 0 });
|
||||||
const selectedPageElement = ref(null);
|
const selectedPageElement = ref(null);
|
||||||
|
const pageCount = ref(0);
|
||||||
const isEditable = ref(false);
|
const isEditable = ref(false);
|
||||||
const inheritanceLocked = ref(true);
|
const inheritanceLocked = ref(true);
|
||||||
const backgroundColorInput = ref(null);
|
const backgroundColorInput = ref(null);
|
||||||
|
|
@ -462,13 +465,11 @@ const loadValuesFromStylesheet = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const open = (pageElement, event) => {
|
const open = (pageElement, event, count = 1) => {
|
||||||
selectedPageElement.value = pageElement;
|
selectedPageElement.value = pageElement;
|
||||||
|
pageCount.value = count;
|
||||||
position.value = calculatePosition(event);
|
position.value = calculatePosition(event);
|
||||||
|
|
||||||
// Add border to the selected page
|
|
||||||
pageElement.style.outline = '2px solid #61afef';
|
|
||||||
|
|
||||||
// Load values from stylesheet
|
// Load values from stylesheet
|
||||||
loadValuesFromStylesheet();
|
loadValuesFromStylesheet();
|
||||||
|
|
||||||
|
|
@ -507,13 +508,10 @@ const open = (pageElement, event) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
// Remove border from the selected page
|
selectedPageElement.value = null;
|
||||||
if (selectedPageElement.value) {
|
|
||||||
selectedPageElement.value.style.outline = '';
|
|
||||||
selectedPageElement.value = null;
|
|
||||||
}
|
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
isEditable.value = false;
|
isEditable.value = false;
|
||||||
|
emit('close');
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleInheritance = () => {
|
const toggleInheritance = () => {
|
||||||
|
|
@ -602,7 +600,7 @@ defineExpose({ open, close, visible });
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-label {
|
.page-label {
|
||||||
background: #ff8a50;
|
background: var(--color-page-highlight);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
@ -616,7 +614,7 @@ defineExpose({ open, close, visible });
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-count {
|
.page-count {
|
||||||
color: #ff8a50;
|
color: var(--color-page-highlight);
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue