From b5c8ac4f440525d0279ff07b795264cb1b6ed435 Mon Sep 17 00:00:00 2001 From: srenault Date: Tue, 7 Jul 2026 14:35:46 +0200 Subject: [PATCH 01/24] fix: reset global page design width to 1320 instead of 0/300 Switching the page width from full window back to custom collapsed the value to 0 because the customWidth computed property fell back to 0 when width was '100%', which the number-input then clamped to its min (300). Introduce a defaultWidth data property (1320) used as the fallback everywhere the previous default value was hardcoded. Co-Authored-By: Claude Sonnet 5 --- .../common-layout/components/drawer/EditPageDrawer.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/layout-webapp/src/main/webapp/vue-app/common-layout/components/drawer/EditPageDrawer.vue b/layout-webapp/src/main/webapp/vue-app/common-layout/components/drawer/EditPageDrawer.vue index feeae601e..5f2eda727 100644 --- a/layout-webapp/src/main/webapp/vue-app/common-layout/components/drawer/EditPageDrawer.vue +++ b/layout-webapp/src/main/webapp/vue-app/common-layout/components/drawer/EditPageDrawer.vue @@ -190,6 +190,7 @@ export default { appBackgroundProperties: null, fullWindow: false, width: 1320, + defaultWidth: 1320, minWidth: 300, maxWidth: 5000, drawer: false, @@ -206,7 +207,7 @@ export default { }); }, customWidth() { - return this.width === '100%' ? 0 : this.width; + return this.width === '100%' ? this.defaultWidth : this.width; }, }, watch: { @@ -244,9 +245,9 @@ export default { this.pageContainer.marginLeft = this.defaultMarginLeft; } this.width = (this.pageContainer.width === 'fullWindow' ? '100%' : this.pageContainer.width) - || (this.pageContainer.width === 'singlePageApplication' ? 1320 : this.pageContainer.width) + || (this.pageContainer.width === 'singlePageApplication' ? this.defaultWidth : this.pageContainer.width) || (!!document.body.style.getPropertyValue('--allPagesWidth') && '100%') - || 1320; + || this.defaultWidth; this.appBackgroundProperties = { storageId: 0, backgroundColor: this.pageContainer.appBackgroundColor || null, From 3cb3662a66085d2fbe7a304d0cea201a85e5fd09 Mon Sep 17 00:00:00 2001 From: srenault Date: Tue, 7 Jul 2026 18:24:01 +0200 Subject: [PATCH 02/24] feat: add gradient options to page background component (task-88418) Extend the shared BackgroundInput.vue component (used both from site "branding options" and the per-page "Edit Page Properties" drawer for page/site/application backgrounds) with 3 gradient types instead of the single implicit top-to-bottom linear gradient: - Linear Gradient: Top to Bottom (default, matches previous behavior) or Left to Right. - Radial Gradient: From color at the center, To color on the outside. - Angular Gradient: conic gradient anchored at one of the 4 corners (Top Left / Top Right / Bottom Right / Bottom Left), From color used as the start of the angle. backgroundEffect stays an opaque CSS background-image string end to end (no backend/DTO changes needed), so existing persisted values (plain `linear-gradient(from, to)`) keep parsing correctly as "Linear / Top to Bottom". Note: the angle mapping used for the angular/conic gradient per corner is a best-effort CSS implementation (I could not access the linked Adobe XD mockup); please visually confirm against the design before merging. EXO-88418 Co-Authored-By: Claude Sonnet 5 --- .../locale/portlet/LayoutEditor_en.properties | 11 +- .../components/form/BackgroundInput.vue | 145 ++++++++++++++++-- 2 files changed, 140 insertions(+), 16 deletions(-) diff --git a/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties b/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties index 5ce5017cb..7b17afbc1 100644 --- a/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties +++ b/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties @@ -406,6 +406,15 @@ layout.imagePositionBottomRight=Bottom right layout.gradient=Gradient layout.gradientFrom=From layout.gradientTo=To +layout.linearGradient=Linear Gradient +layout.radialGradient=Radial Gradient +layout.angularGradient=Angular Gradient +layout.gradientDirectionTopToBottom=Top to Bottom +layout.gradientDirectionLeftToRight=Left to Right +layout.gradientCornerTopLeft=Top Left +layout.gradientCornerTopRight=Top Right +layout.gradientCornerBottomRight=Bottom Right +layout.gradientCornerBottomLeft=Bottom Left layout.saveDraft=Save layout.saveDraftTooltip=Save page as draft layout.saveSiteDraftTooltip=Save site as draft @@ -427,7 +436,7 @@ layout.textColorTitle=Title layout.textColorHeader=Header layout.textColorBody=Body layout.textColorSubtitle=Subtitle -layout.color=Color +layout.color=Solid Color layout.applicationStyling=Application Styling layout.globalPageDesign=Global Page Design layout.globalPageBackground=Page Background diff --git a/layout-webapp/src/main/webapp/vue-app/common-layout/components/form/BackgroundInput.vue b/layout-webapp/src/main/webapp/vue-app/common-layout/components/form/BackgroundInput.vue index c15041e57..c84ca8f84 100644 --- a/layout-webapp/src/main/webapp/vue-app/common-layout/components/form/BackgroundInput.vue +++ b/layout-webapp/src/main/webapp/vue-app/common-layout/components/form/BackgroundInput.vue @@ -36,7 +36,7 @@ v-if="enabled" class="pa-0" dense> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
+
Date: Tue, 7 Jul 2026 18:47:01 +0200 Subject: [PATCH 03/24] feat: add ratio slider for gradient backgrounds - EXO-88425 Since the corner only shows a 90deg slice of the full 360deg conic gradient, the 2-color transition needs explicit 0deg/90deg stops so it completes within that visible slice instead of barely starting before the box edge cuts it off. Co-Authored-By: Claude Sonnet 5 --- .../locale/portlet/LayoutEditor_en.properties | 1 + .../components/form/BackgroundInput.vue | 108 +++++++++++++++--- 2 files changed, 90 insertions(+), 19 deletions(-) diff --git a/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties b/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties index 7b17afbc1..f3a3d243f 100644 --- a/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties +++ b/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties @@ -415,6 +415,7 @@ layout.gradientCornerTopLeft=Top Left layout.gradientCornerTopRight=Top Right layout.gradientCornerBottomRight=Bottom Right layout.gradientCornerBottomLeft=Bottom Left +layout.gradientRatio=Ratio layout.saveDraft=Save layout.saveDraftTooltip=Save page as draft layout.saveSiteDraftTooltip=Save site as draft diff --git a/layout-webapp/src/main/webapp/vue-app/common-layout/components/form/BackgroundInput.vue b/layout-webapp/src/main/webapp/vue-app/common-layout/components/form/BackgroundInput.vue index c84ca8f84..dbedc29a3 100644 --- a/layout-webapp/src/main/webapp/vue-app/common-layout/components/form/BackgroundInput.vue +++ b/layout-webapp/src/main/webapp/vue-app/common-layout/components/form/BackgroundInput.vue @@ -142,7 +142,7 @@ :label="$t('layout.scrollMiddleColor')" class="my-auto" />
-
+
+ + + {{ $t('layout.gradientRatio') }} + + + {{ gradientRatio }}/{{ 100 - gradientRatio }} + + +
+ {{ $t('layout.gradientFrom') }} + + {{ $t('layout.gradientTo') }} +
+ s.trim()); + this.backgroundGradientFrom = stops[0].split(' ')[0]; + if (stops.length === 3) { + this.gradientRatio = parseFloat(stops[1].split(' ')[1]); + this.backgroundGradientTo = stops[2].split(' ')[0]; + } else { + this.gradientRatio = 50; + this.backgroundGradientTo = stops[stops.length - 1].split(' ')[0]; + } } else if (this.container.backgroundEffect?.startsWith('conic-gradient(')) { this.choice = 'angular'; const inner = this.container.backgroundEffect.replace('conic-gradient(', '').replace(/\)$/, ''); const cornerMatch = inner.match(/at (top left|top right|bottom left|bottom right)/); this.gradientCorner = cornerMatch ? cornerMatch[1] : 'top left'; - const colors = inner.substring(inner.indexOf(',') + 1).split(','); - this.backgroundGradientFrom = colors[0].trim(); - this.backgroundGradientTo = colors[1].trim(); + const reversed = this.gradientCorner === 'top right' || this.gradientCorner === 'bottom left'; + const stops = inner.substring(inner.indexOf(',') + 1).split(',').map(s => s.trim()); + const firstColor = stops[0].split(' ')[0]; + const lastColor = stops[stops.length - 1].split(' ')[0]; + this.backgroundGradientFrom = reversed ? lastColor : firstColor; + this.backgroundGradientTo = reversed ? firstColor : lastColor; + if (stops.length === 3) { + const midPercent = Math.round(parseFloat(stops[1].split(' ')[1]) / 90 * 100); + this.gradientRatio = reversed ? (100 - midPercent) : midPercent; + } else { + this.gradientRatio = 50; + } } else if (this.container.backgroundEffect?.startsWith('linear-gradient(')) { this.choice = 'linear'; const inner = this.container.backgroundEffect.replace('linear-gradient(', '').replace(/\)$/, ''); - const parts = inner.split(','); - if (parts[0].trim().startsWith('to ')) { - this.gradientDirection = parts[0].trim(); - this.backgroundGradientFrom = parts[1].trim(); - this.backgroundGradientTo = parts[2].trim(); + const parts = inner.split(',').map(s => s.trim()); + let stops = parts; + if (parts[0].startsWith('to ')) { + this.gradientDirection = parts[0]; + stops = parts.slice(1); } else { this.gradientDirection = 'to bottom'; - this.backgroundGradientFrom = parts[0].trim(); - this.backgroundGradientTo = parts[1].trim(); + } + this.backgroundGradientFrom = stops[0].split(' ')[0]; + if (stops.length === 3) { + this.gradientRatio = parseFloat(stops[1].split(' ')[1]); + this.backgroundGradientTo = stops[2].split(' ')[0]; + } else { + this.gradientRatio = 50; + this.backgroundGradientTo = stops[stops.length - 1].split(' ')[0]; } } else { this.choice = 'color'; From ef0422085b312ee49a4ceed21d47b410997d138e Mon Sep 17 00:00:00 2001 From: srenault Date: Wed, 8 Jul 2026 15:57:52 +0200 Subject: [PATCH 04/24] Fix: Ensure transparency display of gradient value - EXO-88418 Two independent bugs prevented alpha transparency from actually showing on gradient backgrounds (page, section and application): - BackgroundInput.vue: switching the background type radio to a gradient (linear/radial/angular) triggered two watchers reacting to the same change. The backgroundColorChoice watcher correctly set the container's backgroundColor to the fully-transparent #FFFFFF00 marker, but the choice() watcher ran right after and unconditionally reset it back to the opaque defaultBackgroundColor. That opaque white then became the CSS backdrop painted behind the gradient's background-image, so any alpha in the From/To colors blended against solid white instead of true transparency. The reset is now scoped to the "color" choice only. - ColorPicker.vue: the From/To color swatches painted their color directly on the drawer's panel background, so a semi-transparent color (e.g. #FFFFFF41) looked identical to a fully opaque one, making it impossible to visually confirm transparency was applied. Added a checkerboard backdrop behind the swatch (in editor.less, since this module's webpack config has no loader for Vue SFC