mirror of
https://github.com/unraid/api.git
synced 2026-02-17 13:38:29 -06:00
fix: re-add missing header gradient styles (#1787)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Style** * Enhanced header banner styling: centered, non-repeating cover images with layered gradient overlays and adjusted user-profile banner positioning for improved layout. * **Bug Fixes** * Banner display logic updated so "image" is treated like "yes" for showing banner images. * **Tests** * Added unit tests covering banner/theme display behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -3,6 +3,7 @@ import { Test, TestingModule } from '@nestjs/testing';
|
|||||||
import * as fs from 'fs/promises';
|
import * as fs from 'fs/promises';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import type { Mock } from 'vitest';
|
||||||
import { plainToInstance } from 'class-transformer';
|
import { plainToInstance } from 'class-transformer';
|
||||||
import * as ini from 'ini';
|
import * as ini from 'ini';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
@@ -1182,4 +1183,58 @@ describe('CustomizationService - updateCfgFile', () => {
|
|||||||
writeError
|
writeError
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getTheme', () => {
|
||||||
|
const mockDynamix = getters.dynamix as unknown as Mock;
|
||||||
|
const baseDisplay = {
|
||||||
|
theme: 'white',
|
||||||
|
banner: '',
|
||||||
|
showBannerGradient: 'no',
|
||||||
|
background: '123456',
|
||||||
|
headerdescription: 'yes',
|
||||||
|
headermetacolor: '789abc',
|
||||||
|
header: 'abcdef',
|
||||||
|
};
|
||||||
|
|
||||||
|
const setDisplay = (overrides: Partial<typeof baseDisplay>) => {
|
||||||
|
mockDynamix.mockReturnValue({
|
||||||
|
display: {
|
||||||
|
...baseDisplay,
|
||||||
|
...overrides,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
it('reports showBannerImage when banner is "image"', async () => {
|
||||||
|
setDisplay({ banner: 'image' });
|
||||||
|
|
||||||
|
const theme = await service.getTheme();
|
||||||
|
|
||||||
|
expect(theme.showBannerImage).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports showBannerImage when banner is "yes"', async () => {
|
||||||
|
setDisplay({ banner: 'yes' });
|
||||||
|
|
||||||
|
const theme = await service.getTheme();
|
||||||
|
|
||||||
|
expect(theme.showBannerImage).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('disables showBannerImage when banner is empty', async () => {
|
||||||
|
setDisplay({ banner: '' });
|
||||||
|
|
||||||
|
const theme = await service.getTheme();
|
||||||
|
|
||||||
|
expect(theme.showBannerImage).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('mirrors showBannerGradient flag from display settings', async () => {
|
||||||
|
setDisplay({ banner: 'image', showBannerGradient: 'yes' });
|
||||||
|
expect((await service.getTheme()).showBannerGradient).toBe(true);
|
||||||
|
|
||||||
|
setDisplay({ banner: 'image', showBannerGradient: 'no' });
|
||||||
|
expect((await service.getTheme()).showBannerGradient).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ export class CustomizationService implements OnModuleInit {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
showBannerImage: banner === 'yes',
|
showBannerImage: banner === 'image' || banner === 'yes',
|
||||||
showBannerGradient: bannerGradient === 'yes',
|
showBannerGradient: bannerGradient === 'yes',
|
||||||
headerBackgroundColor: this.addHashtoHexField(bgColor),
|
headerBackgroundColor: this.addHashtoHexField(bgColor),
|
||||||
headerPrimaryTextColor: this.addHashtoHexField(textColor),
|
headerPrimaryTextColor: this.addHashtoHexField(textColor),
|
||||||
|
|||||||
@@ -156,3 +156,42 @@ iframe#progressFrame {
|
|||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Header banner compatibility tweaks */
|
||||||
|
#header.image {
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-banner-gradient #header.image {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-banner-gradient #header.image::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: left center, right center;
|
||||||
|
background-size: min(30%, 320px) 100%, min(30%, 320px) 100%;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(
|
||||||
|
90deg,
|
||||||
|
var(--color-header-gradient-end, rgba(0, 0, 0, 0.7)) 0%,
|
||||||
|
var(--color-header-gradient-start, rgba(0, 0, 0, 0)) 100%
|
||||||
|
),
|
||||||
|
linear-gradient(
|
||||||
|
270deg,
|
||||||
|
var(--color-header-gradient-end, rgba(0, 0, 0, 0.7)) 0%,
|
||||||
|
var(--color-header-gradient-start, rgba(0, 0, 0, 0)) 100%
|
||||||
|
);
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-banner-gradient #header.image > * {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -94,11 +94,11 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="UserProfile"
|
id="UserProfile"
|
||||||
class="text-foreground absolute top-0 right-0 z-20 flex h-full flex-col gap-y-1 pt-2 pr-2"
|
class="text-foreground absolute top-0 right-0 z-20 flex h-full max-w-full flex-col items-end gap-y-1 pt-2 pr-2"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="bannerGradient"
|
v-if="bannerGradient"
|
||||||
class="absolute top-0 right-0 bottom-0 z-0 w-full"
|
class="pointer-events-none absolute inset-y-0 right-0 left-0 z-0 w-full"
|
||||||
:style="bannerGradient"
|
:style="bannerGradient"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ export const useThemeStore = defineStore(
|
|||||||
: 'var(--header-gradient-end)';
|
: 'var(--header-gradient-end)';
|
||||||
|
|
||||||
dynamicVars['--banner-gradient'] = `linear-gradient(90deg, ${start} 0, ${end} 90%)`;
|
dynamicVars['--banner-gradient'] = `linear-gradient(90deg, ${start} 0, ${end} 90%)`;
|
||||||
|
customClasses.push('has-banner-gradient');
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
@@ -222,7 +223,13 @@ export const useThemeStore = defineStore(
|
|||||||
const cleanClassList = (classList: string) =>
|
const cleanClassList = (classList: string) =>
|
||||||
classList
|
classList
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.filter((c) => !c.startsWith('theme-') && c !== 'dark' && !c.startsWith('has-custom-'))
|
.filter(
|
||||||
|
(c) =>
|
||||||
|
!c.startsWith('theme-') &&
|
||||||
|
c !== 'dark' &&
|
||||||
|
!c.startsWith('has-custom-') &&
|
||||||
|
c !== 'has-banner-gradient'
|
||||||
|
)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ');
|
.join(' ');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user