From 8b99326de2311e85e3f9386a38c1c4412bf4c24c Mon Sep 17 00:00:00 2001 From: Julie Blanc Date: Thu, 5 Mar 2026 15:59:38 +0100 Subject: [PATCH] =?UTF-8?q?font-size,=20line-height,=20color=20=E2=86=92?= =?UTF-8?q?=20defaults=20values?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ElementPopup.vue | 152 ++++++++++++++++--------- src/components/editor/TextSettings.vue | 61 +++++----- src/composables/useTextDefaults.js | 3 +- 3 files changed, 127 insertions(+), 89 deletions(-) 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 @@ -
- - -
-
- - -
@@ -42,6 +34,20 @@ + +
+
+ + +
+
+
@@ -83,9 +89,8 @@ const fonts = ['Alegreya Sans', 'Arial', 'Georgia', 'Helvetica', 'Times New Roma // State const font = ref('Alegreya Sans'); -const italic = ref(false); -const bold = ref(false); const fontSize = ref({ value: 16, unit: 'px' }); +const lineHeight = ref({ value: 20, unit: 'px' }); const color = ref('rgb(0, 0, 0)'); const colorInput = ref(null); @@ -93,25 +98,16 @@ let isUpdatingFromStore = false; // Watchers for body styles watch(font, (val) => { + textDefaults.fontFamily = val; if (isUpdatingFromStore) return; updateStyle('body', 'font-family', `"${val}"`); -}); - -watch(italic, (val) => { - if (isUpdatingFromStore) return; - updateStyle('p', 'font-style', val ? 'italic' : 'normal'); -}); +}, { immediate: true }); watch(color, (val) => { + textDefaults.color = val; if (isUpdatingFromStore) return; updateStyle('body', 'color', val); -}); - -// Watchers for paragraph styles -watch(bold, (val) => { - if (isUpdatingFromStore) return; - updateStyle('p', 'font-weight', val ? 'bold' : 'normal'); -}); +}, { immediate: true }); watch(fontSize, (val) => { textDefaults.fontSize = { value: val.value, unit: val.unit }; @@ -121,25 +117,28 @@ watch(fontSize, (val) => { }); }, { deep: true, immediate: true }); +watch(lineHeight, (val) => { + textDefaults.lineHeight = { value: val.value, unit: val.unit }; + if (isUpdatingFromStore) return; + debouncedUpdate(() => { + updateStyle('p', 'line-height', `${val.value}${val.unit}`); + }); +}, { deep: true, immediate: true }); + // Sync from store const syncFromStore = () => { isUpdatingFromStore = true; - // Body styles - const fontStyle = extractValue('p', 'font-style'); - if (fontStyle) italic.value = fontStyle === 'italic'; - const colorVal = extractValue('body', 'color'); if (colorVal) color.value = colorVal; - // Paragraph styles - const fontWeight = extractValue('p', 'font-weight'); - if (fontWeight) bold.value = fontWeight === 'bold' || parseInt(fontWeight) >= 700; - const fontSizeVal = extractNumericValue('p', 'font-size', ['px']); // ['px', 'em', 'rem'] if (fontSizeVal) fontSize.value = fontSizeVal; + const lineHeightVal = extractNumericValue('p', 'line-height', ['px']); + if (lineHeightVal) lineHeight.value = lineHeightVal; + isUpdatingFromStore = false; }; diff --git a/src/composables/useTextDefaults.js b/src/composables/useTextDefaults.js index a61eb48..fda7040 100644 --- a/src/composables/useTextDefaults.js +++ b/src/composables/useTextDefaults.js @@ -3,9 +3,8 @@ import { reactive } from 'vue'; // Singleton reactive — TextSettings writes here, ElementPopup reads when disabled const defaults = reactive({ fontSize: { value: 16, unit: 'px' }, + lineHeight: { value: 20, unit: 'px' }, fontFamily: 'Alegreya Sans', - italic: false, - bold: false, color: 'rgb(0, 0, 0)', });