50) hasScrolledDown = true;
// Calcolo Direzione per Utility Classes
if (currentY <= 0) {
isScrollingDown = false;
} else if (currentY > lastY) {
isScrollingDown = true;
} else if (currentY < lastY) {
isScrollingDown = false;
}
// Aggiorna visibilità componenti interni
if (typeof updateVisibilityClasses === 'function') updateVisibilityClasses();
if (typeof updateAutoHeading === 'function') updateAutoHeading();
if (typeof updateUrlSync === 'function') updateUrlSync();
// --- UNIVERSAL ANCHOR LOGIC ---
forceShow = false;
forceHide = false;
const viewportRef = placement === 'bottom' ? currentY + window.innerHeight : currentY;
const syncSuffix = syncId ? '-' + syncId : '';
const slideUpElements = document.querySelectorAll('.sb-slide-up' + syncSuffix);
const slideDownElements = document.querySelectorAll('.sb-slide-down' + syncSuffix);
const allAnchors = [...slideUpElements, ...slideDownElements];
if (allAnchors.length > 0) {
let maxTop = -Infinity;
let foundType = null;
allAnchors.forEach(el => {
const rect = el.getBoundingClientRect();
const top = rect.top + currentY;
if (top <= viewportRef) {
if (top > maxTop) {
maxTop = top;
foundType = el.classList.contains('sb-slide-up' + syncSuffix) ? 'up' : 'down';
}
}
});
if (foundType === 'up') forceHide = true;
if (foundType === 'down') forceShow = true;
}
// --- STANDARD LOGIC ---
// Animation Unlock Logic
if (animationLock && currentY > startHiddenOffset) {
isInitiallyHidden = false;
animationLock = false;
hidden = false;
scrollDelta = 0;
// Qui aggiorniamo lastY solo per il reset del lock
lastY = currentY;
}
// Sticky & Sticky By Anchor Logic
if (positionType === 'sticky' || positionType === 'stickyByAnchor') {
const willBeStuck = currentY > originalInitialOffset && currentY > 0;
/* Pre-apply body paddingTop BEFORE Alpine switches :class to 'fixed'.
Without this, there's a one-frame gap where the bar is fixed but body
has no reserved space → content below jumps up and then back down. */
if (!overlayContent && !hasNoPaddingTop && willBeStuck !== stuck) {
document.body.style.transition = 'none';
if (willBeStuck) {
document.body.style.paddingTop = $el.offsetHeight + 'px';
} else {
document.body.style.paddingTop = '0px';
}
}
stuck = willBeStuck;
if (startHidden && currentY <= 1) stuck = false;
// Normal Sticky Logic
if (positionType === 'sticky') {
if (forceHide) {
hidden = true;
scrollDelta = 0;
} else if (forceShow) {
hidden = false;
scrollDelta = 0;
} else {
if (!stuck) {
hidden = false;
scrollDelta = 0;
} else if (yesAnimation && !animationLock) {
// CALCOLO DELTA (Ora funzionerà perché lastY è ancora il valore precedente)
const delta = currentY - lastY;
if ((delta > 0 && scrollDelta < 0) || (delta < 0 && scrollDelta > 0)) scrollDelta = 0;
scrollDelta += delta;
if (!hidden && scrollDelta >= scrollDownOffset) {
hidden = true;
scrollDelta = 0;
} else if (hidden && scrollDelta <= -scrollUpOffset && !isInitiallyHidden) {
hidden = false;
scrollDelta = 0;
}
} else {
hidden = false;
}
}
}
if ((positionType === 'stickyByAnchor' && stuck) || positionType === 'fixed') {
const delta = currentY - lastY;
if (delta !== 0) scrollDirection = delta > 0 ? 'down' : 'up';
const normalElements = document.querySelectorAll('.sb-normal-colors' + syncSuffix);
const stuckElements = document.querySelectorAll('.sb-stuck-colors' + syncSuffix);
const customElements = document.querySelectorAll('.sb-custom-colors' + syncSuffix);
const colorElements = [...normalElements, ...stuckElements, ...customElements];
const sortedColorElements = colorElements.sort((a, b) => (a.getBoundingClientRect().top + currentY) - (b.getBoundingClientRect().top + currentY));
let activeColorElement = null;
let activeColorType = null;
const getColorType = (el) => {
if (el.classList.contains('sb-normal-colors' + syncSuffix)) return 'normal';
if (el.classList.contains('sb-custom-colors' + syncSuffix)) return 'custom';
return 'stuck';
};
if (scrollDirection === 'down') {
let maxTop = -Infinity;
colorElements.forEach(el => {
if (el.offsetParent === null) return;
const top = el.getBoundingClientRect().top + currentY;
if (top <= viewportRef && top > maxTop) {
maxTop = top;
activeColorElement = el;
activeColorType = getColorType(el);
}
});
} else if (scrollDirection === 'up') {
let targetElement = null;
let targetBottom = Infinity;
colorElements.forEach(el => {
if (el.offsetParent === null) return;
const bottom = el.getBoundingClientRect().bottom + currentY;
if (bottom >= viewportRef && bottom < targetBottom) {
targetBottom = bottom;
targetElement = el;
}
});
if (targetElement) {
const targetIndex = sortedColorElements.indexOf(targetElement);
if (targetIndex > 0) {
activeColorElement = sortedColorElements[targetIndex - 1];
activeColorType = getColorType(activeColorElement);
} else {
activeColorType = 'stuck';
}
}
}
if ((scrollDirection === 'down' && activeColorElement) || (scrollDirection === 'up' && activeColorType !== null)) {
useStuckColors = (activeColorType === 'stuck');
useCustomColors = (activeColorType === 'custom');
}
const slideElements = allAnchors;
const sortedSlideElements = slideElements.sort((a, b) => (a.getBoundingClientRect().top + currentY) - (b.getBoundingClientRect().top + currentY));
let activeSlideElement = null;
let activeSlideType = null;
if (scrollDirection === 'down') {
let maxTop = -Infinity;
slideElements.forEach(el => {
const top = el.getBoundingClientRect().top + currentY;
if (top <= viewportRef && top > maxTop) {
maxTop = top;
activeSlideElement = el;
activeSlideType = el.classList.contains('sb-slide-up' + syncSuffix) ? 'up' : 'down';
}
});
} else if (scrollDirection === 'up') {
let targetElement = null;
let targetBottom = Infinity;
slideElements.forEach(el => {
const bottom = el.getBoundingClientRect().bottom + currentY;
if (bottom >= viewportRef && bottom < targetBottom) {
targetBottom = bottom;
targetElement = el;
}
});
if (targetElement) {
const targetIndex = sortedSlideElements.indexOf(targetElement);
if (targetIndex > 0) {
activeSlideElement = sortedSlideElements[targetIndex - 1];
activeSlideType = activeSlideElement.classList.contains('sb-slide-up' + syncSuffix) ? 'up' : 'down';
if (startHidden && targetIndex === 1) activeSlideType = 'up';
} else {
activeSlideType = 'down';
if (startHidden) activeSlideType = 'up';
}
} else {
activeSlideType = null;
}
}
if (scrollDirection === 'down' && activeSlideElement) hiddenByClass = (activeSlideType === 'up');
else if (scrollDirection === 'up' && activeSlideType !== null) hiddenByClass = (activeSlideType === 'up');
if (startHidden && currentY <= 5) hiddenByClass = true;
const wFull = document.querySelectorAll('.sb-width-full' + syncSuffix);
const wCont = document.querySelectorAll('.sb-width-container' + syncSuffix);
const wInhDef = document.querySelectorAll('.sb-width-inherit-default' + syncSuffix);
const wInhStk = document.querySelectorAll('.sb-width-inherit-stuck' + syncSuffix);
const wInhCust = document.querySelectorAll('.sb-width-inherit-custom' + syncSuffix);
const wCustom = document.querySelectorAll('.sb-width-custom' + syncSuffix);
const wEls = [...wFull, ...wCont, ...wInhDef, ...wInhStk, ...wInhCust, ...wCustom];
const sortedWidthElements = wEls.sort((a, b) => (a.getBoundingClientRect().top + currentY) - (b.getBoundingClientRect().top + currentY));
let activeWidthElement = null;
let activeWidthType = null;
if (scrollDirection === 'down') {
let maxTop = -Infinity;
wEls.forEach(el => {
if (el.offsetParent === null) return;
const top = el.getBoundingClientRect().top + currentY;
if (top <= viewportRef && top > maxTop) {
maxTop = top;
activeWidthElement = el;
}
});
} else if (scrollDirection === 'up') {
let targetElement = null;
let targetBottom = Infinity;
wEls.forEach(el => {
if (el.offsetParent === null) return;
const bottom = el.getBoundingClientRect().bottom + currentY;
if (bottom >= viewportRef && bottom < targetBottom) {
targetBottom = bottom;
targetElement = el;
}
});
if (targetElement) {
const targetIndex = sortedWidthElements.indexOf(targetElement);
if (targetIndex > 0) {
let prevIndex = targetIndex - 1;
while (prevIndex >= 0) {
if (sortedWidthElements[prevIndex].offsetParent !== null) {
activeWidthElement = sortedWidthElements[prevIndex];
break;
}
prevIndex--;
}
if (prevIndex < 0) activeWidthType = null;
} else {
activeWidthType = null;
}
} else {
let maxTop = -Infinity;
wEls.forEach(el => {
if (el.offsetParent === null) return;
const top = el.getBoundingClientRect().top + currentY;
if (top <= viewportRef && top > maxTop) {
maxTop = top;
activeWidthElement = el;
}
});
}
}
if (activeWidthElement) {
if (activeWidthElement.classList.contains('sb-width-full' + syncSuffix)) activeWidthType = 'full';
else if (activeWidthElement.classList.contains('sb-width-container' + syncSuffix)) activeWidthType = 'container';
else if (activeWidthElement.classList.contains('sb-width-inherit-default' + syncSuffix)) activeWidthType = 'inheritDefault';
else if (activeWidthElement.classList.contains('sb-width-inherit-stuck' + syncSuffix)) activeWidthType = 'inheritStuck';
else if (activeWidthElement.classList.contains('sb-width-inherit-custom' + syncSuffix)) activeWidthType = 'inheritCustom';
}
if (activeWidthType) {
anchorWidth = activeWidthType;
} else {
anchorWidth = null;
}
}
if (positionType === 'stickyByAnchor' && startHidden && currentY <= 5) hiddenByClass = true;
} else if (positionType === 'fixed') {
// Fixed Logic WITH OVERRIDE
if (forceHide) {
hidden = true;
scrollDelta = 0;
} else if (forceShow) {
hidden = false;
scrollDelta = 0;
} else {
// Standard Fixed Logic
if (!yesAnimation) {
hidden = false;
scrollDelta = 0;
} else if (!animationLock) {
// CALCOLO DELTA (Anche qui ora funzionerà)
const delta = currentY - lastY;
if ((delta > 0 && scrollDelta < 0) || (delta < 0 && scrollDelta > 0)) scrollDelta = 0;
scrollDelta += delta;
if (!hidden && scrollDelta >= scrollDownOffset) {
hidden = true;
scrollDelta = 0;
} else if (hidden && scrollDelta <= -scrollUpOffset && !isInitiallyHidden) {
hidden = false;
scrollDelta = 0;
}
}
}
}
// IMPORTANTE: Aggiornamento lastY solo alla FINE di tutto
lastY = currentY;
" data-sync-id="" data-placement="top" data-position-type="stickyByAnchor" :class="[
isScrollingDown ? 'sb-scroll-down' : '',
(positionType === 'fixed' || ((positionType === 'sticky' || positionType === 'stickyByAnchor') && (stuck || overlayContent)))
? 'fixed inset-x-0'
: 'relative inset-x-0',
0 && 1 && ('stickyByAnchor' === 'sticky' || 'stickyByAnchor' === 'stickyByAnchor')
? 'opacity-0 ' + ('top' === 'bottom' ? 'translate-y-[100vh]' : 'translate-y-[-100vh]')
: ''
]" class="smartbar flex transform transition-transform transition-opacity will-change-transform 49 w-full pointer-events-none flex [--sb-auto-heading-offset:120px] " :style="
'transition-property: transform, opacity' + (animationType === 'fold' ? ', transform-origin' : '') + (!removeTransitionOffset ? (placement === 'bottom' ? ', bottom' : ', top') : '') + ';' +
'transition-duration: ' + transitionDuration + 'ms;' +
'transition-timing-function: ' + transitionFunction + ';' +
'transition-delay: ' + transitionDelay + 'ms;' +
((positionType === 'fixed' || ((positionType === 'sticky' || positionType === 'stickyByAnchor') && stuck)) &&
(stuck ? (anchorWidth === 'full' ? '' : anchorWidth === 'container' ? 'container' : anchorWidth === 'NewCustom' ? '' : containerWidthStuck) : containerWidth) !== 'container' &&
(stuck ? (anchorWidth === 'full' ? '' : anchorWidth === 'container' ? 'container' : anchorWidth === 'NewCustom' ? '' : containerWidthStuck) : containerWidth) !== ''
? 'left:' + parentLeft + 'px; width:' + parentWidth + 'px;'
: ''
) +
/* top/bottom offset applies only when the bar is actually fixed (stuck or overlay,
or always-fixed positionType). In relative flow (Default state below a Hero),
applying 'top' would visually act as a margin-top — not semantically correct. */
((positionType === 'fixed' || ((positionType === 'sticky' || positionType === 'stickyByAnchor') && (stuck || overlayContent)))
? (placement === 'bottom'
? 'bottom:var(--sb-offset);'
: 'top:' + (useCustomColors ? 'var(--sb-offset-custom)' : (stuck && !shouldDisableStuckStyles && (positionType !== 'stickyByAnchor' || useStuckColors) ? 'var(--sb-offset-stuck)' : 'var(--sb-offset)')) + ';'
)
: ''
) +
(isRuntime
? (() => {
const currentAnimType = animationType || 'slide';
let transformValue = 'translateY(0)';
let transformOriginValue = '';
let finalOpacity = '1';
let pointerEvents = 'auto';
const shouldBeHidden = dismissed || isInitiallyHidden || ((hidden || hiddenByClass) && (yesAnimation || positionType === 'stickyByAnchor' || forceHide) && !shouldDisableAnimations);
if (shouldBeHidden) {
pointerEvents = 'none';
if (currentAnimType === 'slide') {
// FIX ANIMAZIONE: Usa calc() e Variabili CSS per garantire una transizione fluida e corretta
// Determiniamo quale variabile offset usare in base allo stato attuale
const activeOffset = (useCustomColors ? 'var(--sb-offset-custom, 0px)' : (stuck && !shouldDisableStuckStyles && (positionType !== 'stickyByAnchor' || useStuckColors) ? 'var(--sb-offset-stuck, 0px)' : 'var(--sb-offset, 0px)'));
const useOffset = (positionType === 'sticky' || positionType === 'stickyByAnchor' || positionType === 'fixed');
transformValue = placement === 'bottom'
? 'translateY(calc(100% + ' + (useOffset ? activeOffset : '0px') + ' + 20px))'
: 'translateY(calc(-100% - ' + (useOffset ? activeOffset : '0px') + ' - 20px))';
finalOpacity = '0';
} else if (currentAnimType === 'fold') {
transformValue = 'perspective(1500px) rotateX(' + (placement === 'bottom' ? '80deg' : '-80deg') + ')';
const rect = $el.getBoundingClientRect();
if (placement === 'bottom') {
transformOriginValue = 'center calc(100% + ' + (window.innerHeight - rect.bottom) + 'px)';
} else {
transformOriginValue = 'center calc(0% - ' + rect.top + 'px)';
}
finalOpacity = '0';
} else { // Fade
finalOpacity = '0';
}
} else if (currentAnimType === 'fold') {
transformOriginValue = placement === 'bottom' ? 'center bottom' : 'center top';
}
return 'transform: ' + transformValue + ';' +
(transformOriginValue ? 'transform-origin: ' + transformOriginValue + ';' : '') +
'opacity: ' + finalOpacity + ';';
})()
: ''
)
">