mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 04:16:34 +00:00
Dashboard: Replace automatic banner rotation with random initial display and manual arrows
Replace the automatic timed banner rotation with a random initial banner selection and small, manually operated arrow buttons for navigation. Previous behavior can be restored by setting BannerRotation.automaticRotationEnabled to true. The original slides used position:absolute + opacity for transitions. This was changed to display:none/block because absolutely positioned children don't contribute to their parent container's dimensions, causing the banner containers to collapse to zero width/height. The arrow buttons (position:absolute, top:50%) need a parent with real dimensions to center correctly. With display:none/block, the active slide determines the container's natural size. The fixed height:60px on .gold-banner was removed for the same reason — it clipped banner images that are taller than 60px. An explicit height was added to .header-banner to ensure the arrow buttons can vertically center via top:50% within the platinum banner area.
This commit is contained in:
1 parent
a0d2ed99fb
commit
a35ba3a597
3 files changed
+118
-19
No files matched your search
@@ -207,6 +207,7 @@ body {
|
||||
top: 0;
|
||||
left: 0;
|
||||
order: 2;
|
||||
height: var(--header-height);
|
||||
max-height: var(--header-height);
|
||||
}
|
||||
|
||||
@@ -311,17 +312,13 @@ body {
|
||||
}
|
||||
|
||||
.platinum-banner-slide {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
pointer-events: none;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.platinum-banner-slide.active {
|
||||
opacity: 1;
|
||||
display: block;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@@ -351,19 +348,16 @@ body {
|
||||
.gold-banner {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gold-banner-slide {
|
||||
position: absolute;
|
||||
display: none;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.gold-banner-slide.active {
|
||||
opacity: 1;
|
||||
display: block;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@@ -372,6 +366,43 @@ body {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Banner Arrow Navigation */
|
||||
.banner-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 10;
|
||||
background: rgba(128, 128, 128, 0.15);
|
||||
color: var(--text-muted);
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
width: 24px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
opacity: 0.3;
|
||||
transition: opacity 0.2s ease, background-color 0.2s ease, color 0.2s ease;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.banner-arrow:hover {
|
||||
opacity: 0.8;
|
||||
background: rgba(128, 128, 128, 0.4);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.banner-arrow-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.banner-arrow-right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.page-view {
|
||||
/*max-width: 1600px;*/
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -52,8 +52,17 @@ function updateThemeAwareImage($img, theme=null) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages banner loading, display, and navigation.
|
||||
*
|
||||
* When automaticRotationEnabled is true, banners rotate on a timer and arrow
|
||||
* buttons are hidden. When false (the current default), a random initial
|
||||
* banner is shown and the user navigates manually via arrow buttons.
|
||||
*/
|
||||
class BannerRotation {
|
||||
constructor() {
|
||||
this.automaticRotationEnabled = false;
|
||||
|
||||
this.platinumIndex = 0;
|
||||
this.goldIndex = 0;
|
||||
this.platinumTimer = null;
|
||||
@@ -67,8 +76,18 @@ class BannerRotation {
|
||||
init() {
|
||||
let self = this;
|
||||
this.loadBanners(function() {
|
||||
self.startPlatinumRotation();
|
||||
self.startGoldRotation();
|
||||
self.randomizeInitialBanner('platinum');
|
||||
self.randomizeInitialBanner('gold');
|
||||
|
||||
if (self.automaticRotationEnabled) {
|
||||
self.startPlatinumRotation();
|
||||
self.startGoldRotation();
|
||||
// Hide arrows entirely when rotation is automatic
|
||||
$('.banner-arrow').hide();
|
||||
} else {
|
||||
self.hideArrowsIfSingle();
|
||||
self.bindArrowButtons();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,6 +138,23 @@ class BannerRotation {
|
||||
}, this.platinumInterval);
|
||||
}
|
||||
|
||||
randomizeInitialBanner(type) {
|
||||
const slideClass = type === 'platinum' ? '.platinum-banner-slide' : '.gold-banner-slide';
|
||||
const $slides = $(slideClass);
|
||||
const total = $slides.length;
|
||||
|
||||
if (total === 0) return;
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * total);
|
||||
if (type === 'platinum') {
|
||||
this.platinumIndex = randomIndex;
|
||||
} else {
|
||||
this.goldIndex = randomIndex;
|
||||
}
|
||||
$slides.removeClass('active');
|
||||
$slides.eq(randomIndex).addClass('active');
|
||||
}
|
||||
|
||||
startGoldRotation() {
|
||||
const self = this;
|
||||
this.goldTimer = setInterval(() => {
|
||||
@@ -126,6 +162,30 @@ class BannerRotation {
|
||||
}, this.goldInterval);
|
||||
}
|
||||
|
||||
hideArrowsIfSingle() {
|
||||
if ($('.platinum-banner-slide').length <= 1) {
|
||||
$('#platinum-banners .banner-arrow').hide();
|
||||
}
|
||||
if ($('.gold-banner-slide').length <= 1) {
|
||||
$('#gold-banners .banner-arrow').hide();
|
||||
}
|
||||
}
|
||||
|
||||
bindArrowButtons() {
|
||||
let self = this;
|
||||
$('.banner-arrow').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const target = $(this).data('target');
|
||||
const direction = $(this).hasClass('banner-arrow-right') ? 'next' : 'prev';
|
||||
if (target === 'platinum') {
|
||||
self.rotatePlatinum(direction);
|
||||
} else {
|
||||
self.rotateGold(direction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
rotatePlatinum(direction) {
|
||||
const $slides = $('.platinum-banner-slide');
|
||||
const total = $slides.length;
|
||||
@@ -145,9 +205,11 @@ class BannerRotation {
|
||||
// Add active class to new slide
|
||||
$slides.eq(this.platinumIndex).addClass('active');
|
||||
|
||||
// Reset timer
|
||||
clearInterval(this.platinumTimer);
|
||||
this.startPlatinumRotation();
|
||||
// Reset timer when in automatic rotation mode
|
||||
if (this.automaticRotationEnabled) {
|
||||
clearInterval(this.platinumTimer);
|
||||
this.startPlatinumRotation();
|
||||
}
|
||||
}
|
||||
|
||||
rotateGold(direction) {
|
||||
@@ -169,9 +231,11 @@ class BannerRotation {
|
||||
// Add active class to new group
|
||||
$groups.eq(this.goldIndex).addClass('active');
|
||||
|
||||
// Reset timer
|
||||
clearInterval(this.goldTimer);
|
||||
this.startGoldRotation();
|
||||
// Reset timer when in automatic rotation mode
|
||||
if (this.automaticRotationEnabled) {
|
||||
clearInterval(this.goldTimer);
|
||||
this.startGoldRotation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
<!-- Platinum Banners in Header -->
|
||||
<div id="platinum-banners" class="header-banner">
|
||||
<button class="banner-arrow banner-arrow-left" data-target="platinum" aria-label="Previous banner">‹</button>
|
||||
<button class="banner-arrow banner-arrow-right" data-target="platinum" aria-label="Next banner">›</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -147,6 +149,8 @@
|
||||
|
||||
<!-- Gold Banners -->
|
||||
<section id="gold-banners" class="gold-banners-section">
|
||||
<button class="banner-arrow banner-arrow-left" data-target="gold" aria-label="Previous banner">‹</button>
|
||||
<button class="banner-arrow banner-arrow-right" data-target="gold" aria-label="Next banner">›</button>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user