refactor: reorganize App.vue by functional domains
- Group variables and functions by domain instead of by type - Split handleIframeClick into smaller focused functions - Add clear section comments for better navigation - Sections: Store, PagedJS config, Iframe preview, Editor panel, Element popup, Lifecycle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e8298a9fbf
commit
7bc0dad32b
1 changed files with 73 additions and 48 deletions
121
src/App.vue
121
src/App.vue
|
|
@ -6,17 +6,14 @@ import ElementPopup from './components/ElementPopup.vue';
|
|||
import { onMounted, ref, watch } from 'vue';
|
||||
import { useStylesheetStore } from './stores/stylesheet';
|
||||
|
||||
// ============================================================================
|
||||
// Store
|
||||
// ============================================================================
|
||||
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
|
||||
// ============================================================================
|
||||
// PagedJS configuration
|
||||
// ============================================================================
|
||||
const printStyles = `
|
||||
h2 { break-before: page; }
|
||||
|
||||
|
|
@ -32,6 +29,11 @@ h2 { break-before: page; }
|
|||
.chapter > h2 { string-set: title content(text); }
|
||||
`;
|
||||
|
||||
// ============================================================================
|
||||
// Iframe preview
|
||||
// ============================================================================
|
||||
const previewFrame = ref(null);
|
||||
|
||||
const injectStylesToIframe = () => {
|
||||
const iframe = previewFrame.value;
|
||||
if (!iframe?.contentDocument) return;
|
||||
|
|
@ -45,45 +47,6 @@ const injectStylesToIframe = () => {
|
|||
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;
|
||||
|
|
@ -114,6 +77,68 @@ const renderPreview = async () => {
|
|||
};
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Editor panel (temporary hardcoded .about selector)
|
||||
// ============================================================================
|
||||
const aboutFontSize = ref(2);
|
||||
const aboutFontSizeUnit = ref('rem');
|
||||
|
||||
watch(aboutFontSize, (newVal) => {
|
||||
stylesheetStore.updateProperty(
|
||||
'.about',
|
||||
'font-size',
|
||||
newVal,
|
||||
aboutFontSizeUnit.value
|
||||
);
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Element popup
|
||||
// ============================================================================
|
||||
const popupVisible = ref(false);
|
||||
const popupPosition = ref({ x: 0, y: 0 });
|
||||
const popupSelector = ref('');
|
||||
|
||||
const getSelectorFromElement = (element) => {
|
||||
return element.id
|
||||
? `#${element.id}`
|
||||
: `.${element.className.split(' ')[0]}`;
|
||||
};
|
||||
|
||||
const calculatePopupPosition = (element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const iframeRect = previewFrame.value.getBoundingClientRect();
|
||||
return {
|
||||
x: iframeRect.left + rect.left,
|
||||
y: iframeRect.top + rect.bottom + 5,
|
||||
};
|
||||
};
|
||||
|
||||
const openPopup = (element) => {
|
||||
popupSelector.value = getSelectorFromElement(element);
|
||||
popupPosition.value = calculatePopupPosition(element);
|
||||
popupVisible.value = true;
|
||||
};
|
||||
|
||||
const closePopup = () => {
|
||||
popupVisible.value = false;
|
||||
};
|
||||
|
||||
const handleIframeClick = (event) => {
|
||||
const element = event.target;
|
||||
|
||||
if (element.tagName === 'BODY' || element.tagName === 'HTML') {
|
||||
closePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
openPopup(element);
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Lifecycle
|
||||
// ============================================================================
|
||||
watch(() => stylesheetStore.content, injectStylesToIframe);
|
||||
onMounted(renderPreview);
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue