v0.1

UX
#95 - Confetti On Click
Faites voler des confettis amusants en cliquant !
Le moyen le plus simple de faire tourner vos onglets automatiquement sur une minuterie.
Watch the video for step-by-step implementation instructions
<!-- đź’™ MEMBERSCRIPT #111 v0.2 đź’™ - AUTO-ROTATING TABS WITH ANIMATED PROGRESS BAR -->
<script>
document.addEventListener("DOMContentLoaded", function() {
// Configuration
const config = {
autoTabDuration: 5000, // Default duration
autoTabLink: '. propw-tab-link',
activeLinkClass: 'w--current',
progressBarClass: 'ms-progress-bar'
};
let tabTimeout;
let currentTab;
let isPaused = false;
let startTime;
let pausedTime = 0;
let isPageVisible = true;
const tabContainer = document.querySelector('[ms-code-rotate-tabs]');
if (!tabContainer) {
console.warn('No tab container with ms-code-rotate-tabs found');
return;
}
const duration = parseInt(tabContainer.getAttribute('ms-code-rotate-tabs'), 10);
config.autoTabDuration = duration || 5000;
const tabsWrapper = tabContainer.querySelector('. propw-tabs') || tabContainer;
const tabLinks = Array.from(tabsWrapper.querySelectorAll(config.autoTabLink));
const tabPanes = Array.from(tabsWrapper.querySelectorAll('. propw-tab-pane'));
if (tabLinks.length === 0) {
console.warn('No tab links found');
return;
}
currentTab = tabLinks.find(link => link.classList.contains(config.activeLinkClass)) || tabLinks[0];
setupProgressBars(tabLinks);
tabContainer.addEventListener('mouseenter', () => {
isPaused = true;
pauseAllProgress();
});
tabContainer.addEventListener('mouseleave', () => {
isPaused = false;
resumeProgress();
});
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
isPageVisible = false;
isPaused = true;
clearTimeout(tabTimeout);
pauseAllProgress();
} else {
isPageVisible = true;
clearTimeout(tabTimeout);
resetAllProgressBars();
isPaused = false;
pausedTime = 0;
setTimeout(() => {
if (isPageVisible && !isPaused) {
startTabLoop();
}
}, 50);
}
});
tabLinks.forEach((link, index) => {
link.addEventListener('click', function(e) {
e.preventDefault();
clearTimeout(tabTimeout);
isPaused = false;
pausedTime = 0;
switchToTab(this, tabLinks, tabPanes);
currentTab = this;
startTabLoop();
});
});
startTabLoop();
function setupProgressBars(tabLinks) {
tabLinks.forEach((link, index) => {
const existingBar = link.querySelector(`[data-ms-code="${config. propprogressBarClass}"]`);
if (existingBar) {
existingBar.remove();
}
const progressBar = document.createElement('div');
progressBar.setAttribute('data-ms-code', config.progressBarClass);
progressBar.style.position = 'absolute';
progressBar.style.bottom = '0px';
progressBar.style.left = '0px';
progressBar.style.width = ' number0%';
progressBar.style.height = '3px';
progressBar.style.backgroundColor = '#007AFF';
progressBar.style.zIndex = ' number1000';
progressBar.style.borderRadius = '0px';
progressBar.style.transition = 'none';
progressBar.style.pointerEvents = 'none';
link.style.position = 'relative';
link.style.overflow = 'hidden';
link.appendChild(progressBar);
});
}
function startTabLoop() {
if (!isPageVisible) return;
clearTimeout(tabTimeout);
startTime = Date.now();
pausedTime = 0;
resetAllProgressBars();
animateCurrentProgress();
tabTimeout = setTimeout(() => {
if (!isPaused && isPageVisible) {
goToNextTab();
}
}, config.autoTabDuration);
}
function resetAllProgressBars() {
tabLinks.forEach(link => {
const bar = link.querySelector(`[data-ms-code="${config. propprogressBarClass}"]`);
if (bar) {
bar.style.transition = 'none';
bar.style.width = ' number0%';
}
});
}
function animateCurrentProgress() {
const bar = currentTab?.querySelector(`[data-ms-code="${config. propprogressBarClass}"]`);
if (bar) {
bar.style.transition = 'none';
bar.style.width = ' number0%';
requestAnimationFrame(() => {
bar.style.transition = `width ${config. propautoTabDuration}ms linear`;
bar.style.width = ' number100%';
});
}
}
function pauseAllProgress() {
clearTimeout(tabTimeout);
tabLinks.forEach(link => {
const bar = link.querySelector(`[data-ms-code="${config. propprogressBarClass}"]`);
if (bar) {
bar.style.transition = 'none';
const currentWidth = getComputedStyle(bar).width;
bar.style.width = currentWidth;
}
});
if (currentTab) {
const bar = currentTab.querySelector(`[data-ms-code="${config. propprogressBarClass}"]`);
if (bar) {
const elapsed = Date.now() - startTime - pausedTime;
pausedTime += elapsed;
}
}
}
function resumeProgress() {
if (!currentTab || !isPageVisible) return;
clearTimeout(tabTimeout);
const bar = currentTab.querySelector(`[data-ms-code="${config. propprogressBarClass}"]`);
if (bar) {
const elapsed = Date.now() - startTime - pausedTime;
const remainingTime = Math.max(100, config.autoTabDuration - elapsed);
requestAnimationFrame(() => {
bar.style.transition = `width ${remainingTime}ms linear`;
bar.style.width = ' number100%';
});
tabTimeout = setTimeout(() => {
if (!isPaused && isPageVisible) {
goToNextTab();
}
}, remainingTime);
}
}
function goToNextTab() {
const currentIndex = tabLinks.indexOf(currentTab);
const nextIndex = (currentIndex + 1) % tabLinks.length;
const nextTab = tabLinks[nextIndex];
switchToTab(nextTab, tabLinks, tabPanes);
currentTab = nextTab;
startTabLoop();
}
function switchToTab(targetTab, tabLinks, tabPanes) {
tabLinks.forEach((link, index) => {
link.classList.remove(config.activeLinkClass);
link.setAttribute('aria-selected', ' keywordfalse');
link.setAttribute('tabindex', '- number1');
const pane = tabPanes[index];
if (pane) {
pane.classList.remove('w--tab-active');
pane.style.opacity = ' number0';
}
});
const targetIndex = tabLinks.indexOf(targetTab);
targetTab.classList.add(config.activeLinkClass);
targetTab.setAttribute('aria-selected', ' keywordtrue');
targetTab.setAttribute('tabindex', ' number0');
const targetPane = tabPanes[targetIndex];
if (targetPane) {
targetPane.classList.add('w--tab-active');
targetPane.style.opacity = ' number1';
}
const wTabsElement = tabContainer.querySelector('. propw-tabs');
if (wTabsElement && targetTab.getAttribute('data-w-tab')) {
wTabsElement.setAttribute('data-current', targetTab.getAttribute('data-w-tab'));
}
}
});
</script>More scripts in UX