repair panel CSS
This commit is contained in:
parent
89e5fe6612
commit
564de8aba8
3 changed files with 141 additions and 16 deletions
24
src/App.vue
24
src/App.vue
|
|
@ -5,6 +5,7 @@ import ElementPopup from './components/ElementPopup.vue';
|
||||||
// import PagePopup from './components/PagePopup.vue'; // DISABLED: page template styling feature
|
// import PagePopup from './components/PagePopup.vue'; // DISABLED: page template styling feature
|
||||||
import PreviewLoader from './components/PreviewLoader.vue';
|
import PreviewLoader from './components/PreviewLoader.vue';
|
||||||
import SaveButton from './components/SaveButton.vue';
|
import SaveButton from './components/SaveButton.vue';
|
||||||
|
import PrintButton from './components/PrintButton.vue';
|
||||||
import { onMounted, ref, computed, provide } from 'vue';
|
import { onMounted, ref, computed, provide } from 'vue';
|
||||||
import { useStylesheetStore } from './stores/stylesheet';
|
import { useStylesheetStore } from './stores/stylesheet';
|
||||||
import { useNarrativeStore } from './stores/narrative';
|
import { useNarrativeStore } from './stores/narrative';
|
||||||
|
|
@ -107,7 +108,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
<PreviewLoader :isLoading="isTransitioning" :shifted="activeTab.length > 0" />
|
<PreviewLoader :isLoading="isTransitioning" :shifted="activeTab.length > 0" />
|
||||||
|
|
||||||
<SaveButton />
|
|
||||||
|
|
||||||
<ElementPopup
|
<ElementPopup
|
||||||
ref="elementPopup"
|
ref="elementPopup"
|
||||||
|
|
@ -122,21 +123,12 @@ onMounted(async () => {
|
||||||
/>
|
/>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<button
|
|
||||||
class="print-btn"
|
<div id="group-btn">
|
||||||
@click="printPreview"
|
<SaveButton />
|
||||||
:title="`Imprimer (${isMac ? '⌘' : 'Ctrl'}+P)`"
|
<PrintButton :printPreview="printPreview" />
|
||||||
>
|
</div>
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M17 2H7V6H17V2ZM19 8H5C3.34 8 2 9.34 2 11V17H6V21H18V17H22V11C22 9.34 20.66 8 19 8ZM16 19H8V14H16V19ZM19 12C18.45 12 18 11.55 18 11C18 10.45 18.45 10 19 10C19.55 10 20 10.45 20 11C20 11.55 19.55 12 19 12Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
||||||
|
|
@ -805,11 +805,115 @@ watch(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Parse CSS property-value pairs from a block text string
|
||||||
|
const parseCssProperties = (cssText) => {
|
||||||
|
const result = {};
|
||||||
|
const match = cssText.match(/\{([\s\S]*)\}/);
|
||||||
|
if (!match) return result;
|
||||||
|
for (const decl of match[1].split(';')) {
|
||||||
|
const clean = decl.replace(/\/\*[\s\S]*?\*\//g, '').trim();
|
||||||
|
if (!clean) continue;
|
||||||
|
const idx = clean.indexOf(':');
|
||||||
|
if (idx === -1) continue;
|
||||||
|
const prop = clean.substring(0, idx).trim();
|
||||||
|
const val = clean.substring(idx + 1).trim();
|
||||||
|
if (prop && val) result[prop] = val;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sync refs from edited CSS text (bidirectional: textarea → inputs)
|
||||||
|
// For special groups: if user adds/modifies a property, it becomes a "user value" (toggle ON)
|
||||||
|
const syncRefsFromCss = (cssText) => {
|
||||||
|
const parsed = parseCssProperties(cssText);
|
||||||
|
const specialGroupNames = ['font', 'fontSize', 'lineHeight', 'color'];
|
||||||
|
|
||||||
|
for (const [group, cssProps] of Object.entries(settingGroups)) {
|
||||||
|
const hasAnyProp = cssProps.some(p => p in parsed);
|
||||||
|
|
||||||
|
if (hasAnyProp) {
|
||||||
|
// Update refs from parsed values
|
||||||
|
for (const prop of styleProps) {
|
||||||
|
if (prop.group !== group || !(prop.css in parsed)) continue;
|
||||||
|
if (prop.css === 'font-family') {
|
||||||
|
// Take first font from the stack, strip quotes
|
||||||
|
const firstFont = parsed[prop.css].split(',')[0].trim().replace(/['"]/g, '');
|
||||||
|
fontFamily.value = firstFont;
|
||||||
|
} else {
|
||||||
|
prop.set(parsed[prop.css]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const prop of unitProps) {
|
||||||
|
if (prop.group !== group || !(prop.css in parsed)) continue;
|
||||||
|
const m = parsed[prop.css].match(/([\d.]+)(px|rem|em|mm|cm|in)?/);
|
||||||
|
if (m) {
|
||||||
|
prop.ref.value = parseFloat(m[1]);
|
||||||
|
prop.ref.unit = m[2] || 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (group === 'margin') {
|
||||||
|
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||||
|
if (`margin-${side}` in parsed) {
|
||||||
|
const m = parsed[`margin-${side}`].match(/([\d.]+)(px|rem|em|mm|cm|in)?/);
|
||||||
|
if (m) { margin[side].value = parseFloat(m[1]); margin[side].unit = m[2] || 'mm'; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (group === 'padding') {
|
||||||
|
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||||
|
if (`padding-${side}` in parsed) {
|
||||||
|
const m = parsed[`padding-${side}`].match(/([\d.]+)(px|rem|em|mm|cm|in)?/);
|
||||||
|
if (m) { padding[side].value = parseFloat(m[1]); padding[side].unit = m[2] || 'mm'; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-enable toggle if it was OFF (user explicitly wrote the property)
|
||||||
|
if (!settingEnabled[group]) {
|
||||||
|
settingEnabled[group] = true;
|
||||||
|
if (specialGroupNames.includes(group)) {
|
||||||
|
settingCache[group] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (settingEnabled[group]) {
|
||||||
|
// All properties from this group were removed → disable toggle
|
||||||
|
settingEnabled[group] = false;
|
||||||
|
// For special groups, reset to defaults (user intentionally removed)
|
||||||
|
if (group === 'font') {
|
||||||
|
fontFamily.value = textDefaults.fontFamily;
|
||||||
|
italic.value = false;
|
||||||
|
bold.value = false;
|
||||||
|
settingCache.font = null;
|
||||||
|
} else if (group === 'fontSize') {
|
||||||
|
fontSize.value = textDefaults.fontSize.value;
|
||||||
|
fontSize.unit = textDefaults.fontSize.unit;
|
||||||
|
settingCache.fontSize = null;
|
||||||
|
} else if (group === 'lineHeight') {
|
||||||
|
lineHeight.value = textDefaults.lineHeight.value;
|
||||||
|
lineHeight.unit = textDefaults.lineHeight.unit;
|
||||||
|
settingCache.lineHeight = null;
|
||||||
|
} else if (group === 'color') {
|
||||||
|
color.value = textDefaults.color;
|
||||||
|
settingCache.color = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveElementState();
|
||||||
|
};
|
||||||
|
|
||||||
const handleCssInput = (newCss) => {
|
const handleCssInput = (newCss) => {
|
||||||
|
isUpdatingFromStore = true;
|
||||||
const oldBlock = elementCss.value;
|
const oldBlock = elementCss.value;
|
||||||
if (oldBlock) {
|
if (oldBlock) {
|
||||||
stylesheetStore.replaceInCustomCss(oldBlock, newCss);
|
stylesheetStore.replaceInCustomCss(oldBlock, newCss);
|
||||||
|
} else if (newCss.trim()) {
|
||||||
|
stylesheetStore.setCustomCss(
|
||||||
|
(stylesheetStore.customCss ? stylesheetStore.customCss + '\n' : '') + newCss
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
syncRefsFromCss(newCss);
|
||||||
|
nextTick(() => { isUpdatingFromStore = false; });
|
||||||
};
|
};
|
||||||
|
|
||||||
// Re-apply stored enabled properties for all elements with active toggles
|
// Re-apply stored enabled properties for all elements with active toggles
|
||||||
|
|
@ -849,6 +953,7 @@ watch(
|
||||||
() => stylesheetStore.customCss,
|
() => stylesheetStore.customCss,
|
||||||
() => {
|
() => {
|
||||||
if (isUpdatingFromStore) return;
|
if (isUpdatingFromStore) return;
|
||||||
|
if (stylesheetStore.isEditing) return; // Don't fight with user's CSS edits in textarea
|
||||||
isUpdatingFromStore = true;
|
isUpdatingFromStore = true;
|
||||||
reapplyStoredEnabledGroups();
|
reapplyStoredEnabledGroups();
|
||||||
nextTick(() => { isUpdatingFromStore = false; });
|
nextTick(() => { isUpdatingFromStore = false; });
|
||||||
|
|
|
||||||
28
src/components/PrintButton.vue
Normal file
28
src/components/PrintButton.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
class="print-btn"
|
||||||
|
@click="printPreview"
|
||||||
|
:title="`Imprimer (${isMac ? '⌘' : 'Ctrl'}+P)`"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M17 2H7V6H17V2ZM19 8H5C3.34 8 2 9.34 2 11V17H6V21H18V17H22V11C22 9.34 20.66 8 19 8ZM16 19H8V14H16V19ZM19 12C18.45 12 18 11.55 18 11C18 10.45 18.45 10 19 10C19.55 10 20 10.45 20 11C20 11.55 19.55 12 19 12Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
printPreview: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const isMac = typeof navigator !== 'undefined' && navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
||||||
|
</script>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue