@govbr-ds/core
Version:
Padrão Digital de Governo
301 lines (275 loc) • 8.73 kB
JavaScript
import Tooltip from '../../partial/js/behavior/tooltip'
/** Classe para instanciar um objeto BRHeader*/
class BRHeader {
/**
* Instancia do objeto
* @param {string} name - Nome do componente em minúsculo
* @param {object} component - Objeto referenciando a raiz do componente DOM
*/
constructor(name, component) {
this.name = name
this.component = component
this.componentSearch = this.component.querySelector('.header-search')
this.componentSearchInput = this.component.querySelector('.header-search input')
this.componentSearchTrigger = this.component.querySelector('[data-toggle="search"]')
this.componentSearchDismiss = this.component.querySelector('[data-dismiss="search"]')
this.hideDrop = null
this.menuTrigger = this.component.querySelector('[data-target="#main-navigation"]')
this._setBehavior()
}
/**
* Define comportamentos do componente
* @private
*/
_setBehavior() {
this._setLogoutBehavior()
this._setSearchBehaviors()
this._setKeyboardBehaviors()
this._setDropdownBehavior()
this._setSticky()
}
/**
* Define comportamentos de login
* @private
*/
_setLoginBehavior() {
for (const login of this.component.querySelectorAll('[data-trigger="login"]')) {
login.addEventListener('click', () => {
const loginParent = login.closest('.header-login')
loginParent.querySelector('.header-sign-in').classList.add('d-none')
loginParent.querySelector('.header-avatar').classList.remove('d-none')
})
}
}
/**
* Define comportamentos de logout
* @private
*/
_setLogoutBehavior() {
for (const logout of this.component.querySelectorAll('[data-trigger="logout"]')) {
logout.addEventListener('click', () => {
const logoutParent = logout.closest('.header-login')
logoutParent.querySelector('.avatar').classList.remove('show')
logoutParent.querySelector('[data-toggle="dropdown"]').classList.remove('active')
logoutParent.querySelector('.header-sign-in').classList.remove('d-none')
logoutParent.querySelector('.header-avatar').classList.add('d-none')
})
}
}
/**
* Define comportamentos de busca
* @private
*/
_setSearchBehaviors() {
// Abrir busca
if (this.componentSearchTrigger) {
this.componentSearchTrigger.addEventListener('focus', () => {
this._cleanDropDownHeader()
})
this.componentSearchTrigger.addEventListener('click', () => {
this._openSearch()
})
}
// Fechar busca
if (this.componentSearchDismiss) {
this.componentSearchDismiss.addEventListener('click', () => {
this._closeSearch()
})
}
}
/**
* Define comportamentos do teclado
* @private
*/
_setKeyboardBehaviors() {
if (this.componentSearchInput) {
this.componentSearchInput.addEventListener('keydown', (event) => {
switch (event.keyCode) {
// Tecla ESC
case 27:
this._closeSearch()
break
default:
break
}
})
}
for (const trigger of this.component.querySelectorAll('.dropdown [data-toggle="dropdown"]')) {
trigger.addEventListener('keydown', (event) => {
switch (event.keyCode) {
// Tecla ESC
case 32:
if (event.target.parentNode.classList.contains('show')) {
event.target.parentNode.click()
event.target.parentNode.classList.remove('show')
event.target.classList.remove('active')
event.stopPropagation()
}
break
default:
break
}
})
}
}
/**
* Visualização da busca
* @private
*/
_openSearch() {
if (this.componentSearch) {
this.componentSearch.classList.add('active')
this.componentSearch.querySelector('input').focus()
}
}
/**
* Esconde a busca
* @private
*/
_closeSearch() {
if (this.componentSearch) {
this.componentSearch.classList.remove('active')
//this.componentSearchTrigger.focus()
this._nextFocusElement().focus()
}
}
/**
* Exibe notificação
* @param {event} event - referencia ao evento
*/
handleEvent(event) {
const notificationElement = this.component.querySelector('.br-notification')
if (notificationElement && !notificationElement.contains(event.target)) {
if (this.activateTr !== event.target.parentNode) {
this._cleanDropDownHeaderRef(this.component)
}
}
}
/**
* Define comportamentos do dropdown
* @private
*/
_setDropdownBehavior() {
// TODO: Trocar o código abaixo pelo utilitário dropdown
this.cleaned = false
let hideDrop
this.activateTr = 'teste'
for (const trigger of this.component.querySelectorAll('.dropdown [data-toggle="dropdown"]')) {
// eslint-disable-next-line no-loop-func
trigger.addEventListener('click', (event) => {
this._headerTooltip()
clearTimeout(hideDrop)
this.activateTr = trigger
document.addEventListener('mousedown', this, false)
event.stopImmediatePropagation()
// Toggle de abrir / fechar
const hasShow = trigger.classList.contains('active')
if (hasShow) {
trigger.classList.remove('active')
trigger.closest('.dropdown').classList.remove('show')
} else {
this._cleanDropDownHeader()
trigger.classList.add('active')
trigger.closest('.dropdown').classList.add('show')
// Evita que o componente feche o drop ao navegar pelo teclado
const next = this._nextFocusElement()
next.addEventListener('focus', () => {
clearTimeout(hideDrop)
})
}
event.stopPropagation()
return ''
})
// Faz o drop fechar ao clicar fora
// eslint-disable-next-line no-loop-func
}
this.menuTrigger.addEventListener('focus', () => {
this._cleanDropDownHeader()
})
}
/**
* Define comportamentos do tooltip
* @private
*/
_headerTooltip() {
if (this.TooltipExampleList) {
this.TooltipExampleList.forEach((tooltipElem) => {
tooltipElem.component.remove()
tooltipElem.popperInstance.destroy()
})
}
this.TooltipExampleList = []
this.component.querySelectorAll('.notification-tooltip').forEach((TooltipExample) => {
const texttooltip = TooltipExample.getAttribute('data-tooltip-text')
const config = {
activator: TooltipExample,
placement: 'top',
textTooltip: texttooltip,
}
const x = new Tooltip(config)
this.TooltipExampleList.push(x)
})
}
/**
* Limpa referencias do dorpdown
* @private
* @param {object} ref - Referência ao componente header
*/
_cleanDropDownHeaderRef(ref) {
if (this.cleaned === false) {
for (const trigger of ref.querySelectorAll('.dropdown.show')) {
trigger.classList.remove('show')
trigger.parentNode.classList.remove('show')
for (const button of ref.querySelectorAll('.br-button')) {
button.classList.remove('active')
}
}
}
this.cleaned = false
}
/**
* Limpa referencias do dorpdown
* @private
*/
_cleanDropDownHeader() {
this._cleanDropDownHeaderRef(this.component)
}
/**
* Define modo sticky ao header
* @private
*/
_setSticky() {
if (this.component.hasAttribute('data-sticky')) {
window.onscroll = () => {
if (window.pageYOffset > this.component.offsetHeight) {
this.component.classList.add('sticky', 'compact')
} else {
this.component.classList.remove('sticky', 'compact')
}
}
}
}
/**
* Foca no próximo elmento de ação
* @private
*/
_nextFocusElement() {
//lista de elementos de ação
const focussableElements =
'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'
if (document.activeElement) {
const focussable = Array.prototype.filter.call(document.body.querySelectorAll(focussableElements), (element) => {
//check for visibility while always include the current activeElement
return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement
})
const index = focussable.indexOf(document.activeElement)
if (index > -1) {
const nextElement = focussable[index + 1] || focussable[0]
//nextElement.focus();
return nextElement
}
}
return null
}
}
export default BRHeader