All checks were successful
Deploy / Build and Deploy to Production (push) Successful in 26s
207 lines
5.2 KiB
Vue
207 lines
5.2 KiB
Vue
<script setup>
|
|
import PagedJsWrapper from './components/PagedJsWrapper.vue';
|
|
import EditorPanel from './components/editor/EditorPanel.vue';
|
|
import ElementPopup from './components/ElementPopup.vue';
|
|
import ImagePopup from './components/ImagePopup.vue';
|
|
// import PagePopup from './components/PagePopup.vue'; // DISABLED: page template styling feature
|
|
import PreviewLoader from './components/PreviewLoader.vue';
|
|
import SaveButton from './components/SaveButton.vue';
|
|
import PrintButton from './components/PrintButton.vue';
|
|
import ZoomControls from './components/ui/ZoomControls.vue';
|
|
import { onMounted, ref, computed, provide } from 'vue';
|
|
import { useStylesheetStore } from './stores/stylesheet';
|
|
import { useNarrativeStore } from './stores/narrative';
|
|
import { useKeyboardShortcuts } from './composables/useKeyboardShortcuts';
|
|
import { useIframeInteractions } from './composables/useIframeInteractions';
|
|
import { usePreviewRenderer } from './composables/usePreviewRenderer';
|
|
import { usePrintPreview } from './composables/usePrintPreview';
|
|
import { useProjectFonts } from './composables/useProjectFonts';
|
|
|
|
const stylesheetStore = useStylesheetStore();
|
|
const narrativeStore = useNarrativeStore();
|
|
const { loadFontsFromCss } = useProjectFonts();
|
|
|
|
const previewFrame1 = ref(null);
|
|
const previewFrame2 = ref(null);
|
|
const elementPopup = ref(null);
|
|
const imagePopup = ref(null);
|
|
// const pagePopup = ref(null); // DISABLED: page template styling feature
|
|
const activeTab = ref('');
|
|
|
|
provide('activeTab', activeTab);
|
|
|
|
// Setup iframe interactions (hover, click, labels)
|
|
const {
|
|
// hoveredPage, // DISABLED: page template styling feature
|
|
// selectedPages, // DISABLED: page template styling feature
|
|
hoveredElement,
|
|
selectedElement,
|
|
handleIframeMouseMove,
|
|
handleIframeClick,
|
|
// handlePagePopupClose, // DISABLED: page template styling feature
|
|
handleElementPopupClose,
|
|
handleImagePopupClose,
|
|
} = useIframeInteractions({ elementPopup, imagePopup });
|
|
|
|
// Setup preview renderer with double buffering
|
|
const {
|
|
renderPreview,
|
|
currentFrameIndex,
|
|
isTransitioning,
|
|
setKeyboardShortcutHandler,
|
|
} = usePreviewRenderer({
|
|
previewFrame1,
|
|
previewFrame2,
|
|
stylesheetStore,
|
|
narrativeStore,
|
|
handleIframeMouseMove,
|
|
handleIframeClick,
|
|
});
|
|
|
|
const activeFrame = computed(() => {
|
|
return currentFrameIndex.value === 1
|
|
? previewFrame1.value
|
|
: previewFrame2.value;
|
|
});
|
|
|
|
// Setup print preview
|
|
const { printPreview } = usePrintPreview(activeFrame);
|
|
|
|
// Setup keyboard shortcuts (depends on printPreview)
|
|
const { handleKeyboardShortcut, isMac } = useKeyboardShortcuts({
|
|
stylesheetStore,
|
|
elementPopup,
|
|
// pagePopup, // DISABLED: page template styling feature
|
|
activeTab,
|
|
printPreview,
|
|
});
|
|
|
|
// Attach keyboard shortcut handler to renderer
|
|
setKeyboardShortcutHandler(handleKeyboardShortcut);
|
|
|
|
// Zoom
|
|
const zoomControls = ref(null);
|
|
const zoomStyle = computed(() => zoomControls.value?.zoomStyle ?? {});
|
|
|
|
// Lifecycle: Initialize app on mount
|
|
onMounted(async () => {
|
|
// Load narrative data (narrativeUrl constructed from location, always present)
|
|
await narrativeStore.loadNarrative(location.href + '.json');
|
|
|
|
// Initialize stylesheet with custom CSS
|
|
if (narrativeStore.data) {
|
|
await stylesheetStore.initializeFromNarrative(narrativeStore.data);
|
|
// Pre-load any fonts referenced in the saved CSS so @font-face rules are ready for the preview
|
|
await loadFontsFromCss(stylesheetStore.customCss);
|
|
}
|
|
|
|
// Render preview after data is loaded
|
|
renderPreview(true);
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div id="content-source" style="display: none">
|
|
<PagedJsWrapper />
|
|
</div>
|
|
|
|
<EditorPanel />
|
|
|
|
<iframe
|
|
ref="previewFrame1"
|
|
class="preview-frame"
|
|
:class="{ shifted: activeTab.length > 0 }"
|
|
:style="zoomStyle"
|
|
></iframe>
|
|
<iframe
|
|
ref="previewFrame2"
|
|
class="preview-frame"
|
|
:class="{ shifted: activeTab.length > 0 }"
|
|
:style="zoomStyle"
|
|
></iframe>
|
|
|
|
<PreviewLoader :isLoading="isTransitioning" :shifted="activeTab.length > 0" />
|
|
|
|
|
|
|
|
<ElementPopup
|
|
ref="elementPopup"
|
|
:iframeRef="activeFrame"
|
|
@close="handleElementPopupClose"
|
|
/>
|
|
|
|
<ImagePopup
|
|
ref="imagePopup"
|
|
@close="handleImagePopupClose"
|
|
/>
|
|
<!-- DISABLED: page template styling feature
|
|
<PagePopup
|
|
ref="pagePopup"
|
|
:iframeRef="activeFrame"
|
|
@close="handlePagePopupClose"
|
|
/>
|
|
-->
|
|
|
|
|
|
<ZoomControls ref="zoomControls" />
|
|
|
|
<div id="actions-btn">
|
|
<SaveButton />
|
|
<PrintButton :printPreview="printPreview" />
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<style>
|
|
.preview-frame {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
border: none;
|
|
transform-origin: top center;
|
|
}
|
|
|
|
|
|
/* .preview-frame.shifted {
|
|
margin-left: 17.55rem;
|
|
transform: scale(0.65) translateY(-40vh);
|
|
height: 155vh;
|
|
} */
|
|
|
|
.preview-frame:nth-of-type(1) {
|
|
z-index: 1;
|
|
opacity: 1;
|
|
}
|
|
|
|
.preview-frame:nth-of-type(2) {
|
|
z-index: 0;
|
|
opacity: 0;
|
|
}
|
|
|
|
|
|
/* Hide UI elements when printing */
|
|
@media print {
|
|
#editor-panel,
|
|
#element-popup,
|
|
#page-popup,
|
|
#actions-btn,
|
|
.preview-loader,
|
|
.print-btn {
|
|
display: none !important;
|
|
}
|
|
|
|
.preview-frame {
|
|
position: static !important;
|
|
margin-left: 0 !important;
|
|
transform: none !important;
|
|
width: 100% !important;
|
|
height: auto !important;
|
|
}
|
|
|
|
.preview-frame:not(:first-of-type) {
|
|
display: none !important;
|
|
}
|
|
}
|
|
</style>
|