refactor: extract App.vue logic into composables (762→230 lines)
All checks were successful
Deploy / Build and Deploy to Production (push) Successful in 14s
All checks were successful
Deploy / Build and Deploy to Production (push) Successful in 14s
Extracted complex logic from App.vue into focused, reusable composables: New composables: - useKeyboardShortcuts.js (~80 lines): Keyboard shortcuts (Cmd/Ctrl+S, P, Escape, \) - useIframeInteractions.js (~370 lines): Page/element hover, labels, clicks, popups - usePreviewRenderer.js (~160 lines): Double buffering, transitions, scroll persistence - usePrintPreview.js (~70 lines): Print dialog and style collection Benefits: - 70% reduction in App.vue size (532 lines extracted) - Better separation of concerns - Improved maintainability and testability - Clearer code organization Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
dac532a932
commit
be7bb66e70
5 changed files with 729 additions and 581 deletions
157
src/composables/usePreviewRenderer.js
Normal file
157
src/composables/usePreviewRenderer.js
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import { ref, watch } from 'vue';
|
||||
import Coloris from '@melloware/coloris';
|
||||
|
||||
/**
|
||||
* Composable for managing preview rendering with double buffering
|
||||
* Handles iframe transitions, scroll persistence, and PagedJS rendering
|
||||
*/
|
||||
export function usePreviewRenderer({
|
||||
previewFrame1,
|
||||
previewFrame2,
|
||||
stylesheetStore,
|
||||
narrativeStore,
|
||||
handleIframeMouseMove,
|
||||
handleIframeClick,
|
||||
}) {
|
||||
let savedScrollPercentage = 0;
|
||||
const currentFrameIndex = ref(1); // 1 or 2, which iframe is currently visible
|
||||
const isTransitioning = ref(false);
|
||||
let keyboardShortcutHandler = null;
|
||||
|
||||
/**
|
||||
* Render preview to hidden iframe with crossfade transition
|
||||
*/
|
||||
const renderPreview = async (shouldReloadFromFile = false) => {
|
||||
if (isTransitioning.value) return;
|
||||
isTransitioning.value = true;
|
||||
|
||||
// Determine which iframe is currently visible and which to render to
|
||||
const visibleFrame =
|
||||
currentFrameIndex.value === 1 ? previewFrame1.value : previewFrame2.value;
|
||||
const hiddenFrame =
|
||||
currentFrameIndex.value === 1 ? previewFrame2.value : previewFrame1.value;
|
||||
|
||||
if (!hiddenFrame) {
|
||||
isTransitioning.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Save scroll position from visible frame
|
||||
if (
|
||||
visibleFrame &&
|
||||
visibleFrame.contentWindow &&
|
||||
visibleFrame.contentDocument
|
||||
) {
|
||||
const scrollTop = visibleFrame.contentWindow.scrollY || 0;
|
||||
const scrollHeight =
|
||||
visibleFrame.contentDocument.documentElement.scrollHeight;
|
||||
const clientHeight = visibleFrame.contentWindow.innerHeight;
|
||||
const maxScroll = scrollHeight - clientHeight;
|
||||
|
||||
savedScrollPercentage = maxScroll > 0 ? scrollTop / maxScroll : 0;
|
||||
}
|
||||
|
||||
if (shouldReloadFromFile || !stylesheetStore.content) {
|
||||
await stylesheetStore.loadStylesheet();
|
||||
}
|
||||
|
||||
// Render to the hidden frame
|
||||
hiddenFrame.srcdoc = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/assets/css/pagedjs-interface.css">
|
||||
<style id="dynamic-styles">${stylesheetStore.content}</style>
|
||||
<script src="https://unpkg.com/pagedjs/dist/paged.polyfill.js"><\/script>
|
||||
</head>
|
||||
<body>${document.getElementById('content-source').innerHTML}</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
hiddenFrame.onload = () => {
|
||||
// Add event listeners for page and element interactions
|
||||
hiddenFrame.contentDocument.addEventListener(
|
||||
'mousemove',
|
||||
handleIframeMouseMove
|
||||
);
|
||||
hiddenFrame.contentDocument.addEventListener('click', handleIframeClick);
|
||||
|
||||
// Add keyboard shortcut listener to iframe (for when focus is inside iframe)
|
||||
if (keyboardShortcutHandler) {
|
||||
hiddenFrame.contentDocument.addEventListener('keydown', keyboardShortcutHandler);
|
||||
}
|
||||
|
||||
// Close Coloris when clicking in the iframe
|
||||
hiddenFrame.contentDocument.addEventListener('click', () => {
|
||||
Coloris.close();
|
||||
});
|
||||
|
||||
// Wait for PagedJS to finish rendering
|
||||
setTimeout(() => {
|
||||
// Restore scroll position
|
||||
const scrollHeight =
|
||||
hiddenFrame.contentDocument.documentElement.scrollHeight;
|
||||
const clientHeight = hiddenFrame.contentWindow.innerHeight;
|
||||
const maxScroll = scrollHeight - clientHeight;
|
||||
const targetScroll = savedScrollPercentage * maxScroll;
|
||||
|
||||
hiddenFrame.contentWindow.scrollTo(0, targetScroll);
|
||||
|
||||
// Start crossfade transition
|
||||
setTimeout(() => {
|
||||
// Make hidden frame visible (it's already behind)
|
||||
hiddenFrame.style.opacity = '1';
|
||||
hiddenFrame.style.zIndex = '1';
|
||||
|
||||
// Fade out visible frame
|
||||
if (visibleFrame) {
|
||||
visibleFrame.style.opacity = '0';
|
||||
}
|
||||
|
||||
// After fade completes, swap the frames
|
||||
setTimeout(() => {
|
||||
if (visibleFrame) {
|
||||
visibleFrame.style.zIndex = '0';
|
||||
}
|
||||
|
||||
// Swap current frame
|
||||
currentFrameIndex.value = currentFrameIndex.value === 1 ? 2 : 1;
|
||||
isTransitioning.value = false;
|
||||
}, 200); // Match CSS transition duration
|
||||
}, 50); // Small delay to ensure scroll is set
|
||||
}, 200); // Wait for PagedJS
|
||||
};
|
||||
};
|
||||
|
||||
// Watch for stylesheet changes and re-render
|
||||
watch(
|
||||
() => stylesheetStore.content,
|
||||
() => {
|
||||
renderPreview();
|
||||
}
|
||||
);
|
||||
|
||||
// Re-render when narrative data changes
|
||||
watch(
|
||||
() => narrativeStore.data,
|
||||
() => {
|
||||
if (narrativeStore.data) {
|
||||
renderPreview();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Set the keyboard shortcut handler (called after keyboard shortcuts composable is initialized)
|
||||
*/
|
||||
const setKeyboardShortcutHandler = (handler) => {
|
||||
keyboardShortcutHandler = handler;
|
||||
};
|
||||
|
||||
return {
|
||||
renderPreview,
|
||||
currentFrameIndex,
|
||||
isTransitioning,
|
||||
setKeyboardShortcutHandler,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue