diff --git a/src/components/ElementPopup.vue b/src/components/ElementPopup.vue index d469d6d..307b616 100644 --- a/src/components/ElementPopup.vue +++ b/src/components/ElementPopup.vue @@ -307,13 +307,7 @@ const color = ref('rgb(0, 0, 0)'); const background = ref('transparent'); const fontSize = reactive({ value: 23, unit: 'px' }); const fontSizeModel = computed({ - get: () => { - if (!settingEnabled.fontSize) { - // When disabled, display TextSettings' current value reactively - return { ...textDefaults.fontSize }; - } - return { value: fontSize.value, unit: fontSize.unit }; - }, + get: () => ({ value: fontSize.value, unit: fontSize.unit }), set: (v) => { fontSize.value = v.value; fontSize.unit = v.unit; }, }); const lineHeight = reactive({ value: 28, unit: 'px' }); @@ -343,20 +337,20 @@ const padding = reactive({ // Cache for special groups values when unchecked (to restore on re-check) const settingCache = reactive({ - font: null, // { fontFamily, italic, bold } - fontSize: null, // { value, unit } - color: null, // string + font: null, // { fontFamily, italic, bold } + fontSize: null, // { value, unit } + lineHeight: null, // { value, unit } + color: null, // string }); -// Per-subsection toggle state -// Special groups (font, fontSize, color): checked by default -// Other groups: unchecked by default +// Per-subsection toggle state — all unchecked by default +// Special groups (font, fontSize, lineHeight, color) appear in popup-css with "valeur par défaut" comment const settingEnabled = reactive({ - font: true, - fontSize: true, + font: false, + fontSize: false, lineHeight: false, textAlign: false, - color: true, + color: false, background: false, border: false, margin: false, @@ -552,7 +546,45 @@ const generatePreviewCss = () => { const displayedCss = computed(() => { if (!selector.value) return ''; - return elementCss.value || generatePreviewCss(); + + let base = elementCss.value || generatePreviewCss(); + if (!base) base = `${selector.value} {\n}`; + + // Special groups config for displayedCss annotation + const specialDisplayProps = [ + { group: 'fontSize', css: 'font-size', getValue: () => `${(settingEnabled.fontSize ? fontSize : textDefaults.fontSize).value}${(settingEnabled.fontSize ? fontSize : textDefaults.fontSize).unit}` }, + { group: 'lineHeight', css: 'line-height', getValue: () => `${(settingEnabled.lineHeight ? lineHeight : textDefaults.lineHeight).value}${(settingEnabled.lineHeight ? lineHeight : textDefaults.lineHeight).unit}` }, + { group: 'color', css: 'color', getValue: () => settingEnabled.color ? color.value : textDefaults.color }, + { group: 'font', css: 'font-family', getValue: () => `"${settingEnabled.font ? fontFamily.value : textDefaults.fontFamily}"` }, + ]; + + // For disabled special groups: annotate existing lines with comment + // For enabled special groups: remove stale comments + for (const sp of specialDisplayProps) { + if (!settingEnabled[sp.group] && base.includes(sp.css + ':')) { + const escaped = sp.css.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + base = base.replace(new RegExp(`(${escaped}:\\s*[^;]+;)(?!\\s*\\/\\*)`), '$1 /* valeur par défaut */'); + } + if (settingEnabled[sp.group]) { + const escaped = sp.css.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + base = base.replace(new RegExp(`(${escaped}:\\s*[^;]+;)\\s*\\/\\* valeur par défaut \\*\\/`), '$1'); + } + } + + // Inject missing special properties (if not yet in block at all) + const toInject = []; + for (const sp of specialDisplayProps) { + if (!base.includes(sp.css + ':')) { + const comment = !settingEnabled[sp.group] ? ' /* valeur par défaut */' : ''; + toInject.push(` ${sp.css}: ${sp.getValue()};${comment}`); + } + } + + if (toInject.length > 0) { + base = base.replace(/\}\s*$/, toInject.join('\n') + '\n}'); + } + + return base; }); // Apply all properties for a given group to the stylesheet @@ -587,22 +619,15 @@ const applyAllEnabledGroups = () => { }; // When unchecking a special group: save element value, load TextSettings fallback for display -const saveToCacheAndLoadFallback = (group) => { +const saveToCache = (group) => { if (group === 'fontSize') { settingCache.fontSize = { value: fontSize.value, unit: fontSize.unit }; - // Display is handled reactively by fontSizeModel computed + } else if (group === 'lineHeight') { + settingCache.lineHeight = { value: lineHeight.value, unit: lineHeight.unit }; } else if (group === 'font') { settingCache.font = { fontFamily: fontFamily.value, italic: italic.value, bold: bold.value }; - const ff = cssExtractValue('body', 'font-family'); - if (ff) fontFamily.value = ff.replace(/['"]/g, ''); - const fs = cssExtractValue('p', 'font-style'); - if (fs) italic.value = fs === 'italic'; - const fw = cssExtractValue('p', 'font-weight'); - if (fw) bold.value = fw === 'bold' || parseInt(fw) >= 700; } else if (group === 'color') { settingCache.color = color.value; - const c = cssExtractValue('body', 'color'); - if (c) color.value = c; } }; @@ -612,6 +637,10 @@ const restoreFromCache = (group) => { fontSize.value = settingCache.fontSize.value; fontSize.unit = settingCache.fontSize.unit; settingCache.fontSize = null; + } else if (group === 'lineHeight' && settingCache.lineHeight) { + lineHeight.value = settingCache.lineHeight.value; + lineHeight.unit = settingCache.lineHeight.unit; + settingCache.lineHeight = null; } else if (group === 'font' && settingCache.font) { fontFamily.value = settingCache.font.fontFamily; italic.value = settingCache.font.italic; @@ -623,18 +652,38 @@ const restoreFromCache = (group) => { } }; +// Replace a special group's CSS values with TextSettings defaults +const applyDefaultsForGroup = (group) => { + if (!selector.value) return; + if (group === 'fontSize') { + updateProp('font-size', textDefaults.fontSize.value, textDefaults.fontSize.unit); + } else if (group === 'lineHeight') { + updateProp('line-height', textDefaults.lineHeight.value, textDefaults.lineHeight.unit); + } else if (group === 'color') { + updateProp('color', textDefaults.color); + } else if (group === 'font') { + updateProp('font-family', `"${textDefaults.fontFamily}"`); + removeProps(['font-style', 'font-weight']); + } +}; + // Toggle a setting group on/off const onToggleSetting = (group, enabled) => { settingEnabled[group] = enabled; + isUpdatingFromStore = true; if (enabled) { - isUpdatingFromStore = true; restoreFromCache(group); - isUpdatingFromStore = false; applyAllEnabledGroups(); } else { - saveToCacheAndLoadFallback(group); - removeProps(settingGroups[group]); + saveToCache(group); + const specialGroups = ['font', 'fontSize', 'lineHeight', 'color']; + if (specialGroups.includes(group)) { + applyDefaultsForGroup(group); + } else { + removeProps(settingGroups[group]); + } } + nextTick(() => { isUpdatingFromStore = false; }); }; // Watchers — simple props (with group guard) @@ -744,19 +793,6 @@ watch( const loadValuesFromStylesheet = (isInitialLoad = false) => { if (!selector.value) return; - if (isInitialLoad) { - // Reset settingEnabled to defaults only on initial open - settingEnabled.font = true; - settingEnabled.fontSize = true; - settingEnabled.lineHeight = false; - settingEnabled.textAlign = false; - settingEnabled.color = true; - settingEnabled.background = false; - settingEnabled.border = false; - settingEnabled.margin = false; - settingEnabled.padding = false; - } - const groupsFound = new Set(); // Only detect settingEnabled from the custom CSS block (not baseCss fallback) @@ -841,19 +877,9 @@ const loadValuesFromStylesheet = (isInitialLoad = false) => { } if (anyPaddingFound) groupsFound.add('padding'); - // Update settingEnabled based on what was found in the element's CSS - if (isInitialLoad) { - for (const group of ['lineHeight', 'textAlign', 'background', 'border', 'margin', 'padding']) { - settingEnabled[group] = groupsFound.has(group); - } - } else { - // During live sync: only enable groups newly found in CSS, never override user's manual toggles - for (const group of Object.keys(settingEnabled)) { - if (groupsFound.has(group)) settingEnabled[group] = true; - } - } + // settingEnabled is NEVER modified automatically — only by user toggle clicks - // Special groups: font, fontSize, color — always enabled + // Special groups: font, fontSize, color // If not found in element CSS, load fallback values from TextSettings selectors // Only on initial open — during live sync, ElementPopup is independent from TextSettings if (isInitialLoad) { @@ -874,6 +900,14 @@ const loadValuesFromStylesheet = (isInitialLoad = false) => { } } + if (!groupsFound.has('lineHeight')) { + const data = stylesheetStore.extractValue('p', 'line-height'); + if (data && data.value !== undefined) { + lineHeight.value = data.value; + lineHeight.unit = data.unit; + } + } + if (!groupsFound.has('color')) { const c = stylesheetStore.extractValue('body', 'color'); if (c) color.value = typeof c === 'string' ? c : c.value; @@ -886,9 +920,15 @@ const loadValuesFromStylesheet = (isInitialLoad = false) => { }; const open = (element, event, count = null) => { + // Reset all toggles to unchecked for each new element + for (const group of Object.keys(settingEnabled)) { + settingEnabled[group] = false; + } + // Clear cache from any previous element settingCache.font = null; settingCache.fontSize = null; + settingCache.lineHeight = null; settingCache.color = null; isUpdatingFromStore = true; diff --git a/src/components/editor/TextSettings.vue b/src/components/editor/TextSettings.vue index 1698106..c4831cc 100644 --- a/src/components/editor/TextSettings.vue +++ b/src/components/editor/TextSettings.vue @@ -16,14 +16,6 @@ -