refactor: make EditorPanel and ElementPopup fully autonomous

- Move all popup logic into ElementPopup component (state, positioning, click handling)
- Make EditorPanel autonomous with direct store access
- Simplify App.vue by removing prop drilling and intermediary logic
- Update EditorPanel to control paragraph font-size instead of .about
- Fix CSS parsing: escape selectors in extractCssValue and updateCssValue
- Remove hardcoded .about references from PagedJsWrapper

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
isUnknown 2025-11-24 18:18:27 +01:00
parent 7bc0dad32b
commit ae8136a48e
6 changed files with 82 additions and 116 deletions

View file

@ -47,18 +47,14 @@ const injectStylesToIframe = () => {
styleElement.textContent = stylesheetStore.content;
};
const elementPopup = ref(null);
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>
@ -73,68 +69,10 @@ const renderPreview = async () => {
`;
iframe.onload = () => {
iframe.contentDocument.addEventListener('click', handleIframeClick);
iframe.contentDocument.addEventListener('click', elementPopup.value.handleIframeClick);
};
};
// ============================================================================
// 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
// ============================================================================
@ -147,22 +85,13 @@ onMounted(renderPreview);
<PagedJsWrapper />
</div>
<EditorPanel
:fontSize="aboutFontSize"
:unit="aboutFontSizeUnit"
@update:fontSize="aboutFontSize = $event"
/>
<EditorPanel />
<iframe ref="previewFrame" id="preview-frame"></iframe>
<StylesheetViewer :stylesheet="stylesheetStore.content" />
<ElementPopup
:visible="popupVisible"
:position="popupPosition"
:selector="popupSelector"
@close="closePopup"
/>
<ElementPopup ref="elementPopup" :iframeRef="previewFrame" />
</template>
<style>