Fix QR background gradient handling

This commit is contained in:
2026-08-05 19:51:54 +01:00
parent 9f831f04bc
commit 38d82d2ac6
5 changed files with 46 additions and 16 deletions
+6
View File
@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file.
## 2.5.7 - 2026-08-05
### Fixed
- QR template backgrounds now use a single solid color only, and the editor no longer exposes a background gradient mode.
## 2.5.6 - 2026-08-05
### Added
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "pulse-signage",
"version": "2.5.6",
"version": "2.5.7",
"private": false,
"description": "Pulse Signage application with MySQL and media storage",
"repository": {
+3 -12
View File
@@ -312,14 +312,6 @@ function buildQrStylingOptions(source) {
{ offset: 1, color: normalizeQrStyleColor(qrStyle.qr_corners_dot_gradient_color_2, '#ffffff') }
]
} : null;
const backgroundGradient = qrStyle.qr_background_color_mode === 'gradient' ? {
type: String(qrStyle.qr_background_gradient_type || 'linear').trim() || 'linear',
rotation: normalizeQrStyleNumber(qrStyle.qr_background_gradient_rotation, 0, undefined, undefined),
colorStops: [
{ offset: 0, color: normalizeQrStyleColor(qrStyle.qr_background_gradient_color_1, normalizeQrStyleColor(qrStyle.qr_background_color, '#ffffff')) },
{ offset: 1, color: normalizeQrStyleColor(qrStyle.qr_background_gradient_color_2, '#ffffff') }
]
} : null;
return {
width: QR_PNG_WIDTH,
@@ -344,8 +336,7 @@ function buildQrStylingOptions(source) {
gradient: cornersDotGradient || undefined
},
backgroundOptions: {
color: qrStyle.qr_background_transparent ? 'transparent' : normalizeQrStyleColor(qrStyle.qr_background_color, '#ffffff'),
gradient: backgroundGradient || undefined
color: qrStyle.qr_background_transparent ? 'transparent' : normalizeQrStyleColor(qrStyle.qr_background_color, '#ffffff')
},
image: String(qrStyle.qr_image || '').trim() || undefined,
imageOptions: {
@@ -491,7 +482,7 @@ async function buildQrCodeContent(value, options) {
qr_border_radius: Number.isFinite(Number(source.qr_border_radius)) ? Math.max(0, Math.round(Number(source.qr_border_radius))) : undefined,
qr_background_color: String(source.qr_background_color === undefined || source.qr_background_color === null ? '' : source.qr_background_color).trim() || undefined,
qr_background_transparent: hasBooleanField('qr_background_transparent') ? Boolean(source.qr_background_transparent) : undefined,
qr_background_color_mode: String(source.qr_background_color_mode === undefined || source.qr_background_color_mode === null ? '' : source.qr_background_color_mode).trim() || undefined,
qr_background_color_mode: 'single',
qr_background_gradient_type: String(source.qr_background_gradient_type === undefined || source.qr_background_gradient_type === null ? '' : source.qr_background_gradient_type).trim() || undefined,
qr_background_gradient_rotation: normalizeNumber(source.qr_background_gradient_rotation),
qr_background_gradient_color_1: normalizeHex(source.qr_background_gradient_color_1),
@@ -533,7 +524,7 @@ async function buildQrCodeContent(value, options) {
}
});
content.qr_background_use_gradient = content.qr_background_gradient_type && content.qr_background_gradient_type !== 'none';
content.qr_background_use_gradient = false;
content.qr_dots_use_gradient = content.qr_dots_gradient_type && content.qr_dots_gradient_type !== 'none';
content.qr_corners_square_use_gradient = content.qr_corners_square_gradient_type && content.qr_corners_square_gradient_type !== 'none';
content.qr_corners_dot_use_gradient = content.qr_corners_dot_gradient_type && content.qr_corners_dot_gradient_type !== 'none';
+9 -3
View File
@@ -251,6 +251,7 @@
qr_margin: normalizeMarginValue(source.qr_margin, defaultQrOptions.margin),
qr_background_color: normalizeHexColorValue(source.qr_background_color, '#ffffff'),
qr_background_transparent: normalizeBooleanValue(source.qr_background_transparent, false),
qr_background_color_mode: 'single',
qr_background_gradient_type: normalizeGradientTypeValue(source.qr_background_gradient_type, 'linear'),
qr_background_gradient_rotation: normalizeGradientRotationValue(source.qr_background_gradient_rotation, 0),
qr_background_gradient_color_1: normalizeHexColorValue(source.qr_background_gradient_color_1, '#ffffff'),
@@ -729,7 +730,6 @@
var dotsGradient = style.qr_dots_color_mode === 'gradient' ? buildGradientOptions('qr_dots', style) : null;
var cornersSquareGradient = style.qr_corners_square_color_mode === 'gradient' ? buildGradientOptions('qr_corners_square', style) : null;
var cornersDotGradient = style.qr_corners_dot_color_mode === 'gradient' ? buildGradientOptions('qr_corners_dot', style) : null;
var backgroundGradient = style.qr_background_color_mode === 'gradient' ? buildGradientOptions('qr_background', style) : null;
return {
width: defaultQrOptions.width,
height: defaultQrOptions.height,
@@ -753,8 +753,7 @@
gradient: cornersDotGradient || undefined
},
backgroundOptions: {
color: style.qr_background_transparent ? 'transparent' : style.qr_background_color,
gradient: backgroundGradient || undefined
color: style.qr_background_transparent ? 'transparent' : style.qr_background_color
},
image: style.qr_image || undefined,
imageOptions: {
@@ -818,6 +817,13 @@
function renderColorModeSection(accordionId, sectionKey, prefix, regionId, label, style, singleControlsHtml, gradientLabel, fallbackColor, currentOpen, styleSelectHtml) {
var mode = normalizeColorModeValue(style[prefix + '_color_mode'], 'single');
if (prefix === 'qr_background') {
return renderAccordionSection(accordionId, sectionKey, label, '' +
'<div class="d-grid gap-3" data-color-mode-accordion-body>' +
'<div data-single-color-config>' + singleControlsHtml + '</div>' +
styleSelectHtml +
'</div>', currentOpen);
}
var colorModeSelect = '' +
'<div>' +
'<label class="form-label mb-1" for="' + prefix + '_color_mode_' + regionId + '">Colour mode</label>' +
+27
View File
@@ -18,6 +18,33 @@ test('createStyledQrCodeSvg renders a styled qr svg for non-empty values', async
assert.match(svg, /<svg[\s>]/);
});
test('createStyledQrCodeSvg keeps the qr background solid when gradient fields are provided', async () => {
const svg = await createStyledQrCodeSvg({
value: 'https://example.com',
qr_background_color_mode: 'gradient',
qr_background_gradient_type: 'radial',
qr_background_gradient_color_1: '#ff0000',
qr_background_gradient_color_2: '#0000ff'
});
assert.equal(typeof svg, 'string');
assert.match(svg, /<svg[\s>]/);
assert.doesNotMatch(svg, /<linearGradient|<radialGradient/i);
});
test('buildQrCodeContent forces qr background to single colour mode', async () => {
const content = await buildQrCodeContent({
value: 'https://example.com',
qr_background_color_mode: 'gradient',
qr_background_gradient_type: 'radial',
qr_background_gradient_color_1: '#ff0000',
qr_background_gradient_color_2: '#0000ff'
});
assert.equal(content.qr_background_color_mode, 'single');
assert.equal(content.qr_background_use_gradient, false);
});
test('createStyledQrCodeDataUrl renders a styled qr data url for non-empty values', async () => {
const dataUrl = await createStyledQrCodeDataUrl('https://example.com');