Major refactoring to improve code quality and reduce duplication: TextSettings.vue: 1127 → 269 lines (-76%) New composables: - useCssUpdater.js: generic CSS update/remove functions - useCssSync.js: CSS parsing to form fields New UI components: - UnitToggle.vue: reusable unit selector buttons - InputWithUnit.vue: number input with unit toggle - MarginEditor.vue: simple/detailed margin editor with sync Benefits: - Reusable components for other settings panels - Centralized CSS manipulation logic - Better separation of concerns - Easier to test and maintain 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
162 lines
4.1 KiB
Vue
162 lines
4.1 KiB
Vue
<template>
|
|
<div class="margin-editor">
|
|
<div class="field">
|
|
<label :for="id">{{ label }}</label>
|
|
<div class="input-with-unit">
|
|
<input
|
|
:id="id"
|
|
type="number"
|
|
:value="simple.value"
|
|
min="0"
|
|
@input="updateSimpleValue(Number($event.target.value))"
|
|
/>
|
|
<UnitToggle
|
|
:modelValue="simple.unit"
|
|
:units="units"
|
|
@update:modelValue="updateSimpleUnit"
|
|
/>
|
|
<button
|
|
type="button"
|
|
class="collapse-toggle"
|
|
:class="{ expanded }"
|
|
@click="toggleExpanded"
|
|
title="Réglages détaillés"
|
|
>
|
|
▶
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="expanded" class="subsection collapsed-section">
|
|
<div v-for="side in sides" :key="side.key" class="field">
|
|
<label :for="`${id}-${side.key}`">{{ side.label }}</label>
|
|
<div class="input-with-unit">
|
|
<input
|
|
:id="`${id}-${side.key}`"
|
|
type="number"
|
|
:value="detailed[side.key].value"
|
|
min="0"
|
|
@input="updateDetailedValue(side.key, Number($event.target.value))"
|
|
/>
|
|
<UnitToggle
|
|
:modelValue="detailed[side.key].unit"
|
|
:units="units"
|
|
@update:modelValue="(unit) => updateDetailedUnit(side.key, unit)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, watch } from 'vue';
|
|
import UnitToggle from './UnitToggle.vue';
|
|
|
|
const props = defineProps({
|
|
id: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
label: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
simple: {
|
|
type: Object,
|
|
required: true,
|
|
validator: (v) => 'value' in v && 'unit' in v
|
|
},
|
|
detailed: {
|
|
type: Object,
|
|
required: true,
|
|
validator: (v) => ['top', 'right', 'bottom', 'left'].every(k => k in v)
|
|
},
|
|
units: {
|
|
type: Array,
|
|
default: () => ['mm', 'px']
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(['update:simple', 'update:detailed', 'change']);
|
|
|
|
const expanded = ref(false);
|
|
let isSyncing = false;
|
|
|
|
const sides = [
|
|
{ key: 'top', label: 'Haut' },
|
|
{ key: 'bottom', label: 'Bas' },
|
|
{ key: 'left', label: 'Gauche' },
|
|
{ key: 'right', label: 'Droite' }
|
|
];
|
|
|
|
const toggleExpanded = () => {
|
|
expanded.value = !expanded.value;
|
|
if (expanded.value) {
|
|
// Sync detailed values from simple when expanding
|
|
syncDetailedFromSimple();
|
|
}
|
|
};
|
|
|
|
const syncDetailedFromSimple = () => {
|
|
isSyncing = true;
|
|
const newDetailed = {};
|
|
for (const side of sides) {
|
|
newDetailed[side.key] = { value: props.simple.value, unit: props.simple.unit };
|
|
}
|
|
emit('update:detailed', newDetailed);
|
|
isSyncing = false;
|
|
};
|
|
|
|
const updateSimpleValue = (value) => {
|
|
const newSimple = { ...props.simple, value };
|
|
emit('update:simple', newSimple);
|
|
|
|
// Sync all detailed values
|
|
isSyncing = true;
|
|
const newDetailed = {};
|
|
for (const side of sides) {
|
|
newDetailed[side.key] = { value, unit: props.simple.unit };
|
|
}
|
|
emit('update:detailed', newDetailed);
|
|
isSyncing = false;
|
|
|
|
emit('change', { type: 'simple', simple: newSimple });
|
|
};
|
|
|
|
const updateSimpleUnit = (unit) => {
|
|
const newSimple = { ...props.simple, unit };
|
|
emit('update:simple', newSimple);
|
|
|
|
// Sync all detailed units
|
|
isSyncing = true;
|
|
const newDetailed = {};
|
|
for (const side of sides) {
|
|
newDetailed[side.key] = { ...props.detailed[side.key], unit };
|
|
}
|
|
emit('update:detailed', newDetailed);
|
|
isSyncing = false;
|
|
|
|
emit('change', { type: 'simple', simple: newSimple });
|
|
};
|
|
|
|
const updateDetailedValue = (key, value) => {
|
|
if (isSyncing) return;
|
|
const newDetailed = { ...props.detailed, [key]: { ...props.detailed[key], value } };
|
|
emit('update:detailed', newDetailed);
|
|
if (expanded.value) {
|
|
emit('change', { type: 'detailed', detailed: newDetailed });
|
|
}
|
|
};
|
|
|
|
const updateDetailedUnit = (key, unit) => {
|
|
if (isSyncing) return;
|
|
const newDetailed = { ...props.detailed, [key]: { ...props.detailed[key], unit } };
|
|
emit('update:detailed', newDetailed);
|
|
if (expanded.value) {
|
|
emit('change', { type: 'detailed', detailed: newDetailed });
|
|
}
|
|
};
|
|
|
|
defineExpose({ expanded });
|
|
</script>
|