feat: add custom page format with editable width/height fields
All checks were successful
Deploy / Build and Deploy to Production (push) Successful in 20s

Keep only A4/A5 + "Personnalisé" option. Width/height fields use same
layout as margin fields and are editable only in custom mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-02-24 14:51:00 +01:00
parent b808e22274
commit 9f62d3ae5d

View file

@ -1,9 +1,11 @@
<template> <template>
<section class="settings-section" id="settings-section_page" data-color-type="page"> <section
class="settings-section"
id="settings-section_page"
data-color-type="page"
>
<h2>Réglage des pages</h2> <h2>Réglage des pages</h2>
<div class="container"> <div class="container">
<div class="settings-subsection"> <div class="settings-subsection">
<div class="field field-simple"> <div class="field field-simple">
<label for="page-format" class="label-with-tooltip" data-css="size" <label for="page-format" class="label-with-tooltip" data-css="size"
@ -12,38 +14,57 @@
<select id="page-format" v-model="pageFormat"> <select id="page-format" v-model="pageFormat">
<option value="A4">A4</option> <option value="A4">A4</option>
<option value="A5">A5</option> <option value="A5">A5</option>
<option value="A3">A3</option> <!-- <option value="A3">A3</option>
<option value="letter">Letter</option> <option value="letter">Letter</option>
<option value="legal">Legal</option> <option value="legal">Legal</option> -->
<option value="custom">Personnalisé</option>
</select> </select>
</div> </div>
</div> </div>
<div class="settings-subsection"> <div class="settings-subsection">
<div class="field field-size field--view-only"> <div
class="field field-margin"
:class="{ 'field--view-only': !isCustomFormat }"
>
<label for="page-width" class="label-with-tooltip" data-css="width" <label for="page-width" class="label-with-tooltip" data-css="width"
>Largeur</label >Largeur</label
> >
<input <div class="input-with-unit">
<NumberInput
id="page-width" id="page-width"
type="number" :modelValue="customWidth"
:value="parseInt(pageWidth)" @update:modelValue="(v) => (customWidth = v)"
disabled :min="50"
:step="1"
:disabled="!isCustomFormat"
/> />
<button type="button" disabled>mm</button> <div class="unit-toggle">
<button type="button" class="active">mm</button>
</div>
</div>
</div> </div>
<div class="field field-size field--view-only"> <div
class="field field-margin"
:class="{ 'field--view-only': !isCustomFormat }"
>
<label for="page-height" class="label-with-tooltip" data-css="height" <label for="page-height" class="label-with-tooltip" data-css="height"
>Hauteur</label >Hauteur</label
> >
<input <div class="input-with-unit">
<NumberInput
id="page-height" id="page-height"
type="number" :modelValue="customHeight"
:value="parseInt(pageHeight)" @update:modelValue="(v) => (customHeight = v)"
disabled :min="50"
:step="1"
:disabled="!isCustomFormat"
/> />
<button type="button" disabled>mm</button> <div class="unit-toggle">
<button type="button" class="active">mm</button>
</div>
</div>
</div> </div>
</div> </div>
@ -51,7 +72,10 @@
<h3>Marges</h3> <h3>Marges</h3>
<div class="field field-margin"> <div class="field field-margin">
<label for="margin-top" class="label-with-tooltip" data-css="margin-top" <label
for="margin-top"
class="label-with-tooltip"
data-css="margin-top"
>Haut</label >Haut</label
> >
<div class="input-with-unit"> <div class="input-with-unit">
@ -60,7 +84,7 @@
:modelValue="margins.top.value" :modelValue="margins.top.value"
:min="0" :min="0"
:step="1" :step="1"
@update:modelValue="(value) => margins.top.value = value" @update:modelValue="(value) => (margins.top.value = value)"
/> />
<div class="unit-toggle"> <div class="unit-toggle">
<button <button
@ -101,7 +125,7 @@
:modelValue="margins.bottom.value" :modelValue="margins.bottom.value"
:min="0" :min="0"
:step="1" :step="1"
@update:modelValue="(value) => margins.bottom.value = value" @update:modelValue="(value) => (margins.bottom.value = value)"
/> />
<div class="unit-toggle"> <div class="unit-toggle">
<button <button
@ -142,7 +166,7 @@
:modelValue="margins.left.value" :modelValue="margins.left.value"
:min="0" :min="0"
:step="1" :step="1"
@update:modelValue="(value) => margins.left.value = value" @update:modelValue="(value) => (margins.left.value = value)"
/> />
<div class="unit-toggle"> <div class="unit-toggle">
<button <button
@ -183,7 +207,7 @@
:modelValue="margins.right.value" :modelValue="margins.right.value"
:min="0" :min="0"
:step="1" :step="1"
@update:modelValue="(value) => margins.right.value = value" @update:modelValue="(value) => (margins.right.value = value)"
/> />
<div class="unit-toggle"> <div class="unit-toggle">
<button <button
@ -214,7 +238,10 @@
<div class="settings-subsection"> <div class="settings-subsection">
<div class="field field-simple"> <div class="field field-simple">
<label for="background" class="label-with-tooltip" data-css="background" <label
for="background"
class="label-with-tooltip"
data-css="background"
>Arrière-plan</label >Arrière-plan</label
> >
<div class="input-with-color"> <div class="input-with-color">
@ -242,8 +269,7 @@
hex hex
</button> </button>
</div> </div>
--> --></div>
</div>
</div> </div>
</div> </div>
@ -308,15 +334,16 @@ let isUpdatingFromStore = false;
const pageFormat = ref('A4'); const pageFormat = ref('A4');
const pageFormats = { const pageFormats = {
A4: { width: '210mm', height: '297mm' }, A4: { width: 210, height: 297 },
A5: { width: '148mm', height: '210mm' }, A5: { width: 148, height: 210 },
A3: { width: '297mm', height: '420mm' }, // A3: { width: 297, height: 420 },
letter: { width: '8.5in', height: '11in' }, // letter: { width: 216, height: 279 },
legal: { width: '8.5in', height: '14in' }, // legal: { width: 216, height: 356 },
}; };
const pageWidth = computed(() => pageFormats[pageFormat.value].width); const customWidth = ref(210);
const pageHeight = computed(() => pageFormats[pageFormat.value].height); const customHeight = ref(297);
const isCustomFormat = computed(() => pageFormat.value === 'custom');
const margins = ref({ const margins = ref({
top: { value: 20, unit: 'mm' }, top: { value: 20, unit: 'mm' },
@ -347,9 +374,37 @@ const immediateUpdate = (callback) => {
watch(pageFormat, (newFormat) => { watch(pageFormat, (newFormat) => {
if (isUpdatingFromStore) return; if (isUpdatingFromStore) return;
if (newFormat === 'custom') {
immediateUpdate(() => {
stylesheetStore.updateProperty(
'@page',
'size',
`${customWidth.value}mm ${customHeight.value}mm`,
''
);
});
} else {
const format = pageFormats[newFormat];
if (format) {
customWidth.value = format.width;
customHeight.value = format.height;
}
immediateUpdate(() => { immediateUpdate(() => {
stylesheetStore.updateProperty('@page', 'size', newFormat, ''); stylesheetStore.updateProperty('@page', 'size', newFormat, '');
}); });
}
});
watch([customWidth, customHeight], () => {
if (isUpdatingFromStore || !isCustomFormat.value) return;
debouncedUpdate(() => {
stylesheetStore.updateProperty(
'@page',
'size',
`${customWidth.value}mm ${customHeight.value}mm`,
''
);
});
}); });
const updateMargins = () => { const updateMargins = () => {
@ -546,9 +601,24 @@ const syncFromStore = () => {
try { try {
const pageBlock = stylesheetStore.extractBlock('@page'); const pageBlock = stylesheetStore.extractBlock('@page');
const sizeMatch = pageBlock.match(/size:\s*([A-Za-z0-9]+)/); // Try named format first (A4, A5), then custom dimensions
if (sizeMatch) { const namedSizeMatch = pageBlock.match(/size:\s*(A4|A5)\b/);
pageFormat.value = sizeMatch[1]; if (namedSizeMatch) {
pageFormat.value = namedSizeMatch[1];
const format = pageFormats[namedSizeMatch[1]];
if (format) {
customWidth.value = format.width;
customHeight.value = format.height;
}
} else {
const customSizeMatch = pageBlock.match(
/size:\s*([0-9.]+)mm\s+([0-9.]+)mm/
);
if (customSizeMatch) {
customWidth.value = parseFloat(customSizeMatch[1]);
customHeight.value = parseFloat(customSizeMatch[2]);
pageFormat.value = 'custom';
}
} }
const marginMatch = pageBlock.match( const marginMatch = pageBlock.match(