feat: implement template-specific CSS inheritance in PagePopup

- Add templateName extracted from data-page-type attribute
- When unlocked: edits create/update @page <templateName> block
- When re-locked: remove template block, preview returns to @page
- Fields retain their values when re-locking (for user convenience)
- Display dynamic template name in popup header
- Show template-specific CSS block when unlocked

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
isUnknown 2025-12-08 12:38:16 +01:00
parent ed856972bc
commit 30d1d26d15

View file

@ -7,7 +7,7 @@
<div class="popup-header">
<div class="header-left">
<span class="page-label">@page</span>
<span class="page-name">geoformat</span>
<span class="page-name">{{ templateName || 'default' }}</span>
<span class="page-count">{{ pageCount }} page{{ pageCount > 1 ? 's' : '' }}</span>
</div>
<button class="close-btn" @click="close">×</button>
@ -304,6 +304,7 @@ const visible = ref(false);
const position = ref({ x: 0, y: 0 });
const selectedPageElement = ref(null);
const pageCount = ref(0);
const templateName = ref('');
const isEditable = ref(false);
const inheritanceLocked = ref(true);
const backgroundColorInput = ref(null);
@ -339,27 +340,89 @@ const POPUP_HEIGHT = 600;
const { calculatePosition } = usePopupPosition(POPUP_WIDTH, POPUP_HEIGHT);
// Get the selector for the current template's @page rule
const getTemplateSelector = () => {
return templateName.value ? `@page ${templateName.value}` : '@page';
};
// Get or create the template-specific @page block
const getOrCreateTemplateBlock = () => {
const selector = getTemplateSelector();
let block = stylesheetStore.extractBlock(selector);
if (!block && templateName.value) {
// Create new block with current values from @page
const baseBlock = stylesheetStore.extractBlock('@page');
if (baseBlock) {
// Insert the new template block after @page
const marginValue = `${margins.value.top.value}${margins.value.top.unit} ${margins.value.right.value}${margins.value.right.unit} ${margins.value.bottom.value}${margins.value.bottom.unit} ${margins.value.left.value}${margins.value.left.unit}`;
const newBlock = `\n@page ${templateName.value} {\n margin: ${marginValue};${background.value.value ? `\n background: ${background.value.value};` : ''}\n}\n`;
stylesheetStore.content = stylesheetStore.content.replace(
baseBlock,
baseBlock + newBlock
);
block = stylesheetStore.extractBlock(selector);
}
}
return block;
};
// Remove the template-specific @page block
const removeTemplateBlock = () => {
if (!templateName.value) return;
const selector = `@page ${templateName.value}`;
const block = stylesheetStore.extractBlock(selector);
if (block) {
// Remove the block and any surrounding whitespace
stylesheetStore.content = stylesheetStore.content.replace(
new RegExp(`\\n?${selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\\{[^}]*\\}\\n?`),
'\n'
);
}
};
const updateMargins = () => {
// Only update if inheritance is unlocked
if (inheritanceLocked.value) return;
const marginValue = `${margins.value.top.value}${margins.value.top.unit} ${margins.value.right.value}${margins.value.right.unit} ${margins.value.bottom.value}${margins.value.bottom.unit} ${margins.value.left.value}${margins.value.left.unit}`;
const currentBlock = stylesheetStore.extractBlock('@page');
const currentBlock = getOrCreateTemplateBlock();
if (!currentBlock) return;
const updatedBlock = currentBlock.replace(
/(margin:\s*)[^;]+/,
`$1${marginValue}`
);
const selector = getTemplateSelector();
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
if (currentBlock.includes('margin:')) {
const updatedBlock = currentBlock.replace(
/(margin:\s*)[^;]+/,
`$1${marginValue}`
);
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
} else {
const updatedBlock = currentBlock.replace(
/(\s*})$/,
` margin: ${marginValue};\n$1`
);
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
}
};
const updateBackground = () => {
// Only update if inheritance is unlocked
if (inheritanceLocked.value) return;
if (!background.value.value) return;
const currentBlock = stylesheetStore.extractBlock('@page');
const currentBlock = getOrCreateTemplateBlock();
if (!currentBlock) return;
if (currentBlock.includes('background:')) {
@ -468,7 +531,13 @@ const open = (pageElement, event, count = 1) => {
pageCount.value = count;
position.value = calculatePosition(event);
// Load values from stylesheet
// Extract template name from data-page-type attribute
templateName.value = pageElement.getAttribute('data-page-type') || '';
// Reset inheritance state when opening
inheritanceLocked.value = true;
// Load values from stylesheet (@page block)
loadValuesFromStylesheet();
visible.value = true;
@ -513,11 +582,23 @@ const close = () => {
};
const toggleInheritance = () => {
const wasLocked = inheritanceLocked.value;
inheritanceLocked.value = !inheritanceLocked.value;
// TODO: Implement CSS priority logic when unlocked
if (inheritanceLocked.value && !wasLocked) {
// Re-locking: remove the template-specific block
// Fields keep their values, but preview returns to @page defaults
removeTemplateBlock();
}
// When unlocking: fields already have values, block will be created on first edit
};
const pageCss = computed(() => {
// Show template-specific block if unlocked and exists, otherwise show @page
if (!inheritanceLocked.value && templateName.value) {
const templateBlock = stylesheetStore.extractBlock(`@page ${templateName.value}`);
if (templateBlock) return templateBlock;
}
return stylesheetStore.extractBlock('@page') || '';
});