197 lines
6.5 KiB
Vue
197 lines
6.5 KiB
Vue
<template>
|
|
<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>
|
|
|
|
|
|
<div class="container">
|
|
|
|
<!-- Police -->
|
|
<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>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Taille du texte -->
|
|
<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">
|
|
<InputWithUnit
|
|
v-model="fontSize"
|
|
:units="['px']"
|
|
:min="8"
|
|
:max="72"
|
|
showRange
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Interlignage -->
|
|
<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">
|
|
<InputWithUnit
|
|
v-model="lineHeight"
|
|
:units="['px']"
|
|
:min="0"
|
|
:max="72"
|
|
showRange
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 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">
|
|
<div class="input-with-color">
|
|
<input
|
|
ref="colorInput"
|
|
id="text-color"
|
|
type="text"
|
|
v-model="color"
|
|
data-coloris
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, watch, onMounted, nextTick } from 'vue';
|
|
import textIcon from '/assets/svg/text.svg?raw';
|
|
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';
|
|
import { useTextDefaults } from '../../composables/useTextDefaults';
|
|
import { useProjectFonts } from '../../composables/useProjectFonts';
|
|
|
|
const { updateStyle } = useCssUpdater();
|
|
const { extractValue, extractNumericValue } = useCssSync();
|
|
const { debouncedUpdate } = useDebounce(500);
|
|
const textDefaults = useTextDefaults();
|
|
const { fonts: projectFonts, loadFont, loadAllFontPreviews } = useProjectFonts();
|
|
|
|
// State — initial values match stylesheet.print.css (overwritten by syncFromStore)
|
|
const font = ref('sans-serif');
|
|
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);
|
|
|
|
// Start true to block immediate watchers from overwriting textDefaults during setup
|
|
let isUpdatingFromStore = true;
|
|
|
|
// Watchers for body styles
|
|
watch(font, async (val) => {
|
|
if (isUpdatingFromStore) return;
|
|
textDefaults.fontFamily = val;
|
|
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;
|
|
textDefaults.color = val;
|
|
updateStyle('body', 'color', val);
|
|
}, { immediate: true });
|
|
|
|
watch(fontSize, (val) => {
|
|
if (isUpdatingFromStore) return;
|
|
textDefaults.fontSize = { value: val.value, unit: val.unit };
|
|
debouncedUpdate(() => {
|
|
updateStyle('p', 'font-size', `${val.value}${val.unit}`);
|
|
});
|
|
}, { deep: true, immediate: true });
|
|
|
|
watch(lineHeight, (val) => {
|
|
if (isUpdatingFromStore) return;
|
|
textDefaults.lineHeight = { value: val.value, unit: val.unit };
|
|
debouncedUpdate(() => {
|
|
updateStyle('p', 'line-height', `${val.value}${val.unit}`);
|
|
});
|
|
}, { deep: true, immediate: true });
|
|
|
|
|
|
// Sync from store (first mount) or from textDefaults (subsequent mounts)
|
|
const syncFromStore = () => {
|
|
isUpdatingFromStore = true;
|
|
|
|
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
|
|
// For font-family, only accept known project fonts (ignore baseCss fonts like "DM Sans")
|
|
const fontVal = extractValue('body', 'font-family');
|
|
if (fontVal) {
|
|
const cleaned = fontVal.replace(/['"]/g, '').trim();
|
|
const isKnownFont = cleaned === 'sans-serif' || projectFonts.some(f => f.name === cleaned);
|
|
font.value = isKnownFont ? cleaned : 'sans-serif';
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Release flag after watchers triggered by ref changes have been skipped
|
|
nextTick(() => { isUpdatingFromStore = false; });
|
|
};
|
|
|
|
const updateColorisButtons = () => {
|
|
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);
|
|
loadAllFontPreviews();
|
|
});
|
|
</script>
|
|
|