geoproject-app/src/App.vue

153 lines
3.6 KiB
Vue
Raw Normal View History

<script setup>
import PagedJsWrapper from './components/PagedJsWrapper.vue';
import EditorPanel from './components/EditorPanel.vue';
import StylesheetViewer from './components/StylesheetViewer.vue';
import ElementPopup from './components/ElementPopup.vue';
import { onMounted, ref, watch } from 'vue';
import { useStylesheetStore } from './stores/stylesheet';
const stylesheetStore = useStylesheetStore();
const previewFrame = ref(null);
const aboutFontSize = ref(2);
const aboutFontSizeUnit = ref('rem');
const popupVisible = ref(false);
const popupPosition = ref({ x: 0, y: 0 });
const popupSelector = ref('');
// PagedJS print rules
const printStyles = `
h2 { break-before: page; }
@page {
size: A4;
margin: 20mm 15mm 26mm 15mm;
}
@page {
@bottom-center { content: string(title); }
}
.chapter > h2 { string-set: title content(text); }
`;
const injectStylesToIframe = () => {
const iframe = previewFrame.value;
if (!iframe?.contentDocument) return;
let styleElement = iframe.contentDocument.getElementById('dynamic-styles');
if (!styleElement) {
styleElement = iframe.contentDocument.createElement('style');
styleElement.id = 'dynamic-styles';
iframe.contentDocument.head.appendChild(styleElement);
}
styleElement.textContent = stylesheetStore.content;
};
const handleIframeClick = (event) => {
const element = event.target;
if (element.tagName === 'BODY' || element.tagName === 'HTML') {
popupVisible.value = false;
return;
}
const selector = element.id
? `#${element.id}`
: `.${element.className.split(' ')[0]}`;
popupSelector.value = selector;
const rect = element.getBoundingClientRect();
const iframeRect = previewFrame.value.getBoundingClientRect();
popupPosition.value = {
x: iframeRect.left + rect.left,
y: iframeRect.top + rect.bottom + 5,
};
popupVisible.value = true;
};
const closePopup = () => {
popupVisible.value = false;
};
watch(aboutFontSize, (newVal) => {
stylesheetStore.updateProperty(
'.about',
'font-size',
newVal,
aboutFontSizeUnit.value
);
});
watch(() => stylesheetStore.content, injectStylesToIframe);
const renderPreview = async () => {
const iframe = previewFrame.value;
if (!iframe) return;
await stylesheetStore.loadStylesheet();
const initialFontSize = stylesheetStore.extractValue('.about', 'font-size');
if (initialFontSize) {
aboutFontSize.value = initialFontSize.value;
aboutFontSizeUnit.value = initialFontSize.unit;
}
iframe.srcdoc = `
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/assets/css/pagedjs-interface.css">
<style id="dynamic-styles">${stylesheetStore.content}</style>
<style>${printStyles}</style>
<script src="https://unpkg.com/pagedjs/dist/paged.polyfill.js"><\/script>
</head>
<body>${document.getElementById('content-source').innerHTML}</body>
</html>
`;
iframe.onload = () => {
iframe.contentDocument.addEventListener('click', handleIframeClick);
};
};
onMounted(renderPreview);
</script>
<template>
<div id="content-source" style="display: none">
<PagedJsWrapper />
</div>
<EditorPanel
:fontSize="aboutFontSize"
:unit="aboutFontSizeUnit"
@update:fontSize="aboutFontSize = $event"
/>
<iframe ref="previewFrame" id="preview-frame"></iframe>
<StylesheetViewer :stylesheet="stylesheetStore.content" />
<ElementPopup
:visible="popupVisible"
:position="popupPosition"
:selector="popupSelector"
@close="closePopup"
/>
</template>
<style>
#preview-frame {
position: fixed;
top: 0;
left: 250px;
width: calc(100% - 600px);
height: 100vh;
border: none;
}
</style>