From 628f666d6aba9eb48e15df59c9405b318acf10a0 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Fri, 5 Dec 2025 16:18:31 +0100 Subject: [PATCH] feat: implement TextSettings with reactive CSS editing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements functional text settings panel with real-time CSS updates: - Text styling: italic, weight, alignment applied to body selector - Typography: font-size applied to paragraph selector - Colors: text color and background with Coloris picker - Margins/padding: simple and detailed modes for paragraphs - Smart sync between simple and detailed margin fields - Automatic CSS property cleanup (removes conflicting properties) - Parses existing stylesheet values including 4-value shorthand - Default margins: 0mm top/left/right, 24mm bottom for paragraphs Technical details: - Uses Pinia store extractBlock() for CSS manipulation - Implements isSyncingFromSimple flag to prevent watcher conflicts - Syncs detailed fields when modifying simple field (even when expanded) - Removes shorthand properties when using detailed, and vice versa - Fixed background color alpha channel (0 → 1) for Coloris visibility 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- public/assets/css/stylesheet.css | 3 +- src/components/editor/TextSettings.vue | 465 ++++++++++++++++++++++--- 2 files changed, 424 insertions(+), 44 deletions(-) diff --git a/public/assets/css/stylesheet.css b/public/assets/css/stylesheet.css index 919f29e..d6f0264 100644 --- a/public/assets/css/stylesheet.css +++ b/public/assets/css/stylesheet.css @@ -1,7 +1,7 @@ @page { size: A4; margin: 20mm 15mm 26mm 15mm; - background: rgba(255, 255, 255, 0); + background: rgba(255, 255, 255, 1); } @page { @@ -24,4 +24,5 @@ h2 { p { font-size: 1rem; + margin: 0mm 0mm 24mm 0mm; } diff --git a/src/components/editor/TextSettings.vue b/src/components/editor/TextSettings.vue index 33c4551..75538bf 100644 --- a/src/components/editor/TextSettings.vue +++ b/src/components/editor/TextSettings.vue @@ -486,6 +486,7 @@ 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); @@ -524,70 +525,201 @@ const background = ref({ // Margin outer const marginOuter = ref({ - value: 23, + value: 0, 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' }, + 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: 23, + value: 0, 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' }, + 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 + // TODO: implement font update when fonts are available }); }); -watch(italic, () => { +watch(italic, (newValue) => { if (isUpdatingFromStore) return; immediateUpdate(() => { - // TODO: implement italic update + updateBodyStyle('font-style', newValue ? 'italic' : 'normal'); }); }); -watch(weight, () => { +watch(weight, (newValue) => { if (isUpdatingFromStore) return; immediateUpdate(() => { - // TODO: implement weight update + updateParagraphStyle('font-weight', newValue); }); }); -watch(alignment, () => { +watch(alignment, (newValue) => { if (isUpdatingFromStore) return; immediateUpdate(() => { - // TODO: implement alignment update + updateBodyStyle('text-align', newValue); }); }); -// Font size - debounced for value, immediate for unit +// Font size - immediate for both value and unit (applied to paragraphs) watch( () => fontSize.value.value, () => { if (isUpdatingFromStore) return; - debouncedUpdate(() => { - // TODO: implement font size update + immediateUpdate(() => { + updateParagraphStyle('font-size', `${fontSize.value.value}${fontSize.value.unit}`); }); } ); @@ -597,40 +729,50 @@ watch( () => { if (isUpdatingFromStore) return; immediateUpdate(() => { - // TODO: implement font size update + updateParagraphStyle('font-size', `${fontSize.value.value}${fontSize.value.unit}`); }); } ); -// Color - debounced for text value +// Color - immediate update for Coloris watch( () => color.value.value, () => { if (isUpdatingFromStore) return; - debouncedUpdate(() => { - // TODO: implement color update + immediateUpdate(() => { + updateBodyStyle('color', color.value.value); }); } ); -// Background - debounced for value +// Background - immediate update for Coloris watch( () => background.value.value, () => { if (isUpdatingFromStore) return; - debouncedUpdate(() => { - // TODO: implement background update + immediateUpdate(() => { + updateBodyStyle('background', background.value.value); }); } ); -// Margin outer - debounced for value, immediate for unit +// 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(() => { - // TODO: implement margin outer update + removeDetailedMargins(); // Remove detailed properties first + updateParagraphStyle('margin', `${marginOuter.value.value}${marginOuter.value.unit}`); }); } ); @@ -639,13 +781,23 @@ 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(() => { - // TODO: implement margin outer update + removeDetailedMargins(); // Remove detailed properties first + updateParagraphStyle('margin', `${marginOuter.value.value}${marginOuter.value.unit}`); }); } ); -// Margin outer detailed - debounced for values, immediate for units +// Margin outer detailed - debounced for values, immediate for units (applied to paragraphs) watch( () => [ marginOuterDetailed.value.top.value, @@ -654,9 +806,13 @@ watch( marginOuterDetailed.value.right.value, ], () => { - if (isUpdatingFromStore) return; + if (isUpdatingFromStore || !marginOuterExpanded.value || isSyncingFromSimple) return; debouncedUpdate(() => { - // TODO: implement margin outer detailed update + 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}`); }); } ); @@ -669,20 +825,34 @@ watch( marginOuterDetailed.value.right.unit, ], () => { - if (isUpdatingFromStore) return; + if (isUpdatingFromStore || !marginOuterExpanded.value || isSyncingFromSimple) return; immediateUpdate(() => { - // TODO: implement margin outer detailed update + 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 +// 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(() => { - // TODO: implement margin inner update + removeDetailedPadding(); // Remove detailed properties first + updateParagraphStyle('padding', `${marginInner.value.value}${marginInner.value.unit}`); }); } ); @@ -691,13 +861,23 @@ 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(() => { - // TODO: implement margin inner update + removeDetailedPadding(); // Remove detailed properties first + updateParagraphStyle('padding', `${marginInner.value.value}${marginInner.value.unit}`); }); } ); -// Margin inner detailed - debounced for values, immediate for units +// Margin inner detailed - debounced for values, immediate for units (applied to paragraphs) watch( () => [ marginInnerDetailed.value.top.value, @@ -706,9 +886,13 @@ watch( marginInnerDetailed.value.right.value, ], () => { - if (isUpdatingFromStore) return; + if (isUpdatingFromStore || !marginInnerExpanded.value || isSyncingFromSimple) return; debouncedUpdate(() => { - // TODO: implement margin inner detailed update + 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}`); }); } ); @@ -721,13 +905,46 @@ watch( marginInnerDetailed.value.right.unit, ], () => { - if (isUpdatingFromStore) return; + if (isUpdatingFromStore || !marginInnerExpanded.value || isSyncingFromSimple) return; immediateUpdate(() => { - // TODO: implement margin inner detailed update + 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({ @@ -744,5 +961,167 @@ onMounted(() => { '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; +};