UNPKG

bootstrap-italia

Version:

Bootstrap Italia è un tema Bootstrap 5 per la creazione di applicazioni web nel pieno rispetto delle linee guida di design per i siti internet e i servizi digitali della PA

247 lines (216 loc) 7.62 kB
import BaseComponent from './base-component.js'; import EventHandler from './dom/event-handler.js'; import SelectorEngine from './dom/selector-engine.js'; import Manipulator from './dom/manipulator.js'; import onDocumentScroll from './util/on-document-scroll.js'; import NavBarCollapsible from './navbar-collapsible.js'; import { documentScrollTo } from './util/tween.js'; const NAME = 'navscroll'; const CLASS_NAME_ACTIVE = 'active'; const SELECTOR_NAVSCROLL = '[data-bs-navscroll]'; //'.it-navscroll-wrapper' const SELECTOR_LIST = 'ul.link-list'; const SELECTOR_LINK_CONTAINER = 'li.nav-link, li.nav-item'; const SELECTOR_LINK = 'a.nav-link'; const SELECTOR_LINK_ACTIVE = `${SELECTOR_LINK}.${CLASS_NAME_ACTIVE}`; const SELECTOR_LINK_CLICKABLE = `${SELECTOR_LINK}[href^="#"]`; const SELECTOR_CONTAINER = '.it-page-sections-container'; const SELECTOR_PAGE_SECTION = '.it-page-section'; const SELECTOR_TOGGLER = '.custom-navbar-toggler'; const SELECTOR_TOGGLER_ICON = '.it-list'; const SELECTOR_COLLAPSIBLE = '.navbar-collapsable'; const SELECTOR_PROGRESS_BAR = '.it-navscroll-progressbar'; const Default = { scrollPadding: 10, duration: 800, easing: 'easeInOutSine', }; class NavScroll extends BaseComponent { constructor(element, config) { super(element); this._config = this._getConfig(config); this._togglerElement = SelectorEngine.findOne(SELECTOR_TOGGLER, this._element); this._sectionContainer = SelectorEngine.findOne(SELECTOR_CONTAINER); this._collapsible = this._getCollapsible(); this._isCollapseOpened = false; this._callbackQueue = []; this._scrollCb = null; this._bindEvents(); } // Getters static get NAME() { return NAME } // Public setScrollPadding(scrollPadding) { this._config.scrollPadding = scrollPadding; } dispose() { if (this._scrollCb) { this._scrollCb.dispose(); } super.dispose(); } // Private _getConfig(config) { config = { ...Default, ...Manipulator.getDataAttributes(this._element), ...(typeof config === 'object' ? config : {}), }; return config } _bindEvents() { this._scrollCb = onDocumentScroll(() => this._onScroll()); if (this._collapsible) { EventHandler.on(this._collapsible._element, 'shown.bs.navbarcollapsible', () => this._onCollapseOpened()); EventHandler.on(this._collapsible._element, 'hidden.bs.navbarcollapsible', () => this._onCollapseClosed()); } SelectorEngine.find(SELECTOR_LINK_CLICKABLE, this._element).forEach((link) => { link.addEventListener('click', () => { const scrollHash = () => this._scrollToHash(link.hash); if (this._isCollapseOpened) { this._callbackQueue.push(scrollHash); this._collapsible.hide(); } else { scrollHash(); } }); }); if (typeof window !== 'undefined' && typeof document !== 'undefined') { EventHandler.on(window, 'load', () => { //if page is already scrolled setTimeout(() => this._onScroll(), 150); }); } } _onCollapseOpened() { this._isCollapseOpened = true; } _onCollapseClosed() { while (this._callbackQueue.length > 0) { const cb = this._callbackQueue.shift(); if (typeof cb === 'function') { cb(); } } this._isCollapseOpened = false; } _getParentLinks(element) { const parents = []; let parentContainer = element.closest(SELECTOR_LIST); let parentContainerPrev = null; let exit = false; while (parentContainer && !exit) { const parentLinkContainer = parentContainer.closest(SELECTOR_LINK_CONTAINER); if (parentLinkContainer) { const link = parentLinkContainer.querySelector(SELECTOR_LINK); if (link) { parents.push(link); } } parentContainerPrev = parentContainer; parentContainer = (parentContainer.parentElement || parentContainer).closest(SELECTOR_LIST); //avoid self select of closest if (parentContainer === parentContainerPrev) { exit = true; } } return parents } _decorateToggler(text) { if (this._togglerElement) { const icon = SelectorEngine.findOne(SELECTOR_TOGGLER_ICON, this._togglerElement); this._togglerElement.innerText = ''; this._togglerElement.textContent = ''; this._togglerElement.append(icon); this._togglerElement.append(text); } } _scrollToHash(hash) { const target = SelectorEngine.findOne(hash, this._sectionContainer); if (target) { documentScrollTo(target.offsetTop - this._getScrollPadding(), { duration: this._config.duration, easing: this._config.easing, /*complete: () => { },*/ }); if (history.pushState) { history.pushState(null, null, hash); } else { location.hash = hash; } } } _updateProgress(content) { if (!content) { return } const progressBars = SelectorEngine.find(SELECTOR_PROGRESS_BAR); if (progressBars) { const offset = Math.abs(content.getBoundingClientRect().top); const height = content.getBoundingClientRect().height; const scrollAmount = (offset / height) * 100; const scrollValue = Math.min(100, Math.max(0, scrollAmount)); progressBars.forEach((progressBar) => { if (content.getBoundingClientRect().top <= 0) { progressBar.style.width = scrollValue + '%'; progressBar.setAttribute('aria-valuenow', scrollValue); } else { progressBar.style.width = 0 + '%'; progressBar.setAttribute('aria-valuenow', 0); } }); } } _onScroll() { const sectionsContainerTop = this._sectionContainer ? this._sectionContainer.offsetTop : 0; if (typeof document === 'undefined') { return } const scrollDistance = document.scrollingElement.scrollTop - sectionsContainerTop; const navItems = SelectorEngine.find(SELECTOR_LINK, this._element); const scrollPadding = this._getScrollPadding(); SelectorEngine.find(SELECTOR_PAGE_SECTION).forEach((pageSec, idx) => { if (pageSec.offsetTop - sectionsContainerTop <= scrollDistance + scrollPadding) { SelectorEngine.find(SELECTOR_LINK_ACTIVE, this._element).forEach((link) => { link.classList.remove(CLASS_NAME_ACTIVE); }); if (idx < navItems.length) { const currActive = navItems[idx]; this._getParentLinks(currActive).forEach((parentLink) => { parentLink.classList.add(CLASS_NAME_ACTIVE); }); currActive.classList.add(CLASS_NAME_ACTIVE); this._decorateToggler(currActive.innerText); } } }); this._updateProgress(this._sectionContainer); } _getCollapsible() { const coll = SelectorEngine.findOne(SELECTOR_COLLAPSIBLE, this._element); if (coll) { return NavBarCollapsible.getOrCreateInstance(coll) } return null } _getScrollPadding() { if (typeof this._config.scrollPadding === 'function') { return this._config.scrollPadding() } return this._config.scrollPadding } } /** * ------------------------------------------------------------------------ * Data Api implementation * ------------------------------------------------------------------------ */ if (typeof window !== 'undefined' && typeof document !== 'undefined') { const navs = SelectorEngine.find(SELECTOR_NAVSCROLL); navs.map((nav) => { NavScroll.getOrCreateInstance(nav); }); } export { NavScroll as default }; //# sourceMappingURL=navscroll.js.map