diff --git a/public/assets/css/stylesheet.print.css b/public/assets/css/stylesheet.print.css index fd8619e..4784100 100644 --- a/public/assets/css/stylesheet.print.css +++ b/public/assets/css/stylesheet.print.css @@ -31,13 +31,18 @@ figure, img{ } -h1{ +/* h1{ font-size: 38px; background: rgba(255, 255, 255, 0.521); - padding: 10px 20px; - margin: 0; + padding-top: 10px; + padding-left: 20px; + padding-bottom: 10px; + padding-right: 20px; + margin-left: 0px; + margin-right: 0px; + margin-bottom: 0px; margin-top: 60px; -} +} */ p.author{ font-size: 24px; diff --git a/src/composables/useElementSettings.js b/src/composables/useElementSettings.js index 946cd16..4c5e2d4 100644 --- a/src/composables/useElementSettings.js +++ b/src/composables/useElementSettings.js @@ -267,71 +267,96 @@ export function useElementSettings({ margin, padding, basePopup }) { const displayedCssOrder = [ { css: 'font-family', group: 'font', special: true, - getValue: () => { const val = settingEnabled.font ? fontFamily.value : textDefaults.fontFamily; return val === 'sans-serif' ? 'sans-serif' : `"${val}"`; } }, + getValue: () => { const val = fontFamily.value; return val === 'sans-serif' ? 'sans-serif' : `"${val}"`; } }, { css: 'font-style', group: 'font', getValue: () => italic.value ? 'italic' : (INLINE_DEFAULTS[currentTag.value]?.fontStyle || null), - skip: () => !italic.value && !INLINE_DEFAULTS[currentTag.value]?.fontStyle }, + skip: () => !italic.value && !INLINE_DEFAULTS[currentTag.value]?.fontStyle && !hasInCss('font-style') }, { css: 'font-weight', group: 'font', getValue: () => bold.value ? 'bold' : (INLINE_DEFAULTS[currentTag.value]?.fontWeight || null), - skip: () => !bold.value && !INLINE_DEFAULTS[currentTag.value]?.fontWeight }, + skip: () => !bold.value && !INLINE_DEFAULTS[currentTag.value]?.fontWeight && !hasInCss('font-weight') }, { css: 'font-size', group: 'fontSize', special: true, - getValue: () => `${(settingEnabled.fontSize ? fontSize : textDefaults.fontSize).value}${(settingEnabled.fontSize ? fontSize : textDefaults.fontSize).unit}` }, + getValue: () => `${fontSize.value}${fontSize.unit}` }, { css: 'line-height', group: 'lineHeight', special: true, - getValue: () => `${(settingEnabled.lineHeight ? lineHeight : textDefaults.lineHeight).value}${(settingEnabled.lineHeight ? lineHeight : textDefaults.lineHeight).unit}` }, + getValue: () => `${lineHeight.value}${lineHeight.unit}` }, { css: 'text-align', group: 'textAlign', getValue: () => textAlign.value, - skip: () => !settingEnabled.textAlign }, + skip: () => !settingEnabled.textAlign && !hasInCss('text-align') }, { css: 'color', group: 'color', special: true, - getValue: () => settingEnabled.color ? color.value : textDefaults.color }, + getValue: () => color.value }, { css: 'background', group: 'background', getValue: () => background.value, - skip: () => !settingEnabled.background }, + skip: () => !settingEnabled.background && !hasInCss('background') }, { css: 'border-width', group: 'border', getValue: () => `${borderWidth.value}${borderWidth.unit}`, - skip: () => !settingEnabled.border }, + skip: () => !settingEnabled.border && !hasInCss('border-width') }, { css: 'border-style', group: 'border', getValue: () => borderStyle.value, - skip: () => !settingEnabled.border || borderWidth.value === 0 }, + skip: () => (!settingEnabled.border && !hasInCss('border-style')) || borderWidth.value === 0 }, { css: 'border-color', group: 'border', getValue: () => borderColor.value, - skip: () => !settingEnabled.border || borderWidth.value === 0 }, + skip: () => (!settingEnabled.border && !hasInCss('border-color')) || borderWidth.value === 0 }, { css: 'text-decoration-line', group: 'textDecoration', - getValue: () => textDecorationLine.value, - skip: () => !settingEnabled.textDecoration }, + getValue: () => textDecorationLine.value || (INLINE_DEFAULTS[currentTag.value]?.textDecorationLine || null), + skip: () => !settingEnabled.textDecoration && !INLINE_DEFAULTS[currentTag.value]?.textDecorationLine && !hasInCss('text-decoration-line') }, { css: 'text-decoration-style', group: 'textDecoration', getValue: () => textDecorationStyle.value, - skip: () => !settingEnabled.textDecoration }, + skip: () => !settingEnabled.textDecoration && !hasInCss('text-decoration-style') }, { css: 'text-decoration-thickness', group: 'textDecoration', getValue: () => `${textDecorationThickness.value}${textDecorationThickness.unit}`, - skip: () => !settingEnabled.textDecoration }, + skip: () => !settingEnabled.textDecoration && !hasInCss('text-decoration-thickness') }, { css: 'text-decoration-color', group: 'textDecoration', getValue: () => textDecorationColor.value, - skip: () => !settingEnabled.textDecoration }, + skip: () => !settingEnabled.textDecoration && !hasInCss('text-decoration-color') }, { css: 'text-underline-offset', group: 'textDecoration', getValue: () => `${textUnderlineOffset.value}${textUnderlineOffset.unit}`, - skip: () => !settingEnabled.textDecoration }, + skip: () => !settingEnabled.textDecoration && !hasInCss('text-underline-offset') }, ]; const isInlineElement = computed(() => !!INLINE_DEFAULTS[currentTag.value]); + // Check if the current selector has a CSS property in the store + const hasInCss = (cssProp) => { + if (!selector.value) return false; + return !!stylesheetStore.extractValue(selector.value, cssProp); + }; + const displayedCss = computed(() => { if (!selector.value) return ''; const lines = []; for (const entry of displayedCssOrder) { if (entry.skip && entry.skip()) continue; // For inline elements, skip special groups (TextSettings defaults) when toggle is OFF - if (entry.special && isInlineElement.value && !settingEnabled[entry.group]) continue; + // Exception: keep if the current tag has an inline default for this CSS property + if (entry.special && isInlineElement.value && !settingEnabled[entry.group]) { + const tagDefaults = INLINE_DEFAULTS[currentTag.value]; + const cssToDefaultKey = { 'color': 'color', 'font-family': 'fontFamily' }; + const defaultKey = cssToDefaultKey[entry.css]; + if (!defaultKey || !tagDefaults?.[defaultKey]) continue; + } const val = entry.getValue(); if (val === null || val === undefined) continue; - const comment = (entry.special && !settingEnabled[entry.group]) ? ' /* valeur par défaut */' : ''; + // Show "valeur par défaut" only if the value actually matches the TextSettings default + let isTextDefault = false; + if (entry.special && !settingEnabled[entry.group]) { + const textDefaultValues = { + 'font-family': textDefaults.fontFamily === 'sans-serif' ? 'sans-serif' : `"${textDefaults.fontFamily}"`, + 'font-size': `${textDefaults.fontSize.value}${textDefaults.fontSize.unit}`, + 'line-height': `${textDefaults.lineHeight.value}${textDefaults.lineHeight.unit}`, + 'color': textDefaults.color, + }; + isTextDefault = val === textDefaultValues[entry.css]; + } + const comment = isTextDefault ? ' /* valeur par défaut */' : ''; lines.push(` ${entry.css}: ${val};${comment}`); } - if (settingEnabled.margin) { + const showMargin = settingEnabled.margin || ['top', 'right', 'bottom', 'left'].some(s => hasInCss(`margin-${s}`)); + if (showMargin) { for (const side of ['top', 'right', 'bottom', 'left']) { lines.push(` margin-${side}: ${margin[side].value}${margin[side].unit};`); } } - if (settingEnabled.padding) { + const showPadding = settingEnabled.padding || ['top', 'right', 'bottom', 'left'].some(s => hasInCss(`padding-${s}`)); + if (showPadding) { for (const side of ['top', 'right', 'bottom', 'left']) { lines.push(` padding-${side}: ${padding[side].value}${padding[side].unit};`); } @@ -588,25 +613,38 @@ export function useElementSettings({ margin, padding, basePopup }) { } } + // Fallback to TextSettings defaults only if the selector doesn't already have the value if (!settingEnabled.font && !settingCache.font) { - const ff = stylesheetStore.extractValue('body', 'font-family'); - if (ff) fontFamily.value = (typeof ff === 'string' ? ff : ff.value).replace(/['"]/g, ''); - const fs = stylesheetStore.extractValue('p', 'font-style'); - if (fs) italic.value = (typeof fs === 'string' ? fs : fs.value) === 'italic'; - const fw = stylesheetStore.extractValue('p', 'font-weight'); - if (fw) { const v = typeof fw === 'string' ? fw : fw.value; bold.value = v === 'bold' || parseInt(v) >= 700; } + if (!stylesheetStore.extractValue(selector.value, 'font-family')) { + const ff = stylesheetStore.extractValue('body', 'font-family'); + if (ff) fontFamily.value = (typeof ff === 'string' ? ff : ff.value).replace(/['"]/g, ''); + } + if (!stylesheetStore.extractValue(selector.value, 'font-style')) { + const fs = stylesheetStore.extractValue('p', 'font-style'); + if (fs) italic.value = (typeof fs === 'string' ? fs : fs.value) === 'italic'; + } + if (!stylesheetStore.extractValue(selector.value, 'font-weight')) { + const fw = stylesheetStore.extractValue('p', 'font-weight'); + if (fw) { const v = typeof fw === 'string' ? fw : fw.value; bold.value = v === 'bold' || parseInt(v) >= 700; } + } } if (!settingEnabled.fontSize && !settingCache.fontSize) { - const data = stylesheetStore.extractValue('p', 'font-size'); - if (data && data.value !== undefined) { fontSize.value = data.value; fontSize.unit = data.unit; } + if (!stylesheetStore.extractValue(selector.value, 'font-size')) { + const data = stylesheetStore.extractValue('p', 'font-size'); + if (data && data.value !== undefined) { fontSize.value = data.value; fontSize.unit = data.unit; } + } } if (!settingEnabled.lineHeight && !settingCache.lineHeight) { - const data = stylesheetStore.extractValue('p', 'line-height'); - if (data && data.value !== undefined) { lineHeight.value = data.value; lineHeight.unit = data.unit; } + if (!stylesheetStore.extractValue(selector.value, 'line-height')) { + const data = stylesheetStore.extractValue('p', 'line-height'); + if (data && data.value !== undefined) { lineHeight.value = data.value; lineHeight.unit = data.unit; } + } } if (!settingEnabled.color && settingCache.color === null) { - const c = stylesheetStore.extractValue('body', 'color'); - if (c) color.value = typeof c === 'string' ? c : c.value; + if (!stylesheetStore.extractValue(selector.value, 'color')) { + const c = stylesheetStore.extractValue('body', 'color'); + if (c) color.value = typeof c === 'string' ? c : c.value; + } } } catch (error) { console.error('Error loading values from stylesheet:', error); @@ -639,12 +677,19 @@ export function useElementSettings({ margin, padding, basePopup }) { settingCache.color = null; } - // Apply tag-based defaults (em/i → italic, strong/b → bold) + // Reset inline-specific values, then apply tag-based defaults + italic.value = ELEMENT_DEFAULTS.italic; + bold.value = ELEMENT_DEFAULTS.bold; + color.value = ELEMENT_DEFAULTS.color; + textDecorationLine.value = ELEMENT_DEFAULTS.textDecorationLine; + const tag = element.tagName.toLowerCase(); const inlineDefaults = INLINE_DEFAULTS[tag]; if (inlineDefaults) { if (inlineDefaults.fontStyle === 'italic') italic.value = true; if (inlineDefaults.fontWeight === 'bold') bold.value = true; + if (inlineDefaults.color) color.value = inlineDefaults.color; + if (inlineDefaults.textDecorationLine) textDecorationLine.value = inlineDefaults.textDecorationLine; } loadValuesFromStylesheet(true); diff --git a/src/stores/stylesheet.js b/src/stores/stylesheet.js index 7ef06c3..4093a29 100644 --- a/src/stores/stylesheet.js +++ b/src/stores/stylesheet.js @@ -5,7 +5,7 @@ import * as cssComments from '../utils/css-comments'; import prettier from 'prettier/standalone'; import parserPostcss from 'prettier/plugins/postcss'; import { getCsrfToken } from '../utils/kirby-auth'; -import { PAGE_DEFAULTS, TEXT_DEFAULTS, INLINE_DEFAULTS } from '../utils/defaults'; +import { PAGE_DEFAULTS, TEXT_DEFAULTS, HEADING_DEFAULTS, INLINE_DEFAULTS } from '../utils/defaults'; export const useStylesheetStore = defineStore('stylesheet', () => { // Base state @@ -195,10 +195,28 @@ export const useStylesheetStore = defineStore('stylesheet', () => { set('p', 'font-size', TEXT_DEFAULTS.fontSize.value, TEXT_DEFAULTS.fontSize.unit); set('p', 'line-height', TEXT_DEFAULTS.lineHeight.value, TEXT_DEFAULTS.lineHeight.unit); - // Inline element defaults (em, i, strong, b) + // Heading defaults (h1, h2, ...) + for (const [tag, props] of Object.entries(HEADING_DEFAULTS)) { + if (props.fontSize) set(tag, 'font-size', props.fontSize.value, props.fontSize.unit); + if (props.background) set(tag, 'background', props.background); + if (props.padding) { + for (const side of ['top', 'right', 'bottom', 'left']) { + if (props.padding[side]) set(tag, `padding-${side}`, props.padding[side].value, props.padding[side].unit); + } + } + if (props.margin) { + for (const side of ['top', 'right', 'bottom', 'left']) { + if (props.margin[side]) set(tag, `margin-${side}`, props.margin[side].value, props.margin[side].unit); + } + } + } + + // Inline element defaults (em, i, strong, b, a) for (const [tag, props] of Object.entries(INLINE_DEFAULTS)) { if (props.fontStyle) set(tag, 'font-style', props.fontStyle); if (props.fontWeight) set(tag, 'font-weight', props.fontWeight); + if (props.color) set(tag, 'color', props.color); + if (props.textDecorationLine) set(tag, 'text-decoration-line', props.textDecorationLine); } }; diff --git a/src/utils/defaults.js b/src/utils/defaults.js index 720945a..6d60021 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -43,9 +43,27 @@ export const ELEMENT_DEFAULTS = Object.freeze({ textUnderlineOffset: Object.freeze({ value: 1, unit: 'px' }), }); +export const HEADING_DEFAULTS = Object.freeze({ + h1: Object.freeze({ + fontSize: Object.freeze({ value: 38, unit: 'px' }), + lineHeight: Object.freeze({ value: 38, unit: 'px' }), + background: 'rgba(255, 255, 255, 0.521)', + padding: Object.freeze({ + top: Object.freeze({ value: 10, unit: 'px' }), + right: Object.freeze({ value: 20, unit: 'px' }), + bottom: Object.freeze({ value: 10, unit: 'px' }), + left: Object.freeze({ value: 20, unit: 'px' }), + }), + margin: Object.freeze({ + top: Object.freeze({ value: 60, unit: 'px' }), + }), + }), +}); + export const INLINE_DEFAULTS = Object.freeze({ em: Object.freeze({ fontStyle: 'italic' }), i: Object.freeze({ fontStyle: 'italic' }), strong: Object.freeze({ fontWeight: 'bold' }), b: Object.freeze({ fontWeight: 'bold' }), + a: Object.freeze({ color: '#0000EE', textDecorationLine: 'underline' }), });