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

749 lines
19 KiB
Vue
Raw Normal View History

<template>
<section class="settings-section">
<h2>Réglage du texte</h2>
<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>
<div class="settings-subsection">
<div class="field">
<label for="text-font">Police</label>
<div class="field-with-option">
<select id="text-font" v-model="font">
<option value="Alegreya Sans">Alegreya Sans</option>
<option value="Arial">Arial</option>
<option value="Georgia">Georgia</option>
<option value="Helvetica">Helvetica</option>
<option value="Times New Roman">Times New Roman</option>
</select>
<div class="field-checkbox">
<input id="text-italic" type="checkbox" v-model="italic" />
<label for="text-italic">Italique</label>
</div>
</div>
</div>
</div>
<div class="settings-subsection">
<div class="field">
<label>Graisse</label>
<div class="unit-toggle">
<button
type="button"
:class="{ active: weight === '200' }"
@click="weight = '200'"
>
200
</button>
<button
type="button"
:class="{ active: weight === '300' }"
@click="weight = '300'"
>
300
</button>
<button
type="button"
:class="{ active: weight === '400' }"
@click="weight = '400'"
>
400
</button>
<button
type="button"
:class="{ active: weight === '600' }"
@click="weight = '600'"
>
600
</button>
<button
type="button"
:class="{ active: weight === '800' }"
@click="weight = '800'"
>
800
</button>
<button
type="button"
:class="{ active: weight === 'normal' }"
@click="weight = 'normal'"
>
normal
</button>
<button
type="button"
:class="{ active: weight === 'bold' }"
@click="weight = 'bold'"
>
bold
</button>
</div>
</div>
</div>
<div class="settings-subsection">
<div class="field">
<label for="text-size-range">Taille du texte</label>
<div class="input-with-unit">
<input
id="text-size-range"
type="range"
v-model.number="fontSize.value"
min="8"
max="72"
step="1"
/>
<input
type="number"
v-model.number="fontSize.value"
min="8"
max="72"
step="1"
class="size-input"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: fontSize.unit === 'px' }"
@click="fontSize.unit = 'px'"
>
px
</button>
<button
type="button"
:class="{ active: fontSize.unit === 'em' }"
@click="fontSize.unit = 'em'"
>
em
</button>
<button
type="button"
:class="{ active: fontSize.unit === 'rem' }"
@click="fontSize.unit = 'rem'"
>
rem
</button>
</div>
</div>
</div>
</div>
<div class="settings-subsection">
<div class="field">
<label for="text-alignment">Alignement</label>
<select id="text-alignment" v-model="alignment">
<option value="left">Gauche</option>
<option value="center">Centre</option>
<option value="right">Droite</option>
<option value="justify">Justifié</option>
</select>
</div>
</div>
<div class="settings-subsection">
<div class="field">
<label for="text-color">Couleur</label>
<div class="input-with-color">
<input
id="text-color"
type="text"
v-model="color.value"
class="color-input"
data-coloris
/>
</div>
</div>
<div class="field">
<label for="text-background">Arrière-plan</label>
<div class="input-with-color">
<input
id="text-background"
type="text"
v-model="background.value"
class="color-input"
data-coloris
/>
</div>
</div>
</div>
<div class="settings-subsection">
<div class="field">
<label for="margin-outer">Marges extérieures</label>
<div class="input-with-unit">
<input
id="margin-outer"
type="number"
v-model.number="marginOuter.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginOuter.unit === 'mm' }"
@click="marginOuter.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginOuter.unit === 'px' }"
@click="marginOuter.unit = 'px'"
>
px
</button>
</div>
<button
type="button"
class="collapse-toggle"
:class="{ expanded: marginOuterExpanded }"
@click="marginOuterExpanded = !marginOuterExpanded"
title="Réglages détaillés"
>
</button>
</div>
</div>
<div v-if="marginOuterExpanded" class="subsection collapsed-section">
<div class="field">
<label for="margin-outer-top">Haut</label>
<div class="input-with-unit">
<input
id="margin-outer-top"
type="number"
v-model.number="marginOuterDetailed.top.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginOuterDetailed.top.unit === 'mm' }"
@click="marginOuterDetailed.top.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginOuterDetailed.top.unit === 'px' }"
@click="marginOuterDetailed.top.unit = 'px'"
>
px
</button>
</div>
</div>
</div>
<div class="field">
<label for="margin-outer-bottom">Bas</label>
<div class="input-with-unit">
<input
id="margin-outer-bottom"
type="number"
v-model.number="marginOuterDetailed.bottom.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginOuterDetailed.bottom.unit === 'mm' }"
@click="marginOuterDetailed.bottom.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginOuterDetailed.bottom.unit === 'px' }"
@click="marginOuterDetailed.bottom.unit = 'px'"
>
px
</button>
</div>
</div>
</div>
<div class="field">
<label for="margin-outer-left">Gauche</label>
<div class="input-with-unit">
<input
id="margin-outer-left"
type="number"
v-model.number="marginOuterDetailed.left.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginOuterDetailed.left.unit === 'mm' }"
@click="marginOuterDetailed.left.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginOuterDetailed.left.unit === 'px' }"
@click="marginOuterDetailed.left.unit = 'px'"
>
px
</button>
</div>
</div>
</div>
<div class="field">
<label for="margin-outer-right">Droite</label>
<div class="input-with-unit">
<input
id="margin-outer-right"
type="number"
v-model.number="marginOuterDetailed.right.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginOuterDetailed.right.unit === 'mm' }"
@click="marginOuterDetailed.right.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginOuterDetailed.right.unit === 'px' }"
@click="marginOuterDetailed.right.unit = 'px'"
>
px
</button>
</div>
</div>
</div>
</div>
</div>
<div class="settings-subsection">
<div class="field">
<label for="margin-inner">Marges intérieures</label>
<div class="input-with-unit">
<input
id="margin-inner"
type="number"
v-model.number="marginInner.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginInner.unit === 'mm' }"
@click="marginInner.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginInner.unit === 'px' }"
@click="marginInner.unit = 'px'"
>
px
</button>
</div>
<button
type="button"
class="collapse-toggle"
:class="{ expanded: marginInnerExpanded }"
@click="marginInnerExpanded = !marginInnerExpanded"
title="Réglages détaillés"
>
</button>
</div>
</div>
<div v-if="marginInnerExpanded" class="subsection collapsed-section">
<div class="field">
<label for="margin-inner-top">Haut</label>
<div class="input-with-unit">
<input
id="margin-inner-top"
type="number"
v-model.number="marginInnerDetailed.top.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginInnerDetailed.top.unit === 'mm' }"
@click="marginInnerDetailed.top.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginInnerDetailed.top.unit === 'px' }"
@click="marginInnerDetailed.top.unit = 'px'"
>
px
</button>
</div>
</div>
</div>
<div class="field">
<label for="margin-inner-bottom">Bas</label>
<div class="input-with-unit">
<input
id="margin-inner-bottom"
type="number"
v-model.number="marginInnerDetailed.bottom.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginInnerDetailed.bottom.unit === 'mm' }"
@click="marginInnerDetailed.bottom.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginInnerDetailed.bottom.unit === 'px' }"
@click="marginInnerDetailed.bottom.unit = 'px'"
>
px
</button>
</div>
</div>
</div>
<div class="field">
<label for="margin-inner-left">Gauche</label>
<div class="input-with-unit">
<input
id="margin-inner-left"
type="number"
v-model.number="marginInnerDetailed.left.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginInnerDetailed.left.unit === 'mm' }"
@click="marginInnerDetailed.left.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginInnerDetailed.left.unit === 'px' }"
@click="marginInnerDetailed.left.unit = 'px'"
>
px
</button>
</div>
</div>
</div>
<div class="field">
<label for="margin-inner-right">Droite</label>
<div class="input-with-unit">
<input
id="margin-inner-right"
type="number"
v-model.number="marginInnerDetailed.right.value"
min="0"
/>
<div class="unit-toggle">
<button
type="button"
:class="{ active: marginInnerDetailed.right.unit === 'mm' }"
@click="marginInnerDetailed.right.unit = 'mm'"
>
mm
</button>
<button
type="button"
:class="{ active: marginInnerDetailed.right.unit === 'px' }"
@click="marginInnerDetailed.right.unit = 'px'"
>
px
</button>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import { useStylesheetStore } from '../../stores/stylesheet';
import Coloris from '@melloware/coloris';
const stylesheetStore = useStylesheetStore();
let isUpdatingFromStore = false;
let updateTimer = null;
const debouncedUpdate = (callback) => {
clearTimeout(updateTimer);
updateTimer = setTimeout(callback, 1000);
};
const immediateUpdate = (callback) => {
callback();
};
// Font
const font = ref('Alegreya Sans');
const italic = ref(false);
// Weight
const weight = ref('400');
// Font size
const fontSize = ref({
value: 23,
unit: 'px',
});
// Alignment
const alignment = ref('left');
// Color
const color = ref({
value: 'rgb(250, 250, 250)',
});
// Background
const background = ref({
value: 'transparent',
});
// Margin outer
const marginOuter = ref({
value: 23,
unit: 'mm',
});
const marginOuterExpanded = ref(false);
const marginOuterDetailed = ref({
top: { value: 23, unit: 'mm' },
bottom: { value: 23, unit: 'mm' },
left: { value: 23, unit: 'mm' },
right: { value: 23, unit: 'mm' },
});
// Margin inner
const marginInner = ref({
value: 23,
unit: 'mm',
});
const marginInnerExpanded = ref(false);
const marginInnerDetailed = ref({
top: { value: 23, unit: 'mm' },
bottom: { value: 23, unit: 'mm' },
left: { value: 23, unit: 'mm' },
right: { value: 23, unit: 'mm' },
});
// Watchers - Immediate updates for select/buttons/checkboxes
watch(font, () => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement font update
});
});
watch(italic, () => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement italic update
});
});
watch(weight, () => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement weight update
});
});
watch(alignment, () => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement alignment update
});
});
// Font size - debounced for value, immediate for unit
watch(
() => fontSize.value.value,
() => {
if (isUpdatingFromStore) return;
debouncedUpdate(() => {
// TODO: implement font size update
});
}
);
watch(
() => fontSize.value.unit,
() => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement font size update
});
}
);
// Color - debounced for text value
watch(
() => color.value.value,
() => {
if (isUpdatingFromStore) return;
debouncedUpdate(() => {
// TODO: implement color update
});
}
);
// Background - debounced for value
watch(
() => background.value.value,
() => {
if (isUpdatingFromStore) return;
debouncedUpdate(() => {
// TODO: implement background update
});
}
);
// Margin outer - debounced for value, immediate for unit
watch(
() => marginOuter.value.value,
() => {
if (isUpdatingFromStore) return;
debouncedUpdate(() => {
// TODO: implement margin outer update
});
}
);
watch(
() => marginOuter.value.unit,
() => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement margin outer update
});
}
);
// Margin outer detailed - debounced for values, immediate for units
watch(
() => [
marginOuterDetailed.value.top.value,
marginOuterDetailed.value.bottom.value,
marginOuterDetailed.value.left.value,
marginOuterDetailed.value.right.value,
],
() => {
if (isUpdatingFromStore) return;
debouncedUpdate(() => {
// TODO: implement margin outer detailed update
});
}
);
watch(
() => [
marginOuterDetailed.value.top.unit,
marginOuterDetailed.value.bottom.unit,
marginOuterDetailed.value.left.unit,
marginOuterDetailed.value.right.unit,
],
() => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement margin outer detailed update
});
}
);
// Margin inner - debounced for value, immediate for unit
watch(
() => marginInner.value.value,
() => {
if (isUpdatingFromStore) return;
debouncedUpdate(() => {
// TODO: implement margin inner update
});
}
);
watch(
() => marginInner.value.unit,
() => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement margin inner update
});
}
);
// Margin inner detailed - debounced for values, immediate for units
watch(
() => [
marginInnerDetailed.value.top.value,
marginInnerDetailed.value.bottom.value,
marginInnerDetailed.value.left.value,
marginInnerDetailed.value.right.value,
],
() => {
if (isUpdatingFromStore) return;
debouncedUpdate(() => {
// TODO: implement margin inner detailed update
});
}
);
watch(
() => [
marginInnerDetailed.value.top.unit,
marginInnerDetailed.value.bottom.unit,
marginInnerDetailed.value.left.unit,
marginInnerDetailed.value.right.unit,
],
() => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement margin inner detailed update
});
}
);
onMounted(() => {
Coloris.init();
Coloris({
themeMode: 'dark',
alpha: true,
format: 'auto',
formatToggle: true,
swatches: [
'#000000',
'#FFFFFF',
'#FF0000',
'#00FF00',
'#0000FF',
'transparent',
],
});
});
</script>