2025-12-03 15:20:49 +01:00
|
|
|
<template>
|
2026-03-07 19:59:01 +01:00
|
|
|
<section class="panel-settings__container" id="settings__container_elem" data-color-type="text">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="settings__header">
|
|
|
|
|
<div class="icon" v-html="textIcon"></div>
|
|
|
|
|
<h2 class="title">Réglage du texte par défaut</h2>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
2025-12-09 17:08:40 +01:00
|
|
|
<div class="container">
|
2025-12-03 15:20:49 +01:00
|
|
|
|
2025-12-05 16:30:44 +01:00
|
|
|
<!-- Police -->
|
2026-03-07 19:59:01 +01:00
|
|
|
<div class="setting__section" data-setting="font">
|
|
|
|
|
<div class="setting__header">
|
|
|
|
|
<label class="label-with-tooltip" data-css="font-family">Police</label>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="setting__body">
|
|
|
|
|
<select id="text-font" v-model="font">
|
|
|
|
|
<option value="sans-serif">Police système (sans-serif)</option>
|
|
|
|
|
<option v-for="f in projectFonts" :key="f.name" :value="f.name" :style="{ fontFamily: `'${f.name}', ${f.category}` }">{{ f.name }}</option>
|
|
|
|
|
</select>
|
2025-12-03 15:20:49 +01:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-12-05 16:30:44 +01:00
|
|
|
<!-- Taille du texte -->
|
2026-03-07 19:59:01 +01:00
|
|
|
<div class="setting__section" data-setting="fontSize">
|
|
|
|
|
<div class="setting__header">
|
|
|
|
|
<label class="label-with-tooltip" data-css="font-size">Taille du texte</label>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="setting__body">
|
2025-12-05 16:30:44 +01:00
|
|
|
<InputWithUnit
|
|
|
|
|
v-model="fontSize"
|
2026-02-24 14:16:54 +01:00
|
|
|
:units="['px']"
|
2025-12-05 16:30:44 +01:00
|
|
|
:min="8"
|
|
|
|
|
:max="72"
|
|
|
|
|
showRange
|
|
|
|
|
/>
|
2025-12-03 15:20:49 +01:00
|
|
|
</div>
|
2025-12-04 16:21:50 +01:00
|
|
|
</div>
|
2025-12-03 15:20:49 +01:00
|
|
|
|
2026-03-05 15:59:38 +01:00
|
|
|
<!-- Interlignage -->
|
2026-03-07 19:59:01 +01:00
|
|
|
<div class="setting__section" data-setting="lineHeight">
|
|
|
|
|
<div class="setting__header">
|
|
|
|
|
<label class="label-with-tooltip" data-css="line-height">Interlignage</label>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="setting__body">
|
2026-03-05 15:59:38 +01:00
|
|
|
<InputWithUnit
|
|
|
|
|
v-model="lineHeight"
|
|
|
|
|
:units="['px']"
|
|
|
|
|
:min="0"
|
|
|
|
|
:max="72"
|
|
|
|
|
showRange
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-03-07 19:59:01 +01:00
|
|
|
<!-- Couleur -->
|
|
|
|
|
<div class="setting__section" data-setting="color">
|
|
|
|
|
<div class="setting__header">
|
|
|
|
|
<label class="label-with-tooltip" data-css="color">Couleur</label>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="setting__body">
|
2025-12-04 16:21:50 +01:00
|
|
|
<div class="input-with-color">
|
2025-12-03 15:20:49 +01:00
|
|
|
<input
|
2026-02-24 14:02:33 +01:00
|
|
|
ref="colorInput"
|
2025-12-04 16:21:50 +01:00
|
|
|
id="text-color"
|
|
|
|
|
type="text"
|
2025-12-05 16:30:44 +01:00
|
|
|
v-model="color"
|
2025-12-04 16:21:50 +01:00
|
|
|
data-coloris
|
2025-12-03 15:20:49 +01:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-12-09 17:08:40 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
2025-12-03 15:20:49 +01:00
|
|
|
</section>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2026-03-05 19:00:29 +01:00
|
|
|
import { ref, watch, onMounted, nextTick } from 'vue';
|
2026-03-07 19:59:01 +01:00
|
|
|
import textIcon from '/assets/svg/text.svg?raw';
|
2026-02-26 15:35:45 +01:00
|
|
|
import { initColoris } from '../../composables/useColoris';
|
2025-12-05 16:30:44 +01:00
|
|
|
import InputWithUnit from '../ui/InputWithUnit.vue';
|
|
|
|
|
import { useCssUpdater } from '../../composables/useCssUpdater';
|
|
|
|
|
import { useCssSync } from '../../composables/useCssSync';
|
2025-12-10 11:51:53 +01:00
|
|
|
import { useDebounce } from '../../composables/useDebounce';
|
2026-03-05 14:49:58 +01:00
|
|
|
import { useTextDefaults } from '../../composables/useTextDefaults';
|
2026-03-05 16:29:42 +01:00
|
|
|
import { useProjectFonts } from '../../composables/useProjectFonts';
|
2025-12-05 16:30:44 +01:00
|
|
|
|
2026-03-05 11:42:18 +01:00
|
|
|
const { updateStyle } = useCssUpdater();
|
|
|
|
|
const { extractValue, extractNumericValue } = useCssSync();
|
2025-12-10 11:51:53 +01:00
|
|
|
const { debouncedUpdate } = useDebounce(500);
|
2026-03-05 14:49:58 +01:00
|
|
|
const textDefaults = useTextDefaults();
|
2026-03-05 16:29:42 +01:00
|
|
|
const { fonts: projectFonts, loadFont, loadAllFontPreviews } = useProjectFonts();
|
2025-12-05 16:30:44 +01:00
|
|
|
|
2026-03-05 19:00:29 +01:00
|
|
|
// State — initial values match stylesheet.print.css (overwritten by syncFromStore)
|
2026-03-05 19:06:27 +01:00
|
|
|
const font = ref('sans-serif');
|
2026-03-05 19:00:29 +01:00
|
|
|
const fontSize = ref({ value: 14, unit: 'px' });
|
|
|
|
|
const lineHeight = ref({ value: 18, unit: 'px' });
|
2025-12-05 16:30:44 +01:00
|
|
|
const color = ref('rgb(0, 0, 0)');
|
2026-02-24 14:02:33 +01:00
|
|
|
const colorInput = ref(null);
|
2025-12-03 15:20:49 +01:00
|
|
|
|
2026-03-05 19:00:29 +01:00
|
|
|
// Start true to block immediate watchers from overwriting textDefaults during setup
|
|
|
|
|
let isUpdatingFromStore = true;
|
2025-12-05 16:35:53 +01:00
|
|
|
|
2025-12-05 16:30:44 +01:00
|
|
|
// Watchers for body styles
|
2026-03-05 16:29:42 +01:00
|
|
|
watch(font, async (val) => {
|
2026-03-05 19:00:29 +01:00
|
|
|
if (isUpdatingFromStore) return;
|
2026-03-05 15:59:38 +01:00
|
|
|
textDefaults.fontFamily = val;
|
2026-03-05 16:29:42 +01:00
|
|
|
if (val !== 'sans-serif') await loadFont(val);
|
|
|
|
|
const cssValue = val === 'sans-serif' ? 'sans-serif' : `"${val}"`;
|
|
|
|
|
updateStyle('body', 'font-family', cssValue);
|
2026-03-05 15:59:38 +01:00
|
|
|
}, { immediate: true });
|
refactor: optimize EditorPanel updates with selective debouncing
Implement immediate vs debounced updates based on input type to improve
UX responsiveness while preventing excessive re-renders.
Update strategy:
- Immediate (0ms): select, buttons, checkboxes, color picker
- Debounced (1s): text inputs, number inputs, range sliders
Changes:
- PageSettings.vue: Split watchers for margin values/units and background
value/format. Extract update logic into reusable functions.
- TextSettings.vue: Add comprehensive watcher system with selective
debouncing for all settings (font, size, color, margins, etc.)
This ensures button clicks (unit toggles, format switches) apply instantly
while typed values (numbers, text) batch updates to reduce CSS re-parsing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:03:40 +01:00
|
|
|
|
2025-12-05 16:30:44 +01:00
|
|
|
watch(color, (val) => {
|
refactor: optimize EditorPanel updates with selective debouncing
Implement immediate vs debounced updates based on input type to improve
UX responsiveness while preventing excessive re-renders.
Update strategy:
- Immediate (0ms): select, buttons, checkboxes, color picker
- Debounced (1s): text inputs, number inputs, range sliders
Changes:
- PageSettings.vue: Split watchers for margin values/units and background
value/format. Extract update logic into reusable functions.
- TextSettings.vue: Add comprehensive watcher system with selective
debouncing for all settings (font, size, color, margins, etc.)
This ensures button clicks (unit toggles, format switches) apply instantly
while typed values (numbers, text) batch updates to reduce CSS re-parsing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:03:40 +01:00
|
|
|
if (isUpdatingFromStore) return;
|
2026-03-05 19:00:29 +01:00
|
|
|
textDefaults.color = val;
|
2025-12-05 16:30:44 +01:00
|
|
|
updateStyle('body', 'color', val);
|
2026-03-05 15:59:38 +01:00
|
|
|
}, { immediate: true });
|
2025-12-05 16:18:31 +01:00
|
|
|
|
2025-12-05 16:30:44 +01:00
|
|
|
watch(fontSize, (val) => {
|
|
|
|
|
if (isUpdatingFromStore) return;
|
2026-03-05 19:00:29 +01:00
|
|
|
textDefaults.fontSize = { value: val.value, unit: val.unit };
|
2025-12-10 11:51:53 +01:00
|
|
|
debouncedUpdate(() => {
|
|
|
|
|
updateStyle('p', 'font-size', `${val.value}${val.unit}`);
|
|
|
|
|
});
|
2026-03-05 14:49:58 +01:00
|
|
|
}, { deep: true, immediate: true });
|
2025-12-05 16:18:31 +01:00
|
|
|
|
2026-03-05 15:59:38 +01:00
|
|
|
watch(lineHeight, (val) => {
|
|
|
|
|
if (isUpdatingFromStore) return;
|
2026-03-05 19:00:29 +01:00
|
|
|
textDefaults.lineHeight = { value: val.value, unit: val.unit };
|
2026-03-05 15:59:38 +01:00
|
|
|
debouncedUpdate(() => {
|
|
|
|
|
updateStyle('p', 'line-height', `${val.value}${val.unit}`);
|
|
|
|
|
});
|
|
|
|
|
}, { deep: true, immediate: true });
|
|
|
|
|
|
refactor: optimize EditorPanel updates with selective debouncing
Implement immediate vs debounced updates based on input type to improve
UX responsiveness while preventing excessive re-renders.
Update strategy:
- Immediate (0ms): select, buttons, checkboxes, color picker
- Debounced (1s): text inputs, number inputs, range sliders
Changes:
- PageSettings.vue: Split watchers for margin values/units and background
value/format. Extract update logic into reusable functions.
- TextSettings.vue: Add comprehensive watcher system with selective
debouncing for all settings (font, size, color, margins, etc.)
This ensures button clicks (unit toggles, format switches) apply instantly
while typed values (numbers, text) batch updates to reduce CSS re-parsing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:03:40 +01:00
|
|
|
|
2026-03-05 19:00:29 +01:00
|
|
|
// Sync from store (first mount) or from textDefaults (subsequent mounts)
|
2025-12-05 16:30:44 +01:00
|
|
|
const syncFromStore = () => {
|
|
|
|
|
isUpdatingFromStore = true;
|
refactor: optimize EditorPanel updates with selective debouncing
Implement immediate vs debounced updates based on input type to improve
UX responsiveness while preventing excessive re-renders.
Update strategy:
- Immediate (0ms): select, buttons, checkboxes, color picker
- Debounced (1s): text inputs, number inputs, range sliders
Changes:
- PageSettings.vue: Split watchers for margin values/units and background
value/format. Extract update logic into reusable functions.
- TextSettings.vue: Add comprehensive watcher system with selective
debouncing for all settings (font, size, color, margins, etc.)
This ensures button clicks (unit toggles, format switches) apply instantly
while typed values (numbers, text) batch updates to reduce CSS re-parsing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:03:40 +01:00
|
|
|
|
2026-03-05 19:00:29 +01:00
|
|
|
if (textDefaults._initialized) {
|
|
|
|
|
// Already initialized — restore from textDefaults (not from CSS which may contain popup values)
|
|
|
|
|
font.value = textDefaults.fontFamily;
|
|
|
|
|
fontSize.value = { value: textDefaults.fontSize.value, unit: textDefaults.fontSize.unit };
|
|
|
|
|
lineHeight.value = { value: textDefaults.lineHeight.value, unit: textDefaults.lineHeight.unit };
|
|
|
|
|
color.value = textDefaults.color;
|
|
|
|
|
} else {
|
|
|
|
|
// First mount — read from CSS store
|
2026-03-05 19:06:27 +01:00
|
|
|
// For font-family, only accept known project fonts (ignore baseCss fonts like "DM Sans")
|
2026-03-05 19:00:29 +01:00
|
|
|
const fontVal = extractValue('body', 'font-family');
|
|
|
|
|
if (fontVal) {
|
|
|
|
|
const cleaned = fontVal.replace(/['"]/g, '').trim();
|
2026-03-05 19:06:27 +01:00
|
|
|
const isKnownFont = cleaned === 'sans-serif' || projectFonts.some(f => f.name === cleaned);
|
|
|
|
|
font.value = isKnownFont ? cleaned : 'sans-serif';
|
2026-03-05 19:00:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const colorVal = extractValue('body', 'color');
|
|
|
|
|
if (colorVal) color.value = colorVal;
|
|
|
|
|
|
|
|
|
|
const fontSizeVal = extractNumericValue('p', 'font-size', ['px']);
|
|
|
|
|
if (fontSizeVal) fontSize.value = fontSizeVal;
|
|
|
|
|
|
|
|
|
|
const lineHeightVal = extractNumericValue('p', 'line-height', ['px']);
|
|
|
|
|
if (lineHeightVal) lineHeight.value = lineHeightVal;
|
|
|
|
|
|
|
|
|
|
// Persist to textDefaults from CSS-synced values
|
|
|
|
|
textDefaults.fontFamily = font.value;
|
|
|
|
|
textDefaults.fontSize = { value: fontSize.value.value, unit: fontSize.value.unit };
|
|
|
|
|
textDefaults.lineHeight = { value: lineHeight.value.value, unit: lineHeight.value.unit };
|
|
|
|
|
textDefaults.color = color.value;
|
|
|
|
|
textDefaults._initialized = true;
|
2026-03-05 16:29:42 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-05 19:00:29 +01:00
|
|
|
// Release flag after watchers triggered by ref changes have been skipped
|
|
|
|
|
nextTick(() => { isUpdatingFromStore = false; });
|
2025-12-05 16:30:44 +01:00
|
|
|
};
|
2025-12-05 16:18:31 +01:00
|
|
|
|
2026-02-24 14:02:33 +01:00
|
|
|
const updateColorisButtons = () => {
|
2026-03-05 11:42:18 +01:00
|
|
|
if (colorInput.value) {
|
|
|
|
|
colorInput.value.dispatchEvent(new Event('input', { bubbles: true }));
|
|
|
|
|
}
|
2026-02-24 14:02:33 +01:00
|
|
|
};
|
|
|
|
|
|
2025-12-04 16:21:50 +01:00
|
|
|
onMounted(() => {
|
2026-02-26 15:35:45 +01:00
|
|
|
initColoris({
|
2025-12-04 16:21:50 +01:00
|
|
|
format: 'auto',
|
2026-02-26 15:35:45 +01:00
|
|
|
swatches: ['#000000', '#FFFFFF', '#FF0000', '#00FF00', '#0000FF', 'transparent'],
|
2025-12-04 16:21:50 +01:00
|
|
|
});
|
2025-12-05 16:18:31 +01:00
|
|
|
syncFromStore();
|
2026-02-24 14:02:33 +01:00
|
|
|
setTimeout(updateColorisButtons, 100);
|
2026-03-05 16:29:42 +01:00
|
|
|
loadAllFontPreviews();
|
2025-12-04 16:21:50 +01:00
|
|
|
});
|
2025-12-03 15:20:49 +01:00
|
|
|
</script>
|
2025-12-10 13:47:49 +01:00
|
|
|
|