geoproject-app/src/components/editor/TextSettings.vue

190 lines
6.4 KiB
Vue
Raw Normal View History

<template>
2026-03-06 18:20:19 +01:00
<section class="settings__container" id="settings__container_elem" data-color-type="elem">
2026-03-05 11:42:18 +01:00
<h2>Réglage du texte par défaut</h2>
2025-12-09 17:08:40 +01:00
<div class="container">
<p class="infos">
Ces réglages s'appliquent à l'ensemble des éléments du document. Vous
pouvez modifier ensuite les éléments indépendamment.
</p>
<!-- Police -->
2026-03-06 18:20:19 +01:00
<div class="setting__section">
2025-12-09 17:08:40 +01:00
<div class="field field-font">
2026-03-05 16:29:42 +01:00
<label for="text-font" class="label-with-tooltip" data-css="font-family">Police</label>
2026-03-05 11:08:44 +01:00
<div class="field-font__options">
2026-03-05 16:29:42 +01:00
<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>
</div>
</div>
</div>
<!-- Taille du texte -->
2026-03-06 18:20:19 +01:00
<div class="setting__section">
2025-12-11 13:38:27 +01:00
<div class="field field-text-size">
<label for="text-size-range" class="label-with-tooltip" data-css="font-size">Taille du texte</label>
<InputWithUnit
v-model="fontSize"
:units="['px']"
:min="8"
:max="72"
showRange
/>
</div>
</div>
<!-- Interlignage -->
2026-03-06 18:20:19 +01:00
<div class="setting__section">
<div class="field field-text-size">
<label for="text-lineheight-range" class="label-with-tooltip" data-css="line-height">Interlignage</label>
<InputWithUnit
v-model="lineHeight"
:units="['px']"
:min="0"
:max="72"
showRange
/>
</div>
</div>
<!-- Couleurs -->
2026-03-06 18:20:19 +01:00
<div class="setting__section">
2025-12-09 17:08:40 +01:00
<div class="field field-simple">
<label for="text-color" class="label-with-tooltip" data-css="color">Couleur</label>
<div class="input-with-color">
<input
ref="colorInput"
id="text-color"
type="text"
v-model="color"
class="color-input"
data-coloris
/>
</div>
</div>
2025-12-09 17:08:40 +01:00
</div>
</div>
</section>
</template>
<script setup>
2026-03-05 19:00:29 +01:00
import { ref, watch, onMounted, nextTick } from 'vue';
import { initColoris } from '../../composables/useColoris';
import InputWithUnit from '../ui/InputWithUnit.vue';
import { useCssUpdater } from '../../composables/useCssUpdater';
import { useCssSync } from '../../composables/useCssSync';
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';
2026-03-05 11:42:18 +01:00
const { updateStyle } = useCssUpdater();
const { extractValue, extractNumericValue } = useCssSync();
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();
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' });
const color = ref('rgb(0, 0, 0)');
const colorInput = ref(null);
2026-03-05 19:00:29 +01:00
// Start true to block immediate watchers from overwriting textDefaults during setup
let isUpdatingFromStore = true;
// 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;
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);
}, { immediate: true });
watch(color, (val) => {
if (isUpdatingFromStore) return;
2026-03-05 19:00:29 +01:00
textDefaults.color = val;
updateStyle('body', 'color', val);
}, { immediate: true });
watch(fontSize, (val) => {
if (isUpdatingFromStore) return;
2026-03-05 19:00:29 +01:00
textDefaults.fontSize = { value: val.value, unit: val.unit };
debouncedUpdate(() => {
updateStyle('p', 'font-size', `${val.value}${val.unit}`);
});
2026-03-05 14:49:58 +01:00
}, { deep: true, immediate: true });
watch(lineHeight, (val) => {
if (isUpdatingFromStore) return;
2026-03-05 19:00:29 +01:00
textDefaults.lineHeight = { value: val.value, unit: val.unit };
debouncedUpdate(() => {
updateStyle('p', 'line-height', `${val.value}${val.unit}`);
});
}, { deep: true, immediate: true });
2026-03-05 19:00:29 +01:00
// Sync from store (first mount) or from textDefaults (subsequent mounts)
const syncFromStore = () => {
isUpdatingFromStore = true;
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; });
};
const updateColorisButtons = () => {
2026-03-05 11:42:18 +01:00
if (colorInput.value) {
colorInput.value.dispatchEvent(new Event('input', { bubbles: true }));
}
};
onMounted(() => {
initColoris({
format: 'auto',
swatches: ['#000000', '#FFFFFF', '#FF0000', '#00FF00', '#0000FF', 'transparent'],
});
syncFromStore();
setTimeout(updateColorisButtons, 100);
2026-03-05 16:29:42 +01:00
loadAllFontPreviews();
});
</script>