\n\n
\n\n
\n
\n \n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n \n \n \n );\n }\n}\n","/*! @design/bapf-pattern-library v3.7.0 | 09.12.2024 | _tabbar.js */\nimport { Utilities } from './_utilities';\r\n\r\n/**\r\n * Pattern: Tabbar\r\n * Initialisiert Tabbar-Navigation\r\n * @constructor\r\n * @param {HTMLElement} element - TabBar Pattern, default: .ba-tabbar\r\n */\r\nexport class BaTabbar {\r\n\r\n constructor (element) {\r\n /* istanbul ignore next */\r\n if (element.hasAttribute('data-initialized')) return;\r\n\r\n this.scrollFactor = .5; // Anteil des sichtbarer Bereichs, der pro Klick gescrollt wird\r\n this.domEl = element;\r\n this.leftPos = 0;\r\n this.listWidth = 0;\r\n this.viewportWidth = 0;\r\n\r\n this.viewport = this.domEl.querySelector('.ba-viewport');\r\n this.marker = this.domEl.querySelector('.ba-viewport ul li.ba-marker');\r\n\r\n this.list = this.domEl.querySelector('ul');\r\n this.items = this.domEl.querySelectorAll('ul li:not(.ba-marker)');\r\n this.links = this.domEl.querySelectorAll('ul li a');\r\n this.controls = {\r\n prev: {\r\n el : this.domEl.querySelector('.ba-prev'),\r\n title: this.domEl.getAttribute('data-title-prev') || 'zurück'\r\n },\r\n next: {\r\n el : this.domEl.querySelector('.ba-next'),\r\n title: this.domEl.getAttribute('data-title-next') || 'weiter'\r\n }\r\n };\r\n\r\n this.initTabs();\r\n this.initVisibilityChange();\r\n this.initActions();\r\n this.initControls();\r\n this.initItemResizeObserver();\r\n this.initResize();\r\n this.initTouch();\r\n this.initTouchPad();\r\n\r\n this.domEl.setAttribute('data-initialized', 'true');\r\n }\r\n\r\n /**\r\n * Initialisiert die Tabbar, wenn sichtbar\r\n */\r\n initVisibilityChange () {\r\n try {\r\n const observer = new IntersectionObserver((entries) => {\r\n entries.forEach(entry => {\r\n if (entry.intersectionRatio > 0) {\r\n this.initUI();\r\n }\r\n });\r\n });\r\n observer.observe(this.domEl);\r\n }\r\n catch (e)\r\n /* istanbul ignore next */\r\n {\r\n this.initUI();\r\n console.log('IntersectionObserver nicht unterstützt, bitte Browser updaten.'); //NOSONAR\r\n }\r\n }\r\n\r\n /**\r\n * Elemente vermessen, Marker positionieren\r\n */\r\n initUI () {\r\n this.startMeasure();\r\n this.list.classList.add('active'); // Um die max. Größe eines Eintrags zu messen, müssen alle auf aktiv (bold) gestellt werden\r\n\r\n setTimeout(() => {\r\n this.items.forEach((item) => {\r\n item.removeAttribute('style');\r\n item.style.width = item.clientWidth + 'px';\r\n });\r\n\r\n this.listWidth = this.list.offsetWidth;\r\n this.list.classList.remove('active'); // Nach dem Messen wieder alle auf Normalzustand\r\n this.updateUI();\r\n\r\n const activeLink = this.list.querySelector('a.active');\r\n if (activeLink) {\r\n const activeListItem = activeLink.closest('li');\r\n this.setMarker(activeListItem);\r\n }\r\n this.endMeasure();\r\n }, 0);\r\n }\r\n\r\n /**\r\n * Aktionen und Handler initialisieren\r\n */\r\n initActions () {\r\n const tabbar = this;\r\n tabbar.links.forEach((link) => {\r\n\r\n // Marker unter Link bewegen\r\n link.addEventListener('mouseenter', (e) => {\r\n tabbar.marker.classList.add('hover');\r\n const targetElem = e.target;\r\n const targetListItem = targetElem.closest('li');\r\n tabbar.setMarker(targetListItem);\r\n }, false);\r\n\r\n // Marker unter aktiven Link zurückbewegen\r\n link.addEventListener('mouseleave', () => {\r\n tabbar.marker.classList.remove('hover');\r\n const activeLink = tabbar.list.querySelector('a.active');\r\n if (activeLink) {\r\n const activeListItem = activeLink.closest('li');\r\n tabbar.setMarker(activeListItem);\r\n } else {\r\n /* istanbul ignore next */\r\n Object.assign(tabbar.marker.style, {\r\n left : 0,\r\n width: 0\r\n });\r\n }\r\n }, false);\r\n\r\n // Gedrückt-Stil hinzufügen\r\n link.addEventListener('mousedown', () => {\r\n setTimeout(function () { // warten auf blur Event des vorherigen :focus\r\n tabbar.marker.classList.remove('hover');\r\n }, 10);\r\n });\r\n\r\n // Gedrückt-Stil ersetzen durch hover\r\n link.addEventListener('mouseup', () => {\r\n tabbar.marker.classList.add('hover');\r\n });\r\n\r\n // Link aktiv setzen und in Viewport schieben\r\n link.addEventListener('click', (e) => {\r\n /* istanbul ignore next */\r\n if (tabbar.domEl.classList.contains('ba-static-links')) {\r\n return true;\r\n }\r\n e.preventDefault();\r\n const activeElems = tabbar.domEl.querySelectorAll('.active');\r\n if (activeElems.length > 0) {\r\n activeElems.forEach(activeElem => activeElem.classList.remove('active'));\r\n }\r\n e.target.classList.add('active');\r\n tabbar.moveIntoViewport(e.target.closest('li'));\r\n });\r\n\r\n // Active-Marker updaten\r\n link.addEventListener('blur', function () {\r\n setTimeout(function () { // auf focus warten, document.activeElement geht zwischendurch auf \r\n let focusedElement = document.activeElement;\r\n /* istanbul ignore next */\r\n if (document.activeElement.shadowRoot) {\r\n focusedElement = document.activeElement.shadowRoot.activeElement;\r\n }\r\n /* istanbul ignore next */\r\n if (!focusedElement.closest('.ba-tabbar') || focusedElement.closest('.ba-tabbar') !== tabbar.domEl) { // nicht mehr in Tabbar? Active-Marker resetten\r\n const activeLink = tabbar.list.querySelector('a.active');\r\n if (activeLink) {\r\n const activeListItem = activeLink.closest('li');\r\n tabbar.setMarker(activeListItem);\r\n }\r\n }\r\n }, 10);\r\n });\r\n\r\n // Navigation per Tastatur\r\n link.addEventListener('focus', () => {\r\n const entry = link.closest('li');\r\n tabbar.moveIntoViewport(entry);\r\n tabbar.setMarker(entry);\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * Links/rechts Scrollbuttons initialisieren und aus Tab-Index entfernen\r\n */\r\n initControls () {\r\n const tabbar = this;\r\n const prevElem = this.controls.prev.el;\r\n prevElem.setAttribute('tabindex', '-1');\r\n prevElem.setAttribute('title', this.controls.prev.title);\r\n prevElem.addEventListener('click', () => {\r\n tabbar.slide('prev');\r\n });\r\n const nextElem = this.controls.next.el;\r\n nextElem.setAttribute('tabindex', '-1');\r\n nextElem.setAttribute('title', this.controls.next.title);\r\n nextElem.addEventListener('click', () => {\r\n tabbar.slide('next');\r\n });\r\n }\r\n\r\n /**\r\n * A11y: reagieren auf Größenänderungen durch interaktive User-Anpassungen (letter-spacing, font-size, ...)\r\n * Prüfschritt 1.4.12a Textabstände anpassbar (AA)\r\n */\r\n initItemResizeObserver () {\r\n try {\r\n const tabbar = this;\r\n const observer = new ResizeObserver((el) => {\r\n const elementWidth = el[0].contentRect.width;\r\n const containerWidth = el[0].target.closest('li').clientWidth;\r\n\r\n const isEnoughDifference = () => {\r\n const diff = containerWidth - elementWidth; // sollte ~32 sein (2x 16px Container-Padding)\r\n const BOLD_FACTOR = 0.015; // 0.015 ist der Faktor um den sich die Groesse durch bold verändert\r\n return diff < 32 || diff > 34 + elementWidth * BOLD_FACTOR;\r\n };\r\n\r\n if (elementWidth > 0 && isEnoughDifference()) {\r\n tabbar.initUI();\r\n }\r\n });\r\n // Nur das breiteste Item muss beobachtet werden\r\n let widestItem = null;\r\n let maxWidth = 0;\r\n this.startMeasure();\r\n tabbar.items.forEach((item) => {\r\n const itemWidth = item.offsetWidth;\r\n if (itemWidth > maxWidth) {\r\n maxWidth = itemWidth;\r\n widestItem = item;\r\n }\r\n });\r\n this.endMeasure();\r\n if (widestItem) observer.observe((/** @type{HTMLElement} */(widestItem)).querySelector('a'));\r\n } catch (e) {\r\n /* istanbul ignore next */\r\n console.log('ResizeObserver nicht unterstützt, bitte Browser updaten.'); //NOSONAR\r\n }\r\n }\r\n\r\n /**\r\n * Verwendung als Tabs initialisieren\r\n */\r\n initTabs () {\r\n if (this.list.getAttribute('role') === 'tablist') {\r\n const tabbar = this;\r\n\r\n // BS Tabverhalten aktivieren\r\n tabbar.links.forEach((triggerTab) => {\r\n if (triggerTab.getAttribute('role') === 'tab') {\r\n triggerTab.bsTab = (/** @type{any} */(window)).dpl.bs.Tab.getOrCreateInstance(triggerTab);\r\n triggerTab.addEventListener('keydown', () => {\r\n document.body.classList.add('ba-keyboard-mode');\r\n });\r\n triggerTab.addEventListener('click', (e) => {\r\n e.preventDefault();\r\n triggerTab.focus({ preventScroll: true });\r\n triggerTab.bsTab.show();\r\n });\r\n }\r\n });\r\n\r\n // Bewegen des Tastaturfokus per home/end-Tasten\r\n tabbar.domEl.addEventListener('keydown', (e) => {\r\n if (e.key === 'Home') {\r\n e.stopPropagation();\r\n e.preventDefault();\r\n const next = tabbar.items[0];\r\n next.querySelector('a').click();\r\n }\r\n if (e.key === 'End') {\r\n e.stopPropagation();\r\n e.preventDefault();\r\n const next = tabbar.items[tabbar.items.length - 1];\r\n next.querySelector('a').click();\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Auf Größenänderungen und Orientationchange des Browsers reagieren\r\n */\r\n initResize () {\r\n const tabbar = this;\r\n let resizeTimeout = null;\r\n window.addEventListener('resize', () => {\r\n clearTimeout(resizeTimeout);\r\n resizeTimeout = setTimeout(tabbar.updateUI.bind(tabbar), 100);\r\n });\r\n }\r\n\r\n /**\r\n * Touch-Steuerung für Mobile initialisieren\r\n */\r\n initTouch () {\r\n const tabbar = this;\r\n let startPos = 0;\r\n let startOffsetX = 0;\r\n\r\n // Touchdrag Handler\r\n tabbar.list.addEventListener('touchstart', (e) => {\r\n tabbar.domEl.classList.add('ba-noanim'); // Slide-Transition für touch-drag deaktivieren\r\n startPos = tabbar.leftPos;\r\n startOffsetX = e.changedTouches[0].pageX;\r\n window.addEventListener('touchmove', touchDragHandler);\r\n window.addEventListener('touchend', touchDragEnd);\r\n }, Utilities.supportsPassiveEvent() ? { passive: true } : false);\r\n\r\n // Handler während des Drag-Vorgangs\r\n function touchDragHandler (e) {\r\n e.stopPropagation();\r\n const newOffsetX = e.changedTouches[0].pageX;\r\n const distance = startOffsetX - newOffsetX;\r\n tabbar.leftPos = startPos - distance;\r\n tabbar.updateSlidePos();\r\n }\r\n\r\n // Handler nach dem Drag/Touch-Vorgang\r\n function touchDragEnd (e) {\r\n e.stopPropagation();\r\n tabbar.domEl.classList.remove('ba-noanim'); // Slide-Transition wieder aktivieren\r\n window.removeEventListener('touchmove', touchDragHandler);\r\n window.removeEventListener('touchend', touchDragEnd);\r\n }\r\n }\r\n\r\n /**\r\n * Touchpad-/MightyMouse-Steuerung initialisieren\r\n */\r\n initTouchPad () {\r\n const tabbar = this;\r\n const supportsPassive = Utilities.supportsPassiveEvent();\r\n tabbar.domEl.addEventListener('wheel', (e) => {\r\n if (tabbar.listWidth <= tabbar.viewportWidth) {\r\n return;\r\n }\r\n if (!supportsPassive) e.preventDefault();\r\n const delta = e.deltaX;\r\n if (delta < 0) {\r\n tabbar.slide('prev');\r\n }\r\n if (delta > 0) {\r\n tabbar.slide('next');\r\n }\r\n }, supportsPassive ? { passive: true } : false);\r\n }\r\n\r\n /**\r\n * Tabbar im sichtbaren Bereich verschieben: links, rechts oder Pixelposition\r\n * @param {string} direction - Mögliche Werte: 'prev', 'next'\r\n */\r\n slide (direction) {\r\n let newPos = this.leftPos;\r\n const distance = this.viewportWidth * this.scrollFactor;\r\n if (direction === 'prev') {\r\n newPos = this.leftPos + distance;\r\n }\r\n if (direction === 'next') {\r\n newPos = this.leftPos - distance;\r\n }\r\n this.leftPos = newPos;\r\n\r\n this.updateSlidePos();\r\n }\r\n\r\n /**\r\n * Ausrichtung, Overflow, Position und Buttonzustände aktualisieren\r\n * (initial, nach Resize, nach sichtbarmachen, ...)\r\n */\r\n updateUI () {\r\n const isHidden = Utilities.isHidden(this.domEl);\r\n\r\n /* istanbul ignore next */\r\n if (isHidden) {\r\n this.startMeasure();\r\n }\r\n\r\n this.domEl.classList.add('ba-noanim');\r\n this.updateOverflow();\r\n this.updateSlidePos();\r\n const activeElem = this.list.querySelector('.active');\r\n if (activeElem) {\r\n this.moveIntoViewport(activeElem.closest('li'));\r\n }\r\n this.domEl.classList.remove('ba-noanim');\r\n\r\n /* istanbul ignore next */\r\n if (isHidden) {\r\n this.endMeasure();\r\n }\r\n }\r\n\r\n /**\r\n * Testen, ob neue Slider-Position korrekt im Viewport liegt,\r\n * dann auf neue Position sliden und Controls aktualisieren\r\n */\r\n updateSlidePos () {\r\n if (this.leftPos > 0) { // Ganz links\r\n this.leftPos = 0;\r\n }\r\n if (this.leftPos < this.viewportWidth - this.listWidth) { // Ganz rechts\r\n if (this.viewportWidth >= this.listWidth) {\r\n this.leftPos = 0;\r\n } else {\r\n this.leftPos = this.viewportWidth - this.listWidth - 8; // 8px = padding-viewport + outline auf focus\r\n }\r\n }\r\n this.list.style.transform = `translate3d(${this.leftPos}px, 0, 0)`;\r\n this.updateControlStatus();\r\n }\r\n\r\n /**\r\n * Scrollbuttons je nach Scrollposition de/aktivieren\r\n */\r\n updateControlStatus () {\r\n if (this.leftPos >= 0) { // Ganz links\r\n this.domEl.classList.remove('ba-show-prev');\r\n } else {\r\n this.domEl.classList.add('ba-show-prev');\r\n }\r\n if (this.leftPos <= this.viewportWidth - this.listWidth) { // Ganz rechts\r\n this.domEl.classList.remove('ba-show-next');\r\n } else {\r\n this.domEl.classList.add('ba-show-next');\r\n }\r\n }\r\n\r\n /**\r\n * Prüfen, ob Tabbar größer ist als sichtbarer Bereich\r\n */\r\n updateOverflow () {\r\n let availableWidth;\r\n if (this.domEl.classList.contains('ba-overflown')) {\r\n availableWidth = Utilities.outerWidth(this.viewport);\r\n } else {\r\n availableWidth = this.viewport.offsetWidth + 1; //\r\n }\r\n this.listWidth > availableWidth ? this.domEl.classList.add('ba-overflown') : this.domEl.classList.remove('ba-overflown');\r\n this.viewportWidth = this.viewport.offsetWidth;\r\n }\r\n\r\n /**\r\n * Eintrag in den sichtbaren Bereich schieben\r\n * @param {HTMLElement} el - Tabbar-Item\r\n */\r\n moveIntoViewport (el) {\r\n if (!this.domEl.classList.contains('ba-overflown')) {\r\n return;\r\n }\r\n // Zusätzlicher Abstand damit der Focus-Rahmen komplett im View liegt\r\n const spacing = 40;\r\n const elLeft = el.offsetLeft;\r\n const elRight = elLeft + el.offsetWidth;\r\n /* istanbul ignore next */\r\n if (this.leftPos < -elLeft) {\r\n this.leftPos = -elLeft + spacing;\r\n }\r\n /* istanbul ignore next */\r\n if (this.leftPos - this.viewportWidth > -elRight) {\r\n this.leftPos = this.viewportWidth - elRight - spacing;\r\n }\r\n this.updateSlidePos();\r\n }\r\n\r\n /**\r\n * Größe und Position des hover-Markers setzen\r\n * @param {HTMLElement} el - Tabbar-Item, das markiert werden soll\r\n */\r\n setMarker (el) {\r\n const markerStyles = {\r\n left : el.offsetLeft + 'px',\r\n width: el.offsetWidth + 'px'\r\n };\r\n Object.assign(this.marker.style, markerStyles);\r\n }\r\n\r\n /**\r\n * Bevor Elemente vermessen werden können, muss die Tabbar sichtbar sein\r\n */\r\n startMeasure () {\r\n function getParentsHidden (elem, parentSelector) {\r\n if (parentSelector === undefined) {\r\n parentSelector = document.body;\r\n }\r\n const parents = [];\r\n let p = elem.parentNode;\r\n while (p && p !== parentSelector) {\r\n let o = p;\r\n /* istanbul ignore next */\r\n if (o.nodeName === '#document-fragment') {\r\n o = o.host;\r\n }\r\n /* istanbul ignore next */\r\n if (o.style.display === 'none' || window.getComputedStyle(o).display === 'none') {\r\n parents.push(o);\r\n }\r\n p = o.parentNode;\r\n }\r\n return parents;\r\n }\r\n\r\n const parentsHiddenElems = getParentsHidden(this.domEl);\r\n /* istanbul ignore next */\r\n parentsHiddenElems.forEach((elem) => {\r\n elem.classList.add('ba-unhide-for-measure');\r\n });\r\n }\r\n\r\n /**\r\n * Nach dem Vermessen kann Tabbar wieder unsichtbar werden, wenn sie es vorher war\r\n */\r\n endMeasure () {\r\n let parent = this.domEl.closest('.ba-unhide-for-measure');\r\n /* istanbul ignore next */\r\n while (parent !== null) {\r\n this.domEl.closest('.ba-unhide-for-measure').classList.remove('ba-unhide-for-measure');\r\n parent = parent.closest('.ba-unhide-for-measure');\r\n }\r\n }\r\n\r\n /**\r\n * Setzt den Tab mit tabId active\r\n * @param {string} tabId\r\n * @param {boolean} showPanel\r\n */\r\n setActive(tabId, showPanel = true) {\r\n const target = document.getElementById(tabId);\r\n if (showPanel) {\r\n target.click();\r\n return;\r\n }\r\n this.links.forEach(link => {\r\n link.classList.remove('active');\r\n link.setAttribute('aria-selected', 'false');\r\n });\r\n target.classList.add('active');\r\n target.setAttribute('aria-selected', 'true');\r\n const li = target.closest('li');\r\n this.setMarker(li);\r\n this.moveIntoViewport(li);\r\n }\r\n}\r\n","export enum ActiveTab {\n PRIVATPERSONEN = 'privatpersonen',\n UNTERNEHMEN = 'unternehmen',\n INSTITUTIONEN = 'institutionen'\n}\n","import { Component, Element, h, Host, Prop, State, ComponentInterface, Watch } from '@stencil/core';\nimport { DefaultLanguages } from '../../../utils/model/DefaultLanguages';\nimport { ConfigProvider } from '../../../config/ConfigProvider';\nimport { BsWebcomponent } from '../bsWebcomponent';\nimport { BaTabbar } from '@design/bapf-pattern-library/source/js/_tabbar';\nimport { Language } from '../../../utils/model/Language';\nimport { IConfiguration } from '../../../../config/IConfiguration';\nimport { HfUtil } from '../utils/HfUtil';\nimport { ActiveTab } from './ActiveTab';\nimport { ActingType } from '@websso/oiam-oauth-types';\nimport { HeaderMode } from '../types/header-mode';\nimport globalStore from '../../../stores/globalStore';\n\n/**\n * @internal\n */\n@Component({\n tag: 'bahf-header-megaflyout',\n styles: '.pb-2 { padding-bottom:1.0rem !important }',\n assetsDirs: ['../assets'],\n shadow: false,\n scoped: false,\n})\nexport class BahfHeaderMegaflyout implements ComponentInterface {\n @Prop() loggedIn: boolean;\n @Prop() language: Language = DefaultLanguages.DE;\n @Prop() config: IConfiguration = ConfigProvider.get();\n @Prop() withLangSwitch: boolean = true;\n @Prop() jsonData;\n @Prop() actingType: ActingType | undefined;\n\n @State() isMegaFlyoutVisible: boolean;\n\n @State() activeTab: ActiveTab;\n @Element() megaflyoutInstance: HTMLElement;\n\n private readonly ID_MEGAFLYOUT = 'headerFlyoutMain';\n\n // Tabbar\n tabbarElem: HTMLElement;\n\n @Watch('loggedIn')\n @Watch('actingType')\n handleLoggedInChange() {\n this.setActiveTabByUserOrDocument();\n }\n\n componentWillLoad() {\n this.setActiveTabByUserOrDocument();\n }\n\n componentDidRender() {\n this.megaflyoutInstance && new BsWebcomponent(this.megaflyoutInstance);\n setTimeout(() => {\n this.tabbarElem &&\n !this.tabbarElem.hasAttribute('data-initialized') &&\n this.megaflyoutInstance.classList.contains('hydrated') &&\n this.megaflyoutInstance.parentNode !== null &&\n new BaTabbar(this.tabbarElem);\n });\n }\n\n setActiveTabByUserOrDocument() {\n if (this.loggedIn) {\n this.activeTab = this.mapActingTypeToActiveTab(this.actingType);\n } else {\n this.activeTab = this.isLangDe() ? this.getActiveTabFromDocument() : ActiveTab.PRIVATPERSONEN;\n }\n }\n\n getMegaflyoutCollapsibleId(sub: string, index: number) {\n return `headerFlyout${index}${'Sub' + sub}`;\n }\n\n private isLangDe(): boolean {\n return this.language.code === DefaultLanguages.DE.code;\n }\n\n render() {\n return (\n
\n \n