add module setting text-decoration

This commit is contained in:
Julie Blanc 2026-03-08 09:54:36 +01:00
parent 8a896277bf
commit ec1b23d67a
7 changed files with 124 additions and 42 deletions

View file

@ -1,35 +0,0 @@
// .settings__container {
// margin: var(--space-m) 0;
// h2 {
// margin-bottom: var(--space);
// font-weight: 600;
// font-size: 1.4rem;
// border-bottom: 1px solid var(--color-200);
// color: var(--color-800);
// }
// .infos {
// font-size: 0.8rem;
// color: var(--color-interface-400);
// }
// }
// .setting__section:not(:last-child) {
// border-bottom: 1px solid var(--color-interface-100);
// }
// .setting__section {
// padding: var(--space-s) 0;
// h3 {
// margin-top: calc(var(--space-xs) * 1.5);
// margin-bottom: calc(var(--space-xs) * 2);
// // color: var(--color-600);
// font-size: 1rem;
// font-weight: 600;
// color: red;
// }
// }

View file

@ -54,8 +54,12 @@
} }
.setting__section[data-setting="textDecoration"],
.setting__section[data-setting="border"] { .setting__section[data-setting="border"] {
.field-border__option{ .setting__body{
padding-top: 4px;
}
.field__option{
display: grid; display: grid;
width: 100%; width: 100%;
grid-template-columns: 9ch minmax(0, 1fr); grid-template-columns: 9ch minmax(0, 1fr);

View file

@ -780,7 +780,12 @@ input[type=number] {
margin-bottom: var(--space-xs); margin-bottom: var(--space-xs);
} }
.setting__section[data-setting=border] .field-border__option { .setting__section[data-setting=textDecoration] .setting__body,
.setting__section[data-setting=border] .setting__body {
padding-top: 4px;
}
.setting__section[data-setting=textDecoration] .field__option,
.setting__section[data-setting=border] .field__option {
display: grid; display: grid;
width: 100%; width: 100%;
grid-template-columns: 9ch minmax(0, 1fr); grid-template-columns: 9ch minmax(0, 1fr);

File diff suppressed because one or more lines are too long

View file

@ -144,8 +144,8 @@
<label class="label-with-tooltip" data-css="border">Bordure</label> <label class="label-with-tooltip" data-css="border">Bordure</label>
</div> </div>
<div class="setting__body"> <div class="setting__body">
<div class="field-border__options"> <div class="field__options">
<div class="field-border__option"> <div class="field__option">
<label class="label-with-tooltip" data-css="border-width">Épaisseur</label> <label class="label-with-tooltip" data-css="border-width">Épaisseur</label>
<div class="input-with-unit"> <div class="input-with-unit">
<NumberInput <NumberInput
@ -158,7 +158,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="field-border__option"> <div class="field__option">
<label class="label-with-tooltip" data-css="border-style">Style</label> <label class="label-with-tooltip" data-css="border-style">Style</label>
<select v-model="borderStyle"> <select v-model="borderStyle">
<option value="solid">Plein</option> <option value="solid">Plein</option>
@ -167,7 +167,7 @@
<option value="double">Double</option> <option value="double">Double</option>
</select> </select>
</div> </div>
<div class="field-border__option"> <div class="field__option">
<label class="label-with-tooltip" data-css="border-color">Couleur</label> <label class="label-with-tooltip" data-css="border-color">Couleur</label>
<div class="input-with-color"> <div class="input-with-color">
<input <input
@ -182,6 +182,75 @@
</div> </div>
</div> </div>
<!-- Text Decoration -->
<div class="setting__section" data-setting="textDecoration" :class="{ 'setting-disabled': !settingEnabled.textDecoration }" @click="onSubsectionClick('textDecoration')">
<div class="setting__header">
<input type="checkbox" id="toggle-textDecoration" class="toggle-setting" :checked="settingEnabled.textDecoration" @change="onToggleSetting('textDecoration', $event.target.checked)" /><label for="toggle-textDecoration" aria-label="Activer le réglage de surlignement" @click.stop></label>
<label class="label-with-tooltip" data-css="text-decoration">Surlignement</label>
</div>
<div class="setting__body">
<div class="field__options">
<div class="field__option">
<label class="label-with-tooltip" data-css="text-decoration-line">Type</label>
<div class="radio-group">
<label><input type="radio" value="underline" v-model="textDecorationLine" /> Souligné</label>
<label><input type="radio" value="line-through" v-model="textDecorationLine" /> Barré</label>
</div>
</div>
<div class="field__option">
<label class="label-with-tooltip" data-css="text-decoration-style">Style</label>
<select v-model="textDecorationStyle">
<option value="solid">Plein</option>
<option value="double">Double</option>
<option value="dotted">Pointillés</option>
<option value="dashed">Tirets</option>
<option value="wavy">Ondulé</option>
</select>
</div>
<div class="field__option">
<label class="label-with-tooltip" data-css="text-decoration-thickness">Épaisseur</label>
<div class="input-with-unit">
<NumberInput
v-model="textDecorationThickness.value"
:min="1"
:max="100"
:step="1"
/>
<div class="unit-toggle">
<button type="button" class="active">px</button>
</div>
</div>
</div>
<div class="field__option">
<label class="label-with-tooltip" data-css="text-decoration-color">Couleur</label>
<div class="input-with-color">
<input
ref="textDecorationColorInput"
type="text"
v-model="textDecorationColor"
data-coloris
/>
</div>
</div>
<div class="field__option">
<label class="label-with-tooltip" data-css="text-underline-offset">Décalage</label>
<div class="input-with-unit">
<NumberInput
v-model="textUnderlineOffset.value"
:min="1"
:max="100"
:step="1"
/>
<div class="unit-toggle">
<button type="button" class="active">px</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Outer Margins --> <!-- Outer Margins -->
<div class="setting__section" data-setting="margin" :class="{ 'setting-disabled': !settingEnabled.margin }" @click="onSubsectionClick('margin')"> <div class="setting__section" data-setting="margin" :class="{ 'setting-disabled': !settingEnabled.margin }" @click="onSubsectionClick('margin')">
@ -314,6 +383,7 @@ const elementInstanceCount = ref(0);
const colorInput = ref(null); const colorInput = ref(null);
const backgroundInput = ref(null); const backgroundInput = ref(null);
const borderColorInput = ref(null); const borderColorInput = ref(null);
const textDecorationColorInput = ref(null);
// Spacing editor (margin/padding) // Spacing editor (margin/padding)
const { const {
@ -330,6 +400,7 @@ const {
fontFamily, italic, bold, textAlign, color, background, fontFamily, italic, bold, textAlign, color, background,
fontSize, fontSizeModel, lineHeight, lineHeightModel, fontSize, fontSizeModel, lineHeight, lineHeightModel,
borderWidth, borderStyle, borderColor, borderWidth, borderStyle, borderColor,
textDecorationLine, textDecorationStyle, textDecorationThickness, textDecorationColor, textUnderlineOffset,
settingEnabled, settingEnabled,
textDefaults, projectFonts, loadAllFontPreviews, textDefaults, projectFonts, loadAllFontPreviews,
displayedCss, editableFullCss, displayedCss, editableFullCss,

View file

@ -37,6 +37,11 @@ export function useElementSettings({ margin, padding, basePopup }) {
const borderWidth = reactive({ ...ELEMENT_DEFAULTS.borderWidth }); const borderWidth = reactive({ ...ELEMENT_DEFAULTS.borderWidth });
const borderStyle = ref(ELEMENT_DEFAULTS.borderStyle); const borderStyle = ref(ELEMENT_DEFAULTS.borderStyle);
const borderColor = ref(ELEMENT_DEFAULTS.borderColor); const borderColor = ref(ELEMENT_DEFAULTS.borderColor);
const textDecorationLine = ref(ELEMENT_DEFAULTS.textDecorationLine);
const textDecorationStyle = ref(ELEMENT_DEFAULTS.textDecorationStyle);
const textDecorationThickness = reactive({ ...ELEMENT_DEFAULTS.textDecorationThickness });
const textDecorationColor = ref(ELEMENT_DEFAULTS.textDecorationColor);
const textUnderlineOffset = reactive({ ...ELEMENT_DEFAULTS.textUnderlineOffset });
// --- Toggle state --- // --- Toggle state ---
const settingEnabled = reactive({ const settingEnabled = reactive({
@ -47,6 +52,7 @@ export function useElementSettings({ margin, padding, basePopup }) {
color: false, color: false,
background: false, background: false,
border: false, border: false,
textDecoration: false,
margin: false, margin: false,
padding: false, padding: false,
}); });
@ -93,12 +99,17 @@ export function useElementSettings({ margin, padding, basePopup }) {
{ css: 'background', group: 'background', get: () => background.value, set: v => background.value = v, debounce: true }, { css: 'background', group: 'background', get: () => background.value, set: v => background.value = v, debounce: true },
{ css: 'border-style', group: 'border', get: () => borderStyle.value, set: v => borderStyle.value = v || 'solid', debounce: false }, { css: 'border-style', group: 'border', get: () => borderStyle.value, set: v => borderStyle.value = v || 'solid', debounce: false },
{ css: 'border-color', group: 'border', get: () => borderColor.value, set: v => borderColor.value = v, debounce: true }, { css: 'border-color', group: 'border', get: () => borderColor.value, set: v => borderColor.value = v, debounce: true },
{ css: 'text-decoration-line', group: 'textDecoration', get: () => textDecorationLine.value, set: v => textDecorationLine.value = v || 'underline', debounce: false },
{ css: 'text-decoration-style', group: 'textDecoration', get: () => textDecorationStyle.value, set: v => textDecorationStyle.value = v || 'solid', debounce: false },
{ css: 'text-decoration-color', group: 'textDecoration', get: () => textDecorationColor.value, set: v => textDecorationColor.value = v, debounce: true },
]; ];
const unitProps = [ const unitProps = [
{ css: 'font-size', group: 'fontSize', ref: fontSize, debounce: true }, { css: 'font-size', group: 'fontSize', ref: fontSize, debounce: true },
{ css: 'line-height', group: 'lineHeight', ref: lineHeight, debounce: true }, { css: 'line-height', group: 'lineHeight', ref: lineHeight, debounce: true },
{ css: 'border-width', group: 'border', ref: borderWidth, debounce: true }, { css: 'border-width', group: 'border', ref: borderWidth, debounce: true },
{ css: 'text-decoration-thickness', group: 'textDecoration', ref: textDecorationThickness, debounce: true },
{ css: 'text-underline-offset', group: 'textDecoration', ref: textUnderlineOffset, debounce: true },
]; ];
const settingGroups = { const settingGroups = {
@ -109,6 +120,7 @@ export function useElementSettings({ margin, padding, basePopup }) {
color: ['color'], color: ['color'],
background: ['background'], background: ['background'],
border: ['border-width', 'border-style', 'border-color'], border: ['border-width', 'border-style', 'border-color'],
textDecoration: ['text-decoration-line', 'text-decoration-style', 'text-decoration-thickness', 'text-decoration-color', 'text-underline-offset'],
margin: ['margin-top', 'margin-right', 'margin-bottom', 'margin-left'], margin: ['margin-top', 'margin-right', 'margin-bottom', 'margin-left'],
padding: ['padding-top', 'padding-right', 'padding-bottom', 'padding-left'], padding: ['padding-top', 'padding-right', 'padding-bottom', 'padding-left'],
}; };
@ -283,6 +295,21 @@ export function useElementSettings({ margin, padding, basePopup }) {
{ css: 'border-color', group: 'border', { css: 'border-color', group: 'border',
getValue: () => borderColor.value, getValue: () => borderColor.value,
skip: () => !settingEnabled.border || borderWidth.value === 0 }, skip: () => !settingEnabled.border || borderWidth.value === 0 },
{ css: 'text-decoration-line', group: 'textDecoration',
getValue: () => textDecorationLine.value,
skip: () => !settingEnabled.textDecoration },
{ css: 'text-decoration-style', group: 'textDecoration',
getValue: () => textDecorationStyle.value,
skip: () => !settingEnabled.textDecoration },
{ css: 'text-decoration-thickness', group: 'textDecoration',
getValue: () => `${textDecorationThickness.value}${textDecorationThickness.unit}`,
skip: () => !settingEnabled.textDecoration },
{ css: 'text-decoration-color', group: 'textDecoration',
getValue: () => textDecorationColor.value,
skip: () => !settingEnabled.textDecoration },
{ css: 'text-underline-offset', group: 'textDecoration',
getValue: () => `${textUnderlineOffset.value}${textUnderlineOffset.unit}`,
skip: () => !settingEnabled.textDecoration },
]; ];
const isInlineElement = computed(() => !!INLINE_DEFAULTS[currentTag.value]); const isInlineElement = computed(() => !!INLINE_DEFAULTS[currentTag.value]);
@ -812,6 +839,11 @@ export function useElementSettings({ margin, padding, basePopup }) {
borderWidth, borderWidth,
borderStyle, borderStyle,
borderColor, borderColor,
textDecorationLine,
textDecorationStyle,
textDecorationThickness,
textDecorationColor,
textUnderlineOffset,
settingEnabled, settingEnabled,
settingCache, settingCache,
textDefaults, textDefaults,

View file

@ -36,6 +36,11 @@ export const ELEMENT_DEFAULTS = Object.freeze({
borderWidth: Object.freeze({ value: 1, unit: 'px' }), borderWidth: Object.freeze({ value: 1, unit: 'px' }),
borderStyle: 'solid', borderStyle: 'solid',
borderColor: '#000000', borderColor: '#000000',
textDecorationLine: 'underline',
textDecorationStyle: 'solid',
textDecorationThickness: Object.freeze({ value: 1, unit: 'px' }),
textDecorationColor: '#000000',
textUnderlineOffset: Object.freeze({ value: 1, unit: 'px' }),
}); });
export const INLINE_DEFAULTS = Object.freeze({ export const INLINE_DEFAULTS = Object.freeze({