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

1128 lines
36 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;
let isSyncingFromSimple = false; // Flag to prevent detailed watchers from triggering when syncing from simple field
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: 0,
unit: 'mm',
});
const marginOuterExpanded = ref(false);
const marginOuterDetailed = ref({
top: { value: 0, unit: 'mm' },
bottom: { value: 24, unit: 'mm' },
left: { value: 0, unit: 'mm' },
right: { value: 0, unit: 'mm' },
});
// Margin inner
const marginInner = ref({
value: 0,
unit: 'mm',
});
const marginInnerExpanded = ref(false);
const marginInnerDetailed = ref({
top: { value: 0, unit: 'mm' },
bottom: { value: 0, unit: 'mm' },
left: { value: 0, unit: 'mm' },
right: { value: 0, unit: 'mm' },
});
// Update functions
const updateBodyStyle = (property, value) => {
const currentBlock = stylesheetStore.extractBlock('body') || createBodyRule();
if (currentBlock.includes(`${property}:`)) {
// Update existing property
const updatedBlock = currentBlock.replace(
new RegExp(`(${property}:\\s*)[^;]+`, 'i'),
`$1${value}`
);
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
} else {
// Add new property
const updatedBlock = currentBlock.replace(
/(\s*})$/,
` ${property}: ${value};\n$1`
);
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
}
};
const updateParagraphStyle = (property, value) => {
const currentBlock = stylesheetStore.extractBlock('p') || createParagraphRule();
if (currentBlock.includes(`${property}:`)) {
// Update existing property
const updatedBlock = currentBlock.replace(
new RegExp(`(${property}:\\s*)[^;]+`, 'i'),
`$1${value}`
);
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
} else {
// Add new property
const updatedBlock = currentBlock.replace(
/(\s*})$/,
` ${property}: ${value};\n$1`
);
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
}
};
// Remove detailed margin properties (margin-top, margin-right, etc.)
const removeDetailedMargins = () => {
let currentBlock = stylesheetStore.extractBlock('p');
if (!currentBlock) return;
let updatedBlock = currentBlock;
updatedBlock = updatedBlock.replace(/\s*margin-top:\s*[^;]+;\n?/gi, '');
updatedBlock = updatedBlock.replace(/\s*margin-right:\s*[^;]+;\n?/gi, '');
updatedBlock = updatedBlock.replace(/\s*margin-bottom:\s*[^;]+;\n?/gi, '');
updatedBlock = updatedBlock.replace(/\s*margin-left:\s*[^;]+;\n?/gi, '');
if (updatedBlock !== currentBlock) {
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
}
};
// Remove detailed padding properties (padding-top, padding-right, etc.)
const removeDetailedPadding = () => {
let currentBlock = stylesheetStore.extractBlock('p');
if (!currentBlock) return;
let updatedBlock = currentBlock;
updatedBlock = updatedBlock.replace(/\s*padding-top:\s*[^;]+;\n?/gi, '');
updatedBlock = updatedBlock.replace(/\s*padding-right:\s*[^;]+;\n?/gi, '');
updatedBlock = updatedBlock.replace(/\s*padding-bottom:\s*[^;]+;\n?/gi, '');
updatedBlock = updatedBlock.replace(/\s*padding-left:\s*[^;]+;\n?/gi, '');
if (updatedBlock !== currentBlock) {
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
}
};
// Remove shorthand margin property
const removeShorthandMargin = () => {
let currentBlock = stylesheetStore.extractBlock('p');
if (!currentBlock) return;
let updatedBlock = currentBlock.replace(/\s*margin:\s*[^;]+;\n?/gi, '');
if (updatedBlock !== currentBlock) {
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
}
};
// Remove shorthand padding property
const removeShorthandPadding = () => {
let currentBlock = stylesheetStore.extractBlock('p');
if (!currentBlock) return;
let updatedBlock = currentBlock.replace(/\s*padding:\s*[^;]+;\n?/gi, '');
if (updatedBlock !== currentBlock) {
stylesheetStore.content = stylesheetStore.content.replace(
currentBlock,
updatedBlock
);
}
};
const createBodyRule = () => {
stylesheetStore.content += '\n\nbody {\n}\n';
return 'body {\n}';
};
const createParagraphRule = () => {
stylesheetStore.content += '\n\np {\n}\n';
return 'p {\n}';
};
// Watchers - Immediate updates for select/buttons/checkboxes
watch(font, () => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
// TODO: implement font update when fonts are available
});
});
watch(italic, (newValue) => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
updateBodyStyle('font-style', newValue ? 'italic' : 'normal');
});
});
watch(weight, (newValue) => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
updateParagraphStyle('font-weight', newValue);
});
});
watch(alignment, (newValue) => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
updateBodyStyle('text-align', newValue);
});
});
// Font size - immediate for both value and unit (applied to paragraphs)
watch(
() => fontSize.value.value,
() => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
updateParagraphStyle('font-size', `${fontSize.value.value}${fontSize.value.unit}`);
});
}
);
watch(
() => fontSize.value.unit,
() => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
updateParagraphStyle('font-size', `${fontSize.value.value}${fontSize.value.unit}`);
});
}
);
// Color - immediate update for Coloris
watch(
() => color.value.value,
() => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
updateBodyStyle('color', color.value.value);
});
}
);
// Background - immediate update for Coloris
watch(
() => background.value.value,
() => {
if (isUpdatingFromStore) return;
immediateUpdate(() => {
updateBodyStyle('background', background.value.value);
});
}
);
// Margin outer - debounced for value, immediate for unit (applied to paragraphs)
watch(
() => marginOuter.value.value,
() => {
if (isUpdatingFromStore) return;
// Update all detailed values to match the simple value
isSyncingFromSimple = true;
marginOuterDetailed.value.top.value = marginOuter.value.value;
marginOuterDetailed.value.right.value = marginOuter.value.value;
marginOuterDetailed.value.bottom.value = marginOuter.value.value;
marginOuterDetailed.value.left.value = marginOuter.value.value;
isSyncingFromSimple = false;
debouncedUpdate(() => {
removeDetailedMargins(); // Remove detailed properties first
updateParagraphStyle('margin', `${marginOuter.value.value}${marginOuter.value.unit}`);
});
}
);
watch(
() => marginOuter.value.unit,
() => {
if (isUpdatingFromStore) return;
// Update all detailed units to match the simple unit
isSyncingFromSimple = true;
marginOuterDetailed.value.top.unit = marginOuter.value.unit;
marginOuterDetailed.value.right.unit = marginOuter.value.unit;
marginOuterDetailed.value.bottom.unit = marginOuter.value.unit;
marginOuterDetailed.value.left.unit = marginOuter.value.unit;
isSyncingFromSimple = false;
immediateUpdate(() => {
removeDetailedMargins(); // Remove detailed properties first
updateParagraphStyle('margin', `${marginOuter.value.value}${marginOuter.value.unit}`);
});
}
);
// Margin outer detailed - debounced for values, immediate for units (applied to paragraphs)
watch(
() => [
marginOuterDetailed.value.top.value,
marginOuterDetailed.value.bottom.value,
marginOuterDetailed.value.left.value,
marginOuterDetailed.value.right.value,
],
() => {
if (isUpdatingFromStore || !marginOuterExpanded.value || isSyncingFromSimple) return;
debouncedUpdate(() => {
removeShorthandMargin(); // Remove shorthand property first
updateParagraphStyle('margin-top', `${marginOuterDetailed.value.top.value}${marginOuterDetailed.value.top.unit}`);
updateParagraphStyle('margin-bottom', `${marginOuterDetailed.value.bottom.value}${marginOuterDetailed.value.bottom.unit}`);
updateParagraphStyle('margin-left', `${marginOuterDetailed.value.left.value}${marginOuterDetailed.value.left.unit}`);
updateParagraphStyle('margin-right', `${marginOuterDetailed.value.right.value}${marginOuterDetailed.value.right.unit}`);
});
}
);
watch(
() => [
marginOuterDetailed.value.top.unit,
marginOuterDetailed.value.bottom.unit,
marginOuterDetailed.value.left.unit,
marginOuterDetailed.value.right.unit,
],
() => {
if (isUpdatingFromStore || !marginOuterExpanded.value || isSyncingFromSimple) return;
immediateUpdate(() => {
removeShorthandMargin(); // Remove shorthand property first
updateParagraphStyle('margin-top', `${marginOuterDetailed.value.top.value}${marginOuterDetailed.value.top.unit}`);
updateParagraphStyle('margin-bottom', `${marginOuterDetailed.value.bottom.value}${marginOuterDetailed.value.bottom.unit}`);
updateParagraphStyle('margin-left', `${marginOuterDetailed.value.left.value}${marginOuterDetailed.value.left.unit}`);
updateParagraphStyle('margin-right', `${marginOuterDetailed.value.right.value}${marginOuterDetailed.value.right.unit}`);
});
}
);
// Margin inner - debounced for value, immediate for unit (applied to paragraphs)
watch(
() => marginInner.value.value,
() => {
if (isUpdatingFromStore) return;
// Update all detailed values to match the simple value
isSyncingFromSimple = true;
marginInnerDetailed.value.top.value = marginInner.value.value;
marginInnerDetailed.value.right.value = marginInner.value.value;
marginInnerDetailed.value.bottom.value = marginInner.value.value;
marginInnerDetailed.value.left.value = marginInner.value.value;
isSyncingFromSimple = false;
debouncedUpdate(() => {
removeDetailedPadding(); // Remove detailed properties first
updateParagraphStyle('padding', `${marginInner.value.value}${marginInner.value.unit}`);
});
}
);
watch(
() => marginInner.value.unit,
() => {
if (isUpdatingFromStore) return;
// Update all detailed units to match the simple unit
isSyncingFromSimple = true;
marginInnerDetailed.value.top.unit = marginInner.value.unit;
marginInnerDetailed.value.right.unit = marginInner.value.unit;
marginInnerDetailed.value.bottom.unit = marginInner.value.unit;
marginInnerDetailed.value.left.unit = marginInner.value.unit;
isSyncingFromSimple = false;
immediateUpdate(() => {
removeDetailedPadding(); // Remove detailed properties first
updateParagraphStyle('padding', `${marginInner.value.value}${marginInner.value.unit}`);
});
}
);
// Margin inner detailed - debounced for values, immediate for units (applied to paragraphs)
watch(
() => [
marginInnerDetailed.value.top.value,
marginInnerDetailed.value.bottom.value,
marginInnerDetailed.value.left.value,
marginInnerDetailed.value.right.value,
],
() => {
if (isUpdatingFromStore || !marginInnerExpanded.value || isSyncingFromSimple) return;
debouncedUpdate(() => {
removeShorthandPadding(); // Remove shorthand property first
updateParagraphStyle('padding-top', `${marginInnerDetailed.value.top.value}${marginInnerDetailed.value.top.unit}`);
updateParagraphStyle('padding-bottom', `${marginInnerDetailed.value.bottom.value}${marginInnerDetailed.value.bottom.unit}`);
updateParagraphStyle('padding-left', `${marginInnerDetailed.value.left.value}${marginInnerDetailed.value.left.unit}`);
updateParagraphStyle('padding-right', `${marginInnerDetailed.value.right.value}${marginInnerDetailed.value.right.unit}`);
});
}
);
watch(
() => [
marginInnerDetailed.value.top.unit,
marginInnerDetailed.value.bottom.unit,
marginInnerDetailed.value.left.unit,
marginInnerDetailed.value.right.unit,
],
() => {
if (isUpdatingFromStore || !marginInnerExpanded.value || isSyncingFromSimple) return;
immediateUpdate(() => {
removeShorthandPadding(); // Remove shorthand property first
updateParagraphStyle('padding-top', `${marginInnerDetailed.value.top.value}${marginInnerDetailed.value.top.unit}`);
updateParagraphStyle('padding-bottom', `${marginInnerDetailed.value.bottom.value}${marginInnerDetailed.value.bottom.unit}`);
updateParagraphStyle('padding-left', `${marginInnerDetailed.value.left.value}${marginInnerDetailed.value.left.unit}`);
updateParagraphStyle('padding-right', `${marginInnerDetailed.value.right.value}${marginInnerDetailed.value.right.unit}`);
});
}
);
// Toggle expanded state - sync values when expanding
watch(marginOuterExpanded, (newValue) => {
if (newValue) {
// When expanding, copy simple value to all detailed fields
marginOuterDetailed.value.top.value = marginOuter.value.value;
marginOuterDetailed.value.top.unit = marginOuter.value.unit;
marginOuterDetailed.value.bottom.value = marginOuter.value.value;
marginOuterDetailed.value.bottom.unit = marginOuter.value.unit;
marginOuterDetailed.value.left.value = marginOuter.value.value;
marginOuterDetailed.value.left.unit = marginOuter.value.unit;
marginOuterDetailed.value.right.value = marginOuter.value.value;
marginOuterDetailed.value.right.unit = marginOuter.value.unit;
}
});
watch(marginInnerExpanded, (newValue) => {
if (newValue) {
// When expanding, copy simple value to all detailed fields
marginInnerDetailed.value.top.value = marginInner.value.value;
marginInnerDetailed.value.top.unit = marginInner.value.unit;
marginInnerDetailed.value.bottom.value = marginInner.value.value;
marginInnerDetailed.value.bottom.unit = marginInner.value.unit;
marginInnerDetailed.value.left.value = marginInner.value.value;
marginInnerDetailed.value.left.unit = marginInner.value.unit;
marginInnerDetailed.value.right.value = marginInner.value.value;
marginInnerDetailed.value.right.unit = marginInner.value.unit;
}
});
onMounted(() => {
Coloris.init();
Coloris({
themeMode: 'dark',
alpha: true,
format: 'auto',
formatToggle: true,
swatches: [
'#000000',
'#FFFFFF',
'#FF0000',
'#00FF00',
'#0000FF',
'transparent',
],
});
// Sync initial values from stylesheet
syncFromStore();
});
// Sync values from store to form fields
const syncFromStore = () => {
isUpdatingFromStore = true;
const bodyBlock = stylesheetStore.extractBlock('body');
const paragraphBlock = stylesheetStore.extractBlock('p');
// Extract from body block (italic, alignment, color, background)
if (bodyBlock) {
// Extract font-style (italic)
const fontStyleMatch = bodyBlock.match(/font-style:\s*([^;]+)/i);
if (fontStyleMatch) {
italic.value = fontStyleMatch[1].trim() === 'italic';
}
// Extract text-align
const textAlignMatch = bodyBlock.match(/text-align:\s*([^;]+)/i);
if (textAlignMatch) {
alignment.value = textAlignMatch[1].trim();
}
// Extract color
const colorMatch = bodyBlock.match(/color:\s*([^;]+)/i);
if (colorMatch) {
color.value.value = colorMatch[1].trim();
}
// Extract background
const backgroundMatch = bodyBlock.match(/background:\s*([^;]+)/i);
if (backgroundMatch) {
background.value.value = backgroundMatch[1].trim();
}
}
// Extract from paragraph block (weight, size, margins, padding)
if (paragraphBlock) {
// Extract font-weight
const fontWeightMatch = paragraphBlock.match(/font-weight:\s*([^;]+)/i);
if (fontWeightMatch) {
weight.value = fontWeightMatch[1].trim();
}
// Extract font-size
const fontSizeMatch = paragraphBlock.match(/font-size:\s*([0-9.]+)(px|em|rem)/i);
if (fontSizeMatch) {
fontSize.value.value = parseFloat(fontSizeMatch[1]);
fontSize.value.unit = fontSizeMatch[2];
}
// Extract margin (simple or detailed)
const marginMatch = paragraphBlock.match(/margin:\s*([^;]+)/i);
const marginTopMatch = paragraphBlock.match(/margin-top:\s*([0-9.]+)(mm|px)/i);
if (marginTopMatch) {
// Has detailed margins (margin-top, margin-right, etc.)
const marginBottomMatch = paragraphBlock.match(/margin-bottom:\s*([0-9.]+)(mm|px)/i);
const marginLeftMatch = paragraphBlock.match(/margin-left:\s*([0-9.]+)(mm|px)/i);
const marginRightMatch = paragraphBlock.match(/margin-right:\s*([0-9.]+)(mm|px)/i);
if (marginTopMatch) {
marginOuterDetailed.value.top.value = parseFloat(marginTopMatch[1]);
marginOuterDetailed.value.top.unit = marginTopMatch[2];
}
if (marginBottomMatch) {
marginOuterDetailed.value.bottom.value = parseFloat(marginBottomMatch[1]);
marginOuterDetailed.value.bottom.unit = marginBottomMatch[2];
}
if (marginLeftMatch) {
marginOuterDetailed.value.left.value = parseFloat(marginLeftMatch[1]);
marginOuterDetailed.value.left.unit = marginLeftMatch[2];
}
if (marginRightMatch) {
marginOuterDetailed.value.right.value = parseFloat(marginRightMatch[1]);
marginOuterDetailed.value.right.unit = marginRightMatch[2];
}
} else if (marginMatch) {
// Has shorthand margin - check if it's 1 value or 4 values
const marginValue = marginMatch[1].trim();
// Check for 4-value format: "0mm 0mm 24mm 0mm" (top right bottom left)
const fourValueMatch = marginValue.match(/^([0-9.]+)(mm|px)\s+([0-9.]+)(mm|px)\s+([0-9.]+)(mm|px)\s+([0-9.]+)(mm|px)$/i);
if (fourValueMatch) {
// Parse 4-value format and store in detailed margins
marginOuterDetailed.value.top.value = parseFloat(fourValueMatch[1]);
marginOuterDetailed.value.top.unit = fourValueMatch[2];
marginOuterDetailed.value.right.value = parseFloat(fourValueMatch[3]);
marginOuterDetailed.value.right.unit = fourValueMatch[4];
marginOuterDetailed.value.bottom.value = parseFloat(fourValueMatch[5]);
marginOuterDetailed.value.bottom.unit = fourValueMatch[6];
marginOuterDetailed.value.left.value = parseFloat(fourValueMatch[7]);
marginOuterDetailed.value.left.unit = fourValueMatch[8];
} else {
// Single value format: "10mm"
const singleValueMatch = marginValue.match(/^([0-9.]+)(mm|px)$/i);
if (singleValueMatch) {
marginOuter.value.value = parseFloat(singleValueMatch[1]);
marginOuter.value.unit = singleValueMatch[2];
}
}
}
// Extract padding (simple or detailed)
const paddingMatch = paragraphBlock.match(/padding:\s*([^;]+)/i);
const paddingTopMatch = paragraphBlock.match(/padding-top:\s*([0-9.]+)(mm|px)/i);
if (paddingTopMatch) {
// Has detailed padding (padding-top, padding-right, etc.)
const paddingBottomMatch = paragraphBlock.match(/padding-bottom:\s*([0-9.]+)(mm|px)/i);
const paddingLeftMatch = paragraphBlock.match(/padding-left:\s*([0-9.]+)(mm|px)/i);
const paddingRightMatch = paragraphBlock.match(/padding-right:\s*([0-9.]+)(mm|px)/i);
if (paddingTopMatch) {
marginInnerDetailed.value.top.value = parseFloat(paddingTopMatch[1]);
marginInnerDetailed.value.top.unit = paddingTopMatch[2];
}
if (paddingBottomMatch) {
marginInnerDetailed.value.bottom.value = parseFloat(paddingBottomMatch[1]);
marginInnerDetailed.value.bottom.unit = paddingBottomMatch[2];
}
if (paddingLeftMatch) {
marginInnerDetailed.value.left.value = parseFloat(paddingLeftMatch[1]);
marginInnerDetailed.value.left.unit = paddingLeftMatch[2];
}
if (paddingRightMatch) {
marginInnerDetailed.value.right.value = parseFloat(paddingRightMatch[1]);
marginInnerDetailed.value.right.unit = paddingRightMatch[2];
}
} else if (paddingMatch) {
// Has shorthand padding - check if it's 1 value or 4 values
const paddingValue = paddingMatch[1].trim();
// Check for 4-value format: "0mm 0mm 0mm 0mm" (top right bottom left)
const fourValueMatch = paddingValue.match(/^([0-9.]+)(mm|px)\s+([0-9.]+)(mm|px)\s+([0-9.]+)(mm|px)\s+([0-9.]+)(mm|px)$/i);
if (fourValueMatch) {
// Parse 4-value format and store in detailed padding
marginInnerDetailed.value.top.value = parseFloat(fourValueMatch[1]);
marginInnerDetailed.value.top.unit = fourValueMatch[2];
marginInnerDetailed.value.right.value = parseFloat(fourValueMatch[3]);
marginInnerDetailed.value.right.unit = fourValueMatch[4];
marginInnerDetailed.value.bottom.value = parseFloat(fourValueMatch[5]);
marginInnerDetailed.value.bottom.unit = fourValueMatch[6];
marginInnerDetailed.value.left.value = parseFloat(fourValueMatch[7]);
marginInnerDetailed.value.left.unit = fourValueMatch[8];
} else {
// Single value format: "10mm"
const singleValueMatch = paddingValue.match(/^([0-9.]+)(mm|px)$/i);
if (singleValueMatch) {
marginInner.value.value = parseFloat(singleValueMatch[1]);
marginInner.value.unit = singleValueMatch[2];
}
}
}
}
isUpdatingFromStore = false;
};
</script>