Compare commits
3 commits
eac7acdbc6
...
cc36b73325
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc36b73325 | ||
|
|
fc6391a53d | ||
|
|
154804ee44 |
6 changed files with 410 additions and 71 deletions
|
|
@ -67,11 +67,18 @@ input[type="number"] {
|
|||
grid-template-columns: var(--label-w) 1fr;
|
||||
grid-template-rows: var(--input-h) var(--input-h);
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
|
||||
|
||||
.field-font__options{
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
.field-checkbox {
|
||||
grid-column: 2;
|
||||
padding-top: var(--space-xs);
|
||||
label {
|
||||
font-weight: 400;
|
||||
|
|
|
|||
|
|
@ -331,11 +331,15 @@ input[type=number] {
|
|||
grid-template-columns: var(--label-w) 1fr;
|
||||
grid-template-rows: var(--input-h) var(--input-h);
|
||||
}
|
||||
.field-font select {
|
||||
.field-font .field-font__options {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
}
|
||||
.field-font .field-font__options select {
|
||||
width: 100%;
|
||||
grid-column: span 2;
|
||||
}
|
||||
.field-font .field-checkbox {
|
||||
grid-column: 2;
|
||||
padding-top: var(--space-xs);
|
||||
}
|
||||
.field-font .field-checkbox label {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -20,7 +20,7 @@
|
|||
<div class="settings-subsection">
|
||||
<div class="field field-font" :class="{ 'field--view-only': inheritanceLocked }">
|
||||
<label class="label-with-tooltip" data-css="font-family">Police</label>
|
||||
<div class="field-with-option">
|
||||
<div class="field-font__options">
|
||||
<select v-model="fontFamily" :disabled="inheritanceLocked">
|
||||
<option v-for="f in fonts" :key="f" :value="f">{{ f }}</option>
|
||||
</select>
|
||||
|
|
@ -28,18 +28,14 @@
|
|||
<input type="checkbox" v-model="italic" :disabled="inheritanceLocked" />
|
||||
<label class="label-with-tooltip" data-css="font-style">Italique</label>
|
||||
</div>
|
||||
<div class="field-checkbox">
|
||||
<input type="checkbox" v-model="bold" :disabled="inheritanceLocked" />
|
||||
<label class="label-with-tooltip" data-css="font-weight">Gras</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Font Weight -->
|
||||
<div class="settings-subsection">
|
||||
<div class="field" :class="{ 'field--view-only': inheritanceLocked }">
|
||||
<label class="label-with-tooltip" data-css="font-weight">Graisse</label>
|
||||
<UnitToggle v-model="fontWeightString" :units="weights" :disabled="inheritanceLocked" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Font Size -->
|
||||
<div class="settings-subsection">
|
||||
<div class="field" :class="{ 'field--view-only': inheritanceLocked }">
|
||||
|
|
@ -47,7 +43,7 @@
|
|||
<div class="input-with-unit">
|
||||
<NumberInput
|
||||
v-model="fontSize.value"
|
||||
:min="0"
|
||||
:min="6"
|
||||
:step="1"
|
||||
:disabled="inheritanceLocked"
|
||||
/>
|
||||
|
|
@ -65,6 +61,33 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- LineHeight -->
|
||||
<div class="settings-subsection">
|
||||
<div class="field" :class="{ 'field--view-only': inheritanceLocked }">
|
||||
<label class="label-with-tooltip" data-css="line-height">Interlignage</label>
|
||||
<div class="input-with-unit">
|
||||
<NumberInput
|
||||
v-model="lineHeight.value"
|
||||
:min="0"
|
||||
:step="1"
|
||||
:disabled="inheritanceLocked"
|
||||
/>
|
||||
<div class="unit-toggle">
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: lineHeight.unit === 'px' }"
|
||||
:disabled="inheritanceLocked"
|
||||
@click="updateUnitPropUnit(lineHeight, 'px')"
|
||||
>
|
||||
px
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Text Alignment -->
|
||||
<div class="settings-subsection">
|
||||
<div class="field" :class="{ 'field--view-only': inheritanceLocked }">
|
||||
|
|
@ -78,10 +101,56 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bordure -->
|
||||
<div class="settings-subsection">
|
||||
<div class="field field-border" :class="{ 'field--view-only': inheritanceLocked }">
|
||||
<div class="settings-subsection-header">
|
||||
<label class="label-with-tooltip" data-css="border">Bordure</label>
|
||||
</div>
|
||||
<div class="field-border__options">
|
||||
<div class="field-border__option">
|
||||
<label class="label-with-tooltip" data-css="border-width">Épaisseur</label>
|
||||
<div class="input-with-unit">
|
||||
<NumberInput
|
||||
v-model="borderWidth.value"
|
||||
:min="0"
|
||||
:step="1"
|
||||
:disabled="inheritanceLocked"
|
||||
/>
|
||||
<div class="unit-toggle">
|
||||
<button type="button" class="active" :disabled="inheritanceLocked">px</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-border__option">
|
||||
<label class="label-with-tooltip" data-css="border-style">Style</label>
|
||||
<select v-model="borderStyle" :disabled="inheritanceLocked">
|
||||
<option value="solid">Plein</option>
|
||||
<option value="dotted">Pointillés</option>
|
||||
<option value="dashed">Tirets</option>
|
||||
<option value="double">Double</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-border__option">
|
||||
<label class="label-with-tooltip" data-css="border-color">Couleur</label>
|
||||
<div class="input-with-color">
|
||||
<input
|
||||
ref="borderColorInput"
|
||||
type="text"
|
||||
v-model="borderColor"
|
||||
:disabled="inheritanceLocked"
|
||||
data-coloris
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Color -->
|
||||
<div class="settings-subsection">
|
||||
<div class="field field-simple" :class="{ 'field--view-only': inheritanceLocked }">
|
||||
<label class="label-with-tooltip" data-css="color">Couleur</label>
|
||||
<label class="label-with-tooltip" data-css="color">Couleur du texte</label>
|
||||
<div class="input-with-color">
|
||||
<input
|
||||
ref="colorInput"
|
||||
|
|
@ -112,32 +181,54 @@
|
|||
|
||||
<!-- Outer Margins -->
|
||||
<div class="settings-subsection">
|
||||
<div class="field" :class="{ 'field--view-only': inheritanceLocked }">
|
||||
<label class="label-with-tooltip" data-css="margin">Marges extérieures</label>
|
||||
<div class="settings-subsection-header">
|
||||
<span class="label-with-tooltip" data-css="margin">Marges extérieures</span>
|
||||
<button
|
||||
type="button"
|
||||
class="lock-toggle"
|
||||
:class="{ locked: marginLocked }"
|
||||
:disabled="inheritanceLocked"
|
||||
@click="marginLocked = !marginLocked"
|
||||
:title="marginLocked ? 'Déverrouiller (modifier indépendamment)' : 'Verrouiller (modifier ensemble)'"
|
||||
>
|
||||
<svg v-if="marginLocked" width="11" height="13" viewBox="0 0 11 13" fill="none">
|
||||
<rect x="1" y="5.5" width="9" height="7" rx="1" fill="currentColor"/>
|
||||
<path d="M2.5 5.5V3.5a3 3 0 0 1 6 0v2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<svg v-else width="11" height="13" viewBox="0 0 11 13" fill="none">
|
||||
<rect x="1" y="5.5" width="9" height="7" rx="1" fill="currentColor"/>
|
||||
<path d="M2.5 5.5V3.5a3 3 0 0 1 6 0" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-for="side in sides"
|
||||
:key="side.key"
|
||||
class="field field-margin"
|
||||
:class="{ 'field--view-only': inheritanceLocked }"
|
||||
>
|
||||
<label class="label-with-tooltip" :data-css="`margin-${side.key}`">{{ side.label }}</label>
|
||||
<div class="input-with-unit">
|
||||
<NumberInput
|
||||
v-model="marginOuter.value"
|
||||
:modelValue="margin[side.key].value"
|
||||
:min="0"
|
||||
:step="1"
|
||||
:disabled="inheritanceLocked"
|
||||
@update:modelValue="(v) => updateMarginValue(side.key, v)"
|
||||
/>
|
||||
<div class="unit-toggle">
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: marginOuter.unit === 'mm' }"
|
||||
:class="{ active: margin[side.key].unit === 'mm' }"
|
||||
:disabled="inheritanceLocked"
|
||||
@click="updateUnitPropUnit(marginOuter, 'mm')"
|
||||
>
|
||||
mm
|
||||
</button>
|
||||
@click="updateMarginUnit(side.key, 'mm')"
|
||||
>mm</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: marginOuter.unit === 'px' }"
|
||||
:class="{ active: margin[side.key].unit === 'px' }"
|
||||
:disabled="inheritanceLocked"
|
||||
@click="updateUnitPropUnit(marginOuter, 'px')"
|
||||
>
|
||||
px
|
||||
</button>
|
||||
@click="updateMarginUnit(side.key, 'px')"
|
||||
>px</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -145,32 +236,54 @@
|
|||
|
||||
<!-- Inner Margins (Padding) -->
|
||||
<div class="settings-subsection">
|
||||
<div class="field" :class="{ 'field--view-only': inheritanceLocked }">
|
||||
<label class="label-with-tooltip" data-css="padding">Marges intérieures</label>
|
||||
<div class="settings-subsection-header">
|
||||
<span class="label-with-tooltip" data-css="padding">Marges intérieures</span>
|
||||
<button
|
||||
type="button"
|
||||
class="lock-toggle"
|
||||
:class="{ locked: paddingLocked }"
|
||||
:disabled="inheritanceLocked"
|
||||
@click="paddingLocked = !paddingLocked"
|
||||
:title="paddingLocked ? 'Déverrouiller (modifier indépendamment)' : 'Verrouiller (modifier ensemble)'"
|
||||
>
|
||||
<svg v-if="paddingLocked" width="11" height="13" viewBox="0 0 11 13" fill="none">
|
||||
<rect x="1" y="5.5" width="9" height="7" rx="1" fill="currentColor"/>
|
||||
<path d="M2.5 5.5V3.5a3 3 0 0 1 6 0v2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<svg v-else width="11" height="13" viewBox="0 0 11 13" fill="none">
|
||||
<rect x="1" y="5.5" width="9" height="7" rx="1" fill="currentColor"/>
|
||||
<path d="M2.5 5.5V3.5a3 3 0 0 1 6 0" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-for="side in sides"
|
||||
:key="side.key"
|
||||
class="field field-margin"
|
||||
:class="{ 'field--view-only': inheritanceLocked }"
|
||||
>
|
||||
<label class="label-with-tooltip" :data-css="`padding-${side.key}`">{{ side.label }}</label>
|
||||
<div class="input-with-unit">
|
||||
<NumberInput
|
||||
v-model="paddingInner.value"
|
||||
:modelValue="padding[side.key].value"
|
||||
:min="0"
|
||||
:step="1"
|
||||
:disabled="inheritanceLocked"
|
||||
@update:modelValue="(v) => updatePaddingValue(side.key, v)"
|
||||
/>
|
||||
<div class="unit-toggle">
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: paddingInner.unit === 'mm' }"
|
||||
:class="{ active: padding[side.key].unit === 'mm' }"
|
||||
:disabled="inheritanceLocked"
|
||||
@click="updateUnitPropUnit(paddingInner, 'mm')"
|
||||
>
|
||||
mm
|
||||
</button>
|
||||
@click="updatePaddingUnit(side.key, 'mm')"
|
||||
>mm</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: paddingInner.unit === 'px' }"
|
||||
:class="{ active: padding[side.key].unit === 'px' }"
|
||||
:disabled="inheritanceLocked"
|
||||
@click="updateUnitPropUnit(paddingInner, 'px')"
|
||||
>
|
||||
px
|
||||
</button>
|
||||
@click="updatePaddingUnit(side.key, 'px')"
|
||||
>px</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -184,7 +297,6 @@ import { ref, reactive, computed, watch, nextTick } from 'vue';
|
|||
import { useStylesheetStore } from '../stores/stylesheet';
|
||||
import { useDebounce } from '../composables/useDebounce';
|
||||
import NumberInput from './ui/NumberInput.vue';
|
||||
import UnitToggle from './ui/UnitToggle.vue';
|
||||
import BasePopup from './ui/BasePopup.vue';
|
||||
import { convertUnit } from '../utils/unit-conversion';
|
||||
|
||||
|
|
@ -206,6 +318,7 @@ const selectedElement = ref(null);
|
|||
const elementInstanceCount = ref(0);
|
||||
const colorInput = ref(null);
|
||||
const backgroundInput = ref(null);
|
||||
const borderColorInput = ref(null);
|
||||
|
||||
let isUpdatingFromStore = false;
|
||||
const { debouncedUpdate } = useDebounce(500);
|
||||
|
|
@ -213,39 +326,100 @@ const { debouncedUpdate } = useDebounce(500);
|
|||
// Style properties — flat refs for simple values, reactive for value+unit
|
||||
const fontFamily = ref('Alegreya Sans');
|
||||
const italic = ref(false);
|
||||
const fontWeight = ref(400);
|
||||
const bold = ref(false);
|
||||
const textAlign = ref('left');
|
||||
const color = ref('rgb(0, 0, 0)');
|
||||
const background = ref('transparent');
|
||||
const fontSize = reactive({ value: 23, unit: 'px' });
|
||||
const marginOuter = reactive({ value: 0, unit: 'mm' });
|
||||
const paddingInner = reactive({ value: 0, unit: 'mm' });
|
||||
const lineHeight = reactive({ value: 28, unit: 'px' });
|
||||
const borderWidth = reactive({ value: 0, unit: 'px' });
|
||||
const borderStyle = ref('solid');
|
||||
const borderColor = ref('#000000');
|
||||
|
||||
const marginLocked = ref(true);
|
||||
const margin = reactive({
|
||||
top: { value: 0, unit: 'mm' },
|
||||
right: { value: 0, unit: 'mm' },
|
||||
bottom: { value: 0, unit: 'mm' },
|
||||
left: { value: 0, unit: 'mm' },
|
||||
});
|
||||
|
||||
const paddingLocked = ref(true);
|
||||
const padding = reactive({
|
||||
top: { value: 0, unit: 'mm' },
|
||||
right: { value: 0, unit: 'mm' },
|
||||
bottom: { value: 0, unit: 'mm' },
|
||||
left: { value: 0, unit: 'mm' },
|
||||
});
|
||||
|
||||
// Constants
|
||||
const fonts = ['Alegreya Sans', 'Alegreya', 'Arial', 'Georgia', 'Times New Roman'];
|
||||
const weights = ['200', '300', '400', '600', '800'];
|
||||
|
||||
const fontWeightString = computed({
|
||||
get: () => String(fontWeight.value),
|
||||
set: (val) => { fontWeight.value = parseInt(val); }
|
||||
});
|
||||
|
||||
// Style property descriptors
|
||||
const styleProps = [
|
||||
{ css: 'font-family', get: () => fontFamily.value, set: v => fontFamily.value = v.replace(/['"]/g, ''), debounce: false },
|
||||
{ css: 'font-style', get: () => italic.value ? 'italic' : 'normal', set: v => italic.value = v === 'italic', debounce: false },
|
||||
{ css: 'font-weight', get: () => fontWeight.value, set: v => fontWeight.value = parseInt(v), debounce: false },
|
||||
{ css: 'font-weight', get: () => bold.value ? 'bold' : 'normal', set: v => bold.value = v === 'bold' || parseInt(v) >= 700, debounce: false },
|
||||
{ css: 'text-align', get: () => textAlign.value, set: v => textAlign.value = v, debounce: false },
|
||||
{ css: 'color', get: () => color.value, set: v => color.value = v, debounce: true },
|
||||
{ css: 'background', get: () => background.value, set: v => background.value = v, debounce: true },
|
||||
{ css: 'border-style', get: () => borderStyle.value, set: v => borderStyle.value = v || 'solid', debounce: false },
|
||||
{ css: 'border-color', get: () => borderColor.value, set: v => borderColor.value = v, debounce: true },
|
||||
];
|
||||
|
||||
const unitProps = [
|
||||
{ css: 'font-size', ref: fontSize, debounce: true },
|
||||
{ css: 'margin', ref: marginOuter, debounce: true },
|
||||
{ css: 'padding', ref: paddingInner, debounce: true },
|
||||
{ css: 'line-height', ref: lineHeight, debounce: true },
|
||||
{ css: 'border-width', ref: borderWidth, debounce: true },
|
||||
];
|
||||
|
||||
const sides = [
|
||||
{ key: 'top', label: 'Haut' },
|
||||
{ key: 'bottom', label: 'Bas' },
|
||||
{ key: 'left', label: 'Gauche' },
|
||||
{ key: 'right', label: 'Droite' },
|
||||
];
|
||||
|
||||
const updateMarginValue = (side, value) => {
|
||||
if (marginLocked.value) {
|
||||
for (const s of ['top', 'right', 'bottom', 'left']) margin[s].value = value;
|
||||
} else {
|
||||
margin[side].value = value;
|
||||
}
|
||||
};
|
||||
|
||||
const updateMarginUnit = (side, unit) => {
|
||||
if (marginLocked.value) {
|
||||
for (const s of ['top', 'right', 'bottom', 'left']) {
|
||||
margin[s].value = convertUnit(margin[s].value, margin[s].unit, unit);
|
||||
margin[s].unit = unit;
|
||||
}
|
||||
} else {
|
||||
margin[side].value = convertUnit(margin[side].value, margin[side].unit, unit);
|
||||
margin[side].unit = unit;
|
||||
}
|
||||
};
|
||||
|
||||
const updatePaddingValue = (side, value) => {
|
||||
if (paddingLocked.value) {
|
||||
for (const s of ['top', 'right', 'bottom', 'left']) padding[s].value = value;
|
||||
} else {
|
||||
padding[side].value = value;
|
||||
}
|
||||
};
|
||||
|
||||
const updatePaddingUnit = (side, unit) => {
|
||||
if (paddingLocked.value) {
|
||||
for (const s of ['top', 'right', 'bottom', 'left']) {
|
||||
padding[s].value = convertUnit(padding[s].value, padding[s].unit, unit);
|
||||
padding[s].unit = unit;
|
||||
}
|
||||
} else {
|
||||
padding[side].value = convertUnit(padding[side].value, padding[side].unit, unit);
|
||||
padding[side].unit = unit;
|
||||
}
|
||||
};
|
||||
|
||||
// Generic update: push a single property to the stylesheet store
|
||||
const updateProp = (cssProp, value, unit) => {
|
||||
if (!selector.value) return;
|
||||
|
|
@ -296,9 +470,11 @@ const generatePreviewCss = () => {
|
|||
|
||||
for (const prop of styleProps) {
|
||||
const val = prop.get();
|
||||
if (val && (prop.css !== 'font-style' || val === 'italic')) {
|
||||
properties.push(` ${prop.css}: ${val};`);
|
||||
}
|
||||
if (!val) continue;
|
||||
if (prop.css === 'font-style' && val !== 'italic') continue;
|
||||
if (prop.css === 'font-weight' && val === 'normal') continue;
|
||||
if ((prop.css === 'border-style' || prop.css === 'border-color') && borderWidth.value === 0) continue;
|
||||
properties.push(` ${prop.css}: ${val};`);
|
||||
}
|
||||
|
||||
for (const prop of unitProps) {
|
||||
|
|
@ -307,6 +483,17 @@ const generatePreviewCss = () => {
|
|||
}
|
||||
}
|
||||
|
||||
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||
if (margin[side].value !== undefined && margin[side].value !== null) {
|
||||
properties.push(` margin-${side}: ${margin[side].value}${margin[side].unit};`);
|
||||
}
|
||||
}
|
||||
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||
if (padding[side].value !== undefined && padding[side].value !== null) {
|
||||
properties.push(` padding-${side}: ${padding[side].value}${padding[side].unit};`);
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.length === 0) return '';
|
||||
|
||||
return `${selector.value} {\n${properties.join('\n')}\n}`;
|
||||
|
|
@ -335,6 +522,10 @@ const applyAllStyles = () => {
|
|||
for (const prop of unitProps) {
|
||||
updateProp(prop.css, prop.ref.value, prop.ref.unit);
|
||||
}
|
||||
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||
updateProp(`margin-${side}`, margin[side].value, margin[side].unit);
|
||||
updateProp(`padding-${side}`, padding[side].value, padding[side].unit);
|
||||
}
|
||||
};
|
||||
|
||||
// Watchers — simple props
|
||||
|
|
@ -359,6 +550,50 @@ for (const prop of unitProps) {
|
|||
});
|
||||
}
|
||||
|
||||
// Watchers — margin sides
|
||||
watch(
|
||||
() => [margin.top.value, margin.right.value, margin.bottom.value, margin.left.value],
|
||||
() => {
|
||||
if (isUpdatingFromStore) return;
|
||||
debouncedUpdate(() => {
|
||||
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||
updateProp(`margin-${side}`, margin[side].value, margin[side].unit);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => [margin.top.unit, margin.right.unit, margin.bottom.unit, margin.left.unit],
|
||||
() => {
|
||||
if (isUpdatingFromStore) return;
|
||||
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||
updateProp(`margin-${side}`, margin[side].value, margin[side].unit);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Watchers — padding sides
|
||||
watch(
|
||||
() => [padding.top.value, padding.right.value, padding.bottom.value, padding.left.value],
|
||||
() => {
|
||||
if (isUpdatingFromStore) return;
|
||||
debouncedUpdate(() => {
|
||||
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||
updateProp(`padding-${side}`, padding[side].value, padding[side].unit);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => [padding.top.unit, padding.right.unit, padding.bottom.unit, padding.left.unit],
|
||||
() => {
|
||||
if (isUpdatingFromStore) return;
|
||||
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||
updateProp(`padding-${side}`, padding[side].value, padding[side].unit);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const handleCssInput = (newCss) => {
|
||||
const oldBlock = elementCss.value;
|
||||
if (oldBlock) {
|
||||
|
|
@ -411,6 +646,47 @@ const loadValuesFromStylesheet = () => {
|
|||
prop.ref.unit = data.unit;
|
||||
}
|
||||
}
|
||||
|
||||
// Margin sides — try individual first, fallback to shorthand
|
||||
const spacingSides = ['top', 'right', 'bottom', 'left'];
|
||||
let anyMarginFound = false;
|
||||
for (const side of spacingSides) {
|
||||
const data = stylesheetStore.extractValue(selector.value, `margin-${side}`);
|
||||
if (data && data.value !== undefined) {
|
||||
margin[side].value = data.value;
|
||||
margin[side].unit = data.unit;
|
||||
anyMarginFound = true;
|
||||
}
|
||||
}
|
||||
if (!anyMarginFound) {
|
||||
const data = stylesheetStore.extractValue(selector.value, 'margin');
|
||||
if (data && data.value !== undefined) {
|
||||
for (const side of spacingSides) {
|
||||
margin[side].value = data.value;
|
||||
margin[side].unit = data.unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Padding sides — try individual first, fallback to shorthand
|
||||
let anyPaddingFound = false;
|
||||
for (const side of spacingSides) {
|
||||
const data = stylesheetStore.extractValue(selector.value, `padding-${side}`);
|
||||
if (data && data.value !== undefined) {
|
||||
padding[side].value = data.value;
|
||||
padding[side].unit = data.unit;
|
||||
anyPaddingFound = true;
|
||||
}
|
||||
}
|
||||
if (!anyPaddingFound) {
|
||||
const data = stylesheetStore.extractValue(selector.value, 'padding');
|
||||
if (data && data.value !== undefined) {
|
||||
for (const side of spacingSides) {
|
||||
padding[side].value = data.value;
|
||||
padding[side].unit = data.unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading values from stylesheet:', error);
|
||||
}
|
||||
|
|
@ -470,7 +746,7 @@ const toggleInheritance = () => {
|
|||
|
||||
fontFamily.value = cs.fontFamily.replace(/['"]/g, '').split(',')[0].trim();
|
||||
italic.value = cs.fontStyle === 'italic';
|
||||
fontWeight.value = parseInt(cs.fontWeight);
|
||||
bold.value = parseInt(cs.fontWeight) >= 700;
|
||||
|
||||
const fontSizeMatch = cs.fontSize.match(/([\d.]+)(px|rem|em|pt)/);
|
||||
if (fontSizeMatch) {
|
||||
|
|
@ -478,20 +754,36 @@ const toggleInheritance = () => {
|
|||
fontSize.unit = fontSizeMatch[2];
|
||||
}
|
||||
|
||||
const lineHeightMatch = cs.lineHeight.match(/([\d.]+)(px|rem|em|pt)/);
|
||||
if (lineHeightMatch) {
|
||||
lineHeight.value = parseFloat(lineHeightMatch[1]);
|
||||
lineHeight.unit = lineHeightMatch[2];
|
||||
}
|
||||
|
||||
textAlign.value = cs.textAlign;
|
||||
color.value = cs.color;
|
||||
background.value = cs.backgroundColor;
|
||||
|
||||
const marginMatch = cs.marginTop.match(/([\d.]+)(px|mm|pt)/);
|
||||
if (marginMatch) {
|
||||
marginOuter.value = parseFloat(marginMatch[1]);
|
||||
marginOuter.unit = marginMatch[2];
|
||||
const borderWidthMatch = cs.borderTopWidth.match(/([\d.]+)(px)/);
|
||||
if (borderWidthMatch) {
|
||||
borderWidth.value = parseFloat(borderWidthMatch[1]);
|
||||
borderWidth.unit = 'px';
|
||||
}
|
||||
borderStyle.value = cs.borderTopStyle || 'solid';
|
||||
borderColor.value = cs.borderTopColor || '#000000';
|
||||
|
||||
const paddingMatch = cs.paddingTop.match(/([\d.]+)(px|mm|pt)/);
|
||||
if (paddingMatch) {
|
||||
paddingInner.value = parseFloat(paddingMatch[1]);
|
||||
paddingInner.unit = paddingMatch[2];
|
||||
for (const side of ['top', 'right', 'bottom', 'left']) {
|
||||
const cssSide = side.charAt(0).toUpperCase() + side.slice(1);
|
||||
const marginMatch = cs[`margin${cssSide}`].match(/([\d.]+)(px|mm|pt)/);
|
||||
if (marginMatch) {
|
||||
margin[side].value = parseFloat(marginMatch[1]);
|
||||
margin[side].unit = marginMatch[2];
|
||||
}
|
||||
const paddingMatch = cs[`padding${cssSide}`].match(/([\d.]+)(px|mm|pt)/);
|
||||
if (paddingMatch) {
|
||||
padding[side].value = parseFloat(paddingMatch[1]);
|
||||
padding[side].unit = paddingMatch[2];
|
||||
}
|
||||
}
|
||||
|
||||
isUpdatingFromStore = false;
|
||||
|
|
@ -523,4 +815,38 @@ defineExpose({ handleIframeClick, close, visible });
|
|||
color: var(--color-purple);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.settings-subsection-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.lock-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
padding: 2px 4px;
|
||||
color: var(--color-text-muted, #999);
|
||||
transition: color 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.lock-toggle:hover:not(:disabled) {
|
||||
color: var(--color-text, #333);
|
||||
border-color: var(--color-border, #ccc);
|
||||
}
|
||||
|
||||
.lock-toggle.locked {
|
||||
color: var(--color-purple, #7c3aed);
|
||||
}
|
||||
|
||||
.lock-toggle:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<div class="settings-subsection">
|
||||
<div class="field field-font">
|
||||
<label for="text-font" class="label-with-tooltip field--view-only" data-css="font-family" title="Fonctionnalité à venir">Police</label>
|
||||
<div class="field-with-option">
|
||||
<div class="field-font__options">
|
||||
<select id="text-font" v-model="font" disabled class="field--view-only" title="Fonctionnalité à venir">
|
||||
<option v-for="f in fonts" :key="f" :value="f">{{ f }}</option>
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -89,15 +89,17 @@ const handleInput = (event) => {
|
|||
emit('update:modelValue', value);
|
||||
};
|
||||
|
||||
const decimals = (props.step.toString().split('.')[1] || '').length;
|
||||
|
||||
const increment = () => {
|
||||
const newValue = props.modelValue + props.step;
|
||||
const newValue = parseFloat((props.modelValue + props.step).toFixed(decimals));
|
||||
if (props.max === undefined || newValue <= props.max) {
|
||||
emit('update:modelValue', newValue);
|
||||
}
|
||||
};
|
||||
|
||||
const decrement = () => {
|
||||
const newValue = props.modelValue - props.step;
|
||||
const newValue = parseFloat((props.modelValue - props.step).toFixed(decimals));
|
||||
if (props.min === undefined || newValue >= props.min) {
|
||||
emit('update:modelValue', newValue);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue