hr-design-system-handlebars 0.50.1 → 0.50.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -72,6 +72,18 @@ module.exports = {
72
72
  },
73
73
  ],
74
74
  include: path.resolve(__dirname, '../'),
75
+ },
76
+ {
77
+ test: /\.(png|woff|woff2|eot|ttf|jpg|jpeg|gif|svg)$/,
78
+ use: [
79
+ {
80
+ loader: "file-loader",
81
+ options: {
82
+ name: "[path][name].[ext]",
83
+ context: "",
84
+ },
85
+ },
86
+ ],
75
87
  }
76
88
  )
77
89
 
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # v0.50.2 (Fri Jul 15 2022)
2
+
3
+ #### 🐛 Bug Fix
4
+
5
+ - refactor header.stories.mdx [#276](https://github.com/mumprod/hr-design-system-handlebars/pull/276) ([@StefanVesper](https://github.com/StefanVesper))
6
+
7
+ #### Authors: 1
8
+
9
+ - SonicSoulSurfer ([@StefanVesper](https://github.com/StefanVesper))
10
+
11
+ ---
12
+
1
13
  # v0.50.1 (Fri Jul 15 2022)
2
14
 
3
15
  #### 🐛 Bug Fix
@@ -596,6 +596,10 @@ video {
596
596
  .relative {
597
597
  position: relative;
598
598
  }
599
+ .sticky {
600
+ position: -webkit-sticky;
601
+ position: sticky;
602
+ }
599
603
  .bottom-0 {
600
604
  bottom: 0px;
601
605
  }
@@ -2417,6 +2421,9 @@ video {
2417
2421
  --color-topline: #c20016;
2418
2422
  }
2419
2423
  /*! purgecss end ignore */
2424
+ .sbdocs-content {
2425
+ max-width:1140px !important;
2426
+ }
2420
2427
  .hide-scroll-bar {
2421
2428
  -ms-overflow-style: none;
2422
2429
  scrollbar-width: none;
@@ -32,23 +32,25 @@ document.addEventListener('alpine:init', () => {
32
32
  init(){
33
33
  let lastScrollTop = 0
34
34
  let height = window.innerHeight
35
+
36
+ //Globale Variable, true = user initiated scroll / false = programmatic scroll via JS (e.g. click on Anchor Link)
35
37
  let userScroll = false;
36
38
  window.userScroll = userScroll;
37
39
 
40
+ // gets fired when user initated scroll happened, global variable is used in Ticker-Topnews and other anchor links to prevent expanding of the navigation if scrollposition gets corrected by JS.
38
41
  const mouseEvent = () => {
39
42
  userScroll = true;
40
43
  window.userScroll = true;
41
- //console.log('user action detected')
42
44
  }
43
-
45
+ // detect if the user clicked/dragged the scrollbar manually
44
46
  const clickedOnScrollbar = mouseX => {
45
47
  return document.documentElement.offsetWidth <= mouseX ? true : false;
46
48
  }
47
-
49
+ // if clicked on scrollbar, fire user initiated mouse event
48
50
  const mouseDownHandler = e => {
49
51
  clickedOnScrollbar(e.clientX) ? mouseEvent() : null
50
52
  };
51
-
53
+ // main scroll handler, defines scroll direction, percent of viewport scrolled, visibility of navigation and subnavigation
52
54
  const scrollHandler = () => {
53
55
  let winScroll = document.body.scrollTop || document.documentElement.scrollTop
54
56
  winScroll > lastScrollTop ? this.scrollingDown = true : this.scrollingDown = false
@@ -59,14 +61,19 @@ document.addEventListener('alpine:init', () => {
59
61
  //console.log('winscroll: '+winScroll+' screen height: '+height + ' percent scrolled: '+ this.percent)
60
62
  //console.log('Scroll initiated by ' + (window.userScroll == true ? "user" : "browser"));
61
63
  }
62
-
64
+ // Listeners
63
65
  window.addEventListener('mousedown', mouseDownHandler, false)
64
66
  window.addEventListener('wheel', mouseEvent, false);
65
67
  window.addEventListener('touchmove', mouseEvent, false)
66
68
  window.addEventListener('scroll', this.debounce( scrollHandler,50), { passive: true })
67
69
  },
70
+ //Holds the percentage of scrolled viewport
68
71
  percent: 0,
72
+
73
+ //defines the scroll direction
69
74
  scrollingDown: true,
75
+
76
+ //returns true if section navigation is hidden on desktop OR service navigation is hidden on mobile
70
77
  isNavHidden() {
71
78
  if(this.$screen('lg')) {
72
79
  return this.shouldSectionNavBeHidden()
@@ -74,6 +81,8 @@ document.addEventListener('alpine:init', () => {
74
81
  return this.shouldServiceNavBeHidden()
75
82
  }
76
83
  },
84
+
85
+ //returns false if subnav is visible and true if subnav is hidden
77
86
  isSubNavHidden() {
78
87
  if(this.$screen('lg')){
79
88
  if (document.querySelector('.isSelectedAndOpen') !== null) {
@@ -85,9 +94,13 @@ document.addEventListener('alpine:init', () => {
85
94
  return true
86
95
  }
87
96
  },
97
+
98
+ // returns true if the user scrolled at least 1px from top
88
99
  shouldBrandNavBeHidden() {
89
100
  return this.percent > 0
90
101
  },
102
+
103
+ // returns true if user scrolled >50% and scrolls down, no burger menu is open and the screen size is desktop. If scroll was initiated by script, ignore scroll direction.
91
104
  shouldSectionNavBeHidden() {
92
105
  if(window.userScroll == true){
93
106
  return this.percent > 50 && this.scrollingDown && this.$store.burgeropen == false && this.$screen('lg')
@@ -96,6 +109,8 @@ document.addEventListener('alpine:init', () => {
96
109
  }
97
110
 
98
111
  },
112
+
113
+ // returns true if user scrolled >90% and scrolls further down, no burger menu is open and the screen is NOT desktop. If scroll was initiated by script, ignore scroll direction.
99
114
  shouldServiceNavBeHidden() {
100
115
  if(window.userScroll == true) {
101
116
  return (this.percent > 90 && !this.$screen('lg') && this.scrollingDown && this.$store.burgeropen == false)
@@ -103,12 +118,18 @@ document.addEventListener('alpine:init', () => {
103
118
  return (this.percent > 90 && !this.$screen('lg') && this.$store.burgeropen == false)
104
119
  }
105
120
  },
121
+
122
+ //returns true if user scrolled >50% and scrolls further down, no burger menu is open, no serviceNav is open and screen is not larger than mobile. OR: same same, but scrolling up.
106
123
  shouldServiceIconsBeHidden() {
107
124
  return (this.percent > 50 && !this.$screen('md') && this.$store.burgeropen == false && this.$store.serviceNavIsOpen == false && this.scrollingDown == true) || (this.percent > 50 && !this.$screen('md') && this.$store.burgeropen == false && this.$store.serviceNavIsOpen == false && this.scrollingDown == false)
108
125
  },
126
+
127
+ // returns true if user scrolled >50% and scrolls further down and is a desktop viewport
109
128
  shouldFlyoutBeHidden() {
110
129
  return (this.percent > 50 && this.scrollingDown && this.$screen('lg') )
111
130
  },
131
+
132
+ // resets the navigation back to the initial state. Happens f.ex. on resize of window.
112
133
  resetNav() {
113
134
  if(window.innerWidth > 1023) {
114
135
  this.$refs.sectionnavigation.setAttribute("style","")
@@ -143,6 +164,8 @@ document.addEventListener('alpine:init', () => {
143
164
  this.$store.clientWidth = nowClientWidth
144
165
  }
145
166
  },
167
+
168
+ // toggles the maxHeight of the section nav and makes sure there is enough space to display all items.
146
169
  toggleSectionNav() {
147
170
  //false = sectionNav schließt ( mobile/tablet? --> maxHeight = 0 /// desktop? just clear maxHeight attribute )
148
171
  //true = sectionNav öffnet (maxheight = scrollheight)
@@ -168,16 +191,22 @@ document.addEventListener('alpine:init', () => {
168
191
  }
169
192
  }
170
193
  },
194
+
195
+ // no scrolling when overlay is visible
171
196
  disableScrolling() {
172
197
  document.body.classList.add('overflow-hidden','h-full','w-full')
173
198
  this.$refs.myOverlay.ontouchmove = (e) => e.preventDefault();
174
199
  console.log("disableScrolling")
175
200
  },
201
+
202
+ //only scroll when no overlay is visible
176
203
  enableScrolling() {
177
204
  document.body.classList.remove('overflow-hidden','h-full','w-full')
178
205
  this.$refs.myOverlay.ontouchmove = (e) => true;
179
206
  console.log("enableScrolling")
180
207
  },
208
+
209
+ // toggles scrolling ability
181
210
  toggleScrolling(mode){
182
211
  if(this.$screen(0) && !this.$screen('lg')){
183
212
  mode == false ? this.disableScrolling() : this.enableScrolling()
@@ -191,9 +220,13 @@ document.addEventListener('alpine:init', () => {
191
220
 
192
221
  // context for the overlay
193
222
  Alpine.data('overlayHandler', () => ({
223
+
224
+ // show the overlay on mobile and tablet if burger menu is open OR service nav is open OR search field is open
194
225
  shouldOverlayBeShown() {
195
226
  return (!this.$screen('lg') && ( this.$store.burgeropen == true || this.$store.serviceNavIsOpen == true || this.$store.searchFieldOpen == true ))
196
227
  },
228
+
229
+ // on click on overlay change global var for servicenav, dispatch events to close burger and service menu, re-enable scrolling.
197
230
  overlayWasClicked() {
198
231
  this.$store.serviceNavIsOpen ? this.$store.serviceNavIsOpen = false : null
199
232
  this.$dispatch('burger-close')
@@ -203,18 +236,28 @@ document.addEventListener('alpine:init', () => {
203
236
  }
204
237
  }))
205
238
 
206
- // context for all dropdowns
239
+ // context for all dropdowns, used in section nav submenus and service nav flyout submenus
207
240
  Alpine.data('dropdown', () => ({
241
+
242
+ // state of the dropdown
208
243
  dropped: false,
244
+
245
+ // toggle() interpolates state
209
246
  toggle() {
210
247
  this.dropped = ! this.dropped;
211
248
  },
249
+
250
+ // toggles visibility of service nav and sets global variables in stores
212
251
  toggleServiceNav(){
213
252
  this.dropped = ! this.dropped;
253
+
254
+ // close search if open
214
255
  this.$store.searchFieldOpen = false;
215
256
 
257
+ // if clicked element is not the current serviceID, leave the servicenav open, else interpolate servicenav state
216
258
  this.$el.id != this.$store.serviceID.current ? this.$store.serviceNavIsOpen = true : this.$el.id == this.$store.serviceID.current ? this.$store.serviceNavIsOpen = !this.$store.serviceNavIsOpen : null;
217
259
 
260
+ //if burger is open, dispatch event to close it
218
261
  this.$store.burgeropen == true ? this.$dispatch('burger-close') : null
219
262
 
220
263
  console.log('currentID: '+ this.$store.serviceID.current)
@@ -222,10 +265,13 @@ document.addEventListener('alpine:init', () => {
222
265
  console.log('element-id: '+this.$el.id)
223
266
  console.log('serviceNav is open:'+ this.$store.serviceNavIsOpen)
224
267
 
268
+ //set the serviceID to the current element´s ID.
225
269
  this.$store.serviceID.current = this.$el.id
226
270
 
271
+ //enable/disable scrolling
227
272
  this.toggleScrolling(!this.$store.serviceNavIsOpen)
228
273
 
274
+ //defines behaviour for servicenav on mobile viewports, taking care of viewport sizes
229
275
  let myFlyout = document.querySelector('#flyout-'+this.$el.id)
230
276
  let brandNavHeight = this.percent > 0 ? 40 : 0
231
277
 
@@ -252,8 +298,9 @@ document.addEventListener('alpine:init', () => {
252
298
  this.$el.setAttribute("x-collapse","")
253
299
  }
254
300
  },
301
+
302
+ //Adds scrollheight of the flyout to sectionNav container to make sure all following items stay visible
255
303
  sectionNavFlyoutWatcher() {
256
- //Adds scrollheight of the flyout to sectionNav container to make sure all following items stay visible
257
304
  this.$watch('dropped', value => {
258
305
  let a = this.$refs.sectionnavigation.scrollHeight + this.$el.scrollHeight;
259
306
  let brandNavHeight = this.percent > 0 ? 40 : 0
@@ -278,8 +325,9 @@ document.addEventListener('alpine:init', () => {
278
325
 
279
326
  })
280
327
  },
328
+
329
+ //sets/cleansup the x-collapse attributes depending on window.innerWidth, gets fired @resize.window in NavigationFlyout.hbs
281
330
  setFlyoutAnimationStyle() {
282
- //sets/cleansup the x-collapse attributes depending on window.innerWidth, gets fired @resize.window in NavigationFlyout.hbs
283
331
  if(window.innerWidth > 1023) {
284
332
  if(this.$el.hasAttribute("x-collapse.duration.500ms")) {
285
333
  this.$el.removeAttribute("x-collapse.duration.500ms")
@@ -289,7 +337,6 @@ document.addEventListener('alpine:init', () => {
289
337
  if (! this.$el._x_isShown) this.$el.style.display = 'none'
290
338
  if(this.$el.hasAttribute("hidden")) this.$el.removeAttribute("hidden")
291
339
  }
292
-
293
340
  } else {
294
341
  if(!this.$el.hasAttribute("x-collapse.duration.500ms")) this.$el.setAttribute("x-collapse.duration.500ms","")
295
342
  }
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "license": "MIT",
7
7
  "main": "dist/index.js",
8
8
  "repository": "https://github.com/szuelch/hr-design-system-handlebars",
9
- "version": "0.50.1",
9
+ "version": "0.50.2",
10
10
  "scripts": {
11
11
  "test": "echo \"Error: no test specified\" && exit 1",
12
12
  "storybook": "start-storybook -p 6006 public",
@@ -353,6 +353,10 @@
353
353
 
354
354
  /*! purgecss end ignore */
355
355
 
356
+ .sbdocs-content {
357
+ max-width:1140px !important;
358
+ }
359
+
356
360
  .hide-scroll-bar {
357
361
  -ms-overflow-style: none;
358
362
  scrollbar-width: none;
@@ -1,9 +1,10 @@
1
1
  import { ArgsTable, Meta, Story, Canvas, Preview } from '@storybook/addon-docs'
2
-
2
+ import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport'
3
3
  import navigation from './header.hbs'
4
+ import brandnav from './brand_navigation/brand_navigation.hbs'
4
5
  import JsonData from './fixtures/site_header_default.json'
5
6
  import JsonData2 from './fixtures/site_header_mit_warnung.json'
6
- import JsonData3 from './fixtures/site_header_mit_submenu.json'
7
+ import JsonData3 from './fixtures/site_header_mit_submenu.json'
7
8
 
8
9
  <Meta title="Komponenten/Header/Header"
9
10
  argTypes={{}}
@@ -12,7 +13,10 @@ import JsonData3 from './fixtures/site_header_mit_submenu.json'
12
13
  viewports: [360, 768, 1024],
13
14
  },
14
15
  layout: 'fullscreen',
15
- docs: { inlineStories : false, iframeHeight: 400 }
16
+ docs: { inlineStories : false, iframeHeight: 200 },
17
+ viewport: {
18
+ viewports: INITIAL_VIEWPORTS
19
+ },
16
20
  }}
17
21
 
18
22
  />
@@ -25,22 +29,330 @@ export const Template = (args, { globals: { customConditionalToolbar } }) => {
25
29
  return navigation({ brand, ...args })
26
30
  }
27
31
 
32
+
33
+
28
34
  # Header
29
35
 
30
- Ein toller Einleitungstext für unsere `Header` Komponente:
36
+ [Frameworks](#frameworks) - [Varianten](#varianten) - [Scrollverhalten](#scrollverhalten) - [Templates & Codes](#templates) - [Sub-Komponenten](#subkomponenten)
31
37
 
32
38
  <Canvas>
33
- <Story name="Default" args={JsonData}>
39
+ <Story name="Default"
40
+ args={JsonData}
41
+ parameters={{}}>
34
42
  {Template.bind({})}
35
43
  </Story>
36
44
  </Canvas>
37
45
 
46
+ ## Beschreibung
47
+
48
+ Die <b>Header</b>-Komponente ist die Haupt-Navigation der Hessenschau. Sie ist in der Komponente 'Page' eingebunden und besteht grundsätzlich aus 4 Subkomponenten:
49
+
50
+ <ul>
51
+ <li>components/site_header/<b>anchor_navigation</b></li>
52
+ <li>components/site_header/<b>brand_navigation</b></li>
53
+ <li>components/site_header/<b>service_navigation</b></li>
54
+ <li>components/site_header/<b>section_navigation</b></li>
55
+ </ul>
56
+ <br />
57
+ Die Header-Komponente wird in handlebars wie folgt eingebaut:
58
+
59
+ ```html
60
+ {{> components/site_header/header}}
61
+ ```
62
+
63
+ <a name="frameworks" />
64
+
65
+ ### Frameworks
66
+
67
+ Die wesentlichen Funktionen und Features der Navigation sind mit dem Javascript-Framework [**Alpine.js**](https://alpinejs.dev/) realisiert. Alpine regelt u.a. die Sichtbarkeit der einzelnen Elemente und ist für die Interaktivität der Navigation zuständig. Alle Codes, die in der Header-Komponente Alpine.js direkt betreffen, sind in der Datei **"header_alpine.js** ausgelagert. Hier finden sich global verwendete dynamische Variablen (via Alpine.store) und zu verwendende Kontexte (via Alpine.data).
68
+
69
+ Für das CSS wird das Framework [**TailwindCSS**](https://www.tailwindcss.com) verwendet.
70
+
71
+
72
+
73
+ <a name="varianten" />
74
+
75
+ ### Varianten
76
+
77
+ Die Navigation ist sticky und responsiv mit drei Breakpoints für Smartphone, Tablet und Desktop. Für diese drei Viewportgrößen gibt es jeweils unterschiedliche Darstellungen der Subkomponenten innerhalb der Navigation. Grundsätzlich kann die Navigation z.B. in der Anzahl der ServiceNavigation-Items und der Sichtbarkeit von Flyouts und/oder Submenus variieren.
78
+
79
+ Desktop:
80
+ <img src="/images/navi_default.png" />
81
+ <br />
82
+
83
+ ***
84
+
85
+ Tablet:
86
+ <img src="/images/navi_tablet.PNG" />
87
+ <br />
88
+
89
+ ***
90
+
91
+ Mobil:
92
+ <img src="/images/navi_mobil_default.png" />
93
+ <br />
94
+
95
+ ***
96
+ <br />
97
+
98
+
99
+ <a name="scrollverhalten" />
100
+
101
+ ### Scrollverhalten
102
+
103
+ Es gibt 3 Scrollpositionen, die relevant sind: 0%, 50% und 90%. Die Höhe des Viewports wird hierbei als Referenz bzw. 100% angenommen. (Programmatisch wird die Scrollposition in der Variable **"percent"** abgebildet und zur Verarbeitung genutzt) Scrollt man bei ausgeblendeter Navigation die Seite wieder um 1px zurück, wird im Desktop-Viewport die Section-Navigation wieder eingeblendet, im Tablet und Mobil-Viewport kommt je nach Scrollposition die Navigation wieder in den sichtbaren Bereich bzw. die Service-List mit den Service-Icons wird wieder eingeblendet. Die Brand-Navigation wird erst wieder angezeigt, wenn ganz nach oben gescrollt wird.
104
+
105
+ | Scrollposition | Viewport | Beschreibung |
106
+ |:---------------|:---------|:-------------|
107
+ |`>0%`| alle Viewports | Brand-Navigation wird ausgeblendet |
108
+ |`>50%`| Desktop | Section-Navigation wird ausgeblendet & Logo verkleinert |
109
+ || Tablet | Logo wird verkleinert |
110
+ || Mobil | Logo wird verkleinert & ServiceList wird ausgeblendet |
111
+ |`>90%`| Desktop | keine weitere Änderung |
112
+ || Tablet | Navigation verschwindet ganz |
113
+ || Mobil | Navigation verschwindet ganz |
114
+
115
+ <br />
116
+
117
+ **Desktop >0%:**
118
+ <img src="/images/navi_scroll_50percent.png" />
119
+
120
+ **Desktop >50%:**
121
+ <img src="/images/navi_scroll_90percent.png" />
122
+ <br />
123
+
124
+ ***
125
+
126
+ **Tablet > 0% / < 50%:**
127
+ <img src="/images/navi_tablet_scroll_1percent.PNG" />
128
+ <br />
129
+
130
+ **Tablet > 50% / < 90%:**
131
+ <img src="/images/navi_tablet_scroll_50percent.PNG" />
132
+ <br />
133
+
134
+ ***
135
+
136
+ **Mobil >0% / < 50%:**
137
+ <img src="/images/navi_mobil_scroll_1percent.png" />
138
+ <br />
139
+
140
+ **Mobil >50% / < 90%:**
141
+ <img src="/images/navi_mobil_scroll_50percent.PNG" />
142
+ <br />
143
+
144
+ ***
145
+
146
+
147
+
148
+ <br />
149
+ <a href="#top">Back to top</a>
150
+ <a name="templates" />
151
+
152
+ ### Template & Codes
153
+
154
+ Im Haupt-Template **header.hbs** wird für den umschließenden Hauptcontainer mittels der Alpine-Direktive **"x-data"** der Kontext auf das Data-Objekt **"mainNavigationHandler()"** gesetzt und beim erstmaligen Initialisieren die Unter-Funktion **"init()"** aufgerufen. Durch den angegebenen Kontext stehen innerhalb der Navigation nun alle Unterfunktionen von mainNavigationHandler() bereit. Hier finden sich neben der "init()"-Funktion z.B. auch alle Hilfsfunktionen, die zur Ermittlung der Scrollposition bzw. der Sichtbarkeit der jeweiligen Elemente benötig werden.
155
+
156
+ ```html
157
+ <div class="sb-main-navigation" x-data="mainNavigationHandler()" x-init="init()">
158
+ ```
159
+
160
+ Innerhalb von "init()" werden die Listener und deren Handler für das Scrollen via Scrollrad oder Mouse- bzw. Touch-Aktionen definiert:
161
+
162
+ ```javascript
163
+ init(){
164
+ let lastScrollTop = 0
165
+ let height = window.innerHeight
166
+ let userScroll = false;
167
+ window.userScroll = userScroll;
168
+
169
+ const mouseEvent = () => {
170
+ userScroll = true;
171
+ window.userScroll = true;
172
+ }
173
+ const clickedOnScrollbar = mouseX => {
174
+ return document.documentElement.offsetWidth <= mouseX ? true : false;
175
+ }
176
+ const mouseDownHandler = e => {
177
+ clickedOnScrollbar(e.clientX) ? mouseEvent() : null
178
+ };
179
+ const scrollHandler = () => {
180
+ let winScroll = document.body.scrollTop || document.documentElement.scrollTop
181
+ winScroll > lastScrollTop ? this.scrollingDown = true : this.scrollingDown = false
182
+ this.percent = Math.round((winScroll / height) * 100)
183
+ lastScrollTop = winScroll
184
+ this.$store.navIsVisible = !this.isNavHidden()
185
+ this.$store.subNavIsVisible = !this.isSubNavHidden()
186
+ }
187
+ window.addEventListener('mousedown', mouseDownHandler, false)
188
+ window.addEventListener('wheel', mouseEvent, false);
189
+ window.addEventListener('touchmove', mouseEvent, false)
190
+ window.addEventListener('scroll', this.debounce( scrollHandler,50), { passive: true })
191
+ }
192
+ ```
193
+
194
+ *Beispielhaft wird hier eine der weiteren Hilfsfunktionen aus dem mainNavigationHandler() erklärt:*
195
+
196
+ Innerhalb des serviceNavWrappers wird mit Alpine die Function "shouldServiceNavBeHidden()" zum Setzen der korrekten CSS-Klassen via Ternary Expression verwendet: Wenn shouldServiceNavBeHidden() den Wert '_true_' zurückgibt, wird die CSS-Klasse "-mt-40" gesetzt, welche den Wrapper mit einem negativen Margin von -10rem aus dem Viewport schiebt:
197
+
198
+ **header.hbs:**
199
+
200
+ ```html
201
+ <div id="serviceNavWrapper"
202
+ :class="shouldServiceNavBeHidden() ? '-mt-40' : ''"
203
+ ```
204
+ WENN _percent > 90_ ist, der _Viewport NICHT Desktop_ ist, die _Scrollrichtung nach unten_ ist und es _kein offenes Burger-Menu_ gibt, sendet die Funktion *true* als Rückgabewert zurück. Wenn die Seite _via JS_ (z.B. durch einen Ankerlink) gescrollt wird, verstecke die ServiceNavigation unter den gleichen Bedingungen, aber _unabhängig von der Scrollrichtung_:
205
+
206
+ **header_alpine.js:**
207
+
208
+ ```js
209
+ shouldServiceNavBeHidden() {
210
+ if(window.userScroll == true) {
211
+ return (this.percent > 90 && !this.$screen('lg') && this.scrollingDown && this.$store.burgeropen == false)
212
+ } else {
213
+ return (this.percent > 90 && !this.$screen('lg') && this.$store.burgeropen == false)
214
+ }
215
+ }
216
+ ```
217
+
218
+
219
+ ***
220
+
221
+ Weiterhin sind in der Datei **header_alpine.js** weitere Alpine.data-Kontexte für das Overlay, die Dropdown-Menus und die Flyouts hinterlegt.
222
+ Für die Erklärung können die Kommentare direkt in der Datei herangezogen werden.
223
+
224
+ <a href="#top">Back to top</a>
225
+ <br />
226
+ <a name="subkomponenten" />
227
+
228
+ ### Sub-Komponenten
229
+ <br />
230
+
231
+ <b>Anchor Navigation</b> <br/>
232
+
233
+ **anchor_navigation.hbs** Inkludiert folgende Subkomponenten:
234
+
235
+ * featurebox_anchor.hbs
236
+
237
+ In dieser (nicht sichtbaren) Sub-Komponente werden Anchorlinks zu den Teilbereichen der Navigation bereitgestellt, um sie für Screen-Reader direkt erreichbar zu machen.
238
+
239
+ **header.hbs:**
240
+ ```html
241
+ <div id="anchorNavWrapper" class="hidden">
242
+ {{> components/site_header/anchor_navigation}}
243
+ </div>
244
+ ```
245
+
246
+ <hr/>
247
+ <br/>
248
+ <b>Brand Navigation</b> <br/>
249
+
250
+ **brand_navigation.hbs** inkludiert folgende Subkomponenten:
251
+
252
+ * brand_navigation_item
253
+
254
+ Die Brand-Navigation stellt im obersten Bereich des Headers direkte Links zu den einzelnen Angeboten des HR zur Verfügung.
255
+
256
+ ```html
257
+ <div id="brandNavWrapper" class="relative flex items-center justify-center order-1 w-full bg-white z-10000 print:hidden">
258
+ {{> components/site_header/brand_navigation/brand_navigation }}
259
+ </div>
260
+ ```
261
+
262
+ <hr/>
263
+ <br/>
264
+ <b>Service Navigation</b>
265
+
266
+ **service_navigation.hbs** inkludiert folgende Subkomponenten:
267
+
268
+ * service_logo.hbs
269
+ * service_list.hbs
270
+ * service_navigation_item.hbs
271
+ * navigation_flyout.hbs
272
+ * navigation_search.hbs
273
+ * burger.hbs
274
+ <br/>
275
+
276
+ Die Service-Navigation beinhaltet das Logo **(service_logo.hbs)**, die Service-Items und die Suchfunktion **(navigation_search.hbs)**, sowie bei mobilen Viewports das Burger-Menu **(burger.hbs)**, welches die mobile Variante der **SectionNavigation** darstellt.<br />
277
+ Die gesamte ServiceNavigation liegt innerhalb des **"serviceNavWrapper"**. Bei mobilem Viewport wird dieser Wrapper beim Scrollen der Seite mit Hilfe der Funktion **"shouldServiceNavBeHidden()"** ein- bzw. ausgeblendet.
278
+
279
+ Innerhalb des Hauptwrappers liegt der **"serviceNavMainContainer"** welcher die serviceNavHeadline, einen Wrapper für das Logo sowie den **"serviceItemsWrapper"** beinhaltet. In diesem wiederum liegt die Service-List **(service_list.hbs)** und die Komponente für die Suchfunktion **(navigation_search.hbs)**.
280
+
281
+ **header.hbs:**
282
+
283
+ ```html
284
+ <div id="serviceNavWrapper" :class="shouldServiceNavBeHidden() ? '-mt-40' : ''"
285
+ class="relative flex justify-center order-2 w-full transition-all duration-500 ease-in-out md:border-white lg:border-b bg-blue-congress z-10002">
286
+ <div id="serviceNavMainContainer" class="flex w-full h-10 lg:container md:h-12 lg:px-10 lg:h-16 z-10001">
287
+ <span id="serviceNavHeadline" class="hidden print:hidden">Service Navigation</span>
288
+ <div id="serviceLogoWrapper" class="flex items-center order-1 w-full pl-4 pr-2 tablet:pl-5 lg:items-end lg:pb-3 bg-blue-congress md:px-0 md:h-12 lg:h-16 md:w-1/2 md:max-w-1/2 lg:w-1/4 lg:max-w-1/4">
289
+ {{> components/site_header/service_logo }}
290
+ </div>
291
+ <div id="serviceItemsWrapper" class="flex items-center justify-end flex-initial order-2 inline-block w-full max-w-full align-top bg-blue-congress md:h-12 lg:h-16 lg:order-2 lg:w-3/4 lg:max-w-3/4 md:mt-0 md:w-1/2 md:max-w-1/2 md:order-2 md:border-0 print:hidden ">
292
+
293
+ {{> components/site_header/service_navigation/service_list }}
294
+ {{> components/site_header/navigation_search/quick_search_button }}
295
+
296
+ <div class="hidden lg:flex">
297
+ {{> components/site_header/navigation_search/quick_search_form }}
298
+ </div>
299
+ <div id="burgerWrapper" class="flex justify-end flex-none order-2 lg:order-4 md:order-4 lg:hidden ">
300
+ {{> components/site_header/burger }}
301
+ </div>
302
+ </div>
303
+ </div>
304
+ </div>
305
+ ```
306
+
307
+ Innerhalb der Komponente **'service_list'** befinden sich die Service-Navigation-Items. Es existieren derzeit folgende Items: "Warnung", "Video/Podcast", "Verkehr", "Wetter". Das "Warnung"-Item führt direkt zu einer Verlinkten Seite. Ein Click auf die anderen Items öffnet jeweils ein Flyout, in welchem Links zu den Unterseiten des jeweiligen Strukturknotens/Bereichs oder externen Zielen dargstellt sind.
308
+ Innerhalb der Service-List können mittels der Funktion **"shouldServiceIconsBeHidden()"** je nach Scrollposition die Service-Icons ausgeblendet werden.
309
+ <br />
310
+
311
+ <img src="/images/navi_flyout_service.png" />
312
+ <br />
313
+ <br />
314
+ <a href="#top">Back to top</a>
315
+ <br />
316
+
317
+ ***
318
+ <br />
319
+ <b>Section Navigation</b>
320
+
321
+ **section_navigation.hbs** inkludiert folgende Subkomponenten:
322
+
323
+ * section_navigation_item.hbs
324
+ * navigation_flyout.hbs
325
+
326
+ <br/>
327
+ Die Section-Navigation stellt die Links zu den einzelnen Themenbereichen der Hessenschau bereit. Die Navigation-Items der Section Navigation können je nach Einstellung vom CMS direkt zur Themen-Indexseite verlinken oder ihrerseits ein Submenu öffnen, über dessen Einträge dann Themenbereiche angesteuert werden können:
328
+ <br />
329
+ <br />
330
+ <img src="/images/navi_flyout_section.png" />
331
+ <br/><br/>
332
+ Bei den Viewports "mobil" und "tablet" wird die Section-Navigation in Form eines Burger-Menus dargstellt:
333
+ <br />
334
+ <br />
335
+ <img src="/images/navi_mobil_burger_open.png" />
336
+ <br/><br/>
337
+ <hr/>
338
+ <br />
339
+ <a href="#top">Back to top</a>
340
+
341
+
342
+ ### Header-Komponente mit Warnung-Item in der ServiceList:
343
+
344
+ Hier ist die Navigation mit zusätzlichem Service-Item zu sehen.
345
+
38
346
  <Canvas>
39
347
  <Story name="Mit Warnung" args={JsonData2}>
40
348
  {Template.bind({})}
41
349
  </Story>
42
350
  </Canvas>
43
351
 
352
+ ### Header-Komponente mit Subnavigation:
353
+
354
+ Hier ist die Navigation mit zusätzlicher Subnavigation zu sehen.
355
+
44
356
  <Canvas>
45
357
  <Story name="Mit Subnavigation" args={JsonData3}>
46
358
  {Template.bind({})}
@@ -32,23 +32,25 @@ document.addEventListener('alpine:init', () => {
32
32
  init(){
33
33
  let lastScrollTop = 0
34
34
  let height = window.innerHeight
35
+
36
+ //Globale Variable, true = user initiated scroll / false = programmatic scroll via JS (e.g. click on Anchor Link)
35
37
  let userScroll = false;
36
38
  window.userScroll = userScroll;
37
39
 
40
+ // gets fired when user initated scroll happened, global variable is used in Ticker-Topnews and other anchor links to prevent expanding of the navigation if scrollposition gets corrected by JS.
38
41
  const mouseEvent = () => {
39
42
  userScroll = true;
40
43
  window.userScroll = true;
41
- //console.log('user action detected')
42
44
  }
43
-
45
+ // detect if the user clicked/dragged the scrollbar manually
44
46
  const clickedOnScrollbar = mouseX => {
45
47
  return document.documentElement.offsetWidth <= mouseX ? true : false;
46
48
  }
47
-
49
+ // if clicked on scrollbar, fire user initiated mouse event
48
50
  const mouseDownHandler = e => {
49
51
  clickedOnScrollbar(e.clientX) ? mouseEvent() : null
50
52
  };
51
-
53
+ // main scroll handler, defines scroll direction, percent of viewport scrolled, visibility of navigation and subnavigation
52
54
  const scrollHandler = () => {
53
55
  let winScroll = document.body.scrollTop || document.documentElement.scrollTop
54
56
  winScroll > lastScrollTop ? this.scrollingDown = true : this.scrollingDown = false
@@ -59,14 +61,19 @@ document.addEventListener('alpine:init', () => {
59
61
  //console.log('winscroll: '+winScroll+' screen height: '+height + ' percent scrolled: '+ this.percent)
60
62
  //console.log('Scroll initiated by ' + (window.userScroll == true ? "user" : "browser"));
61
63
  }
62
-
64
+ // Listeners
63
65
  window.addEventListener('mousedown', mouseDownHandler, false)
64
66
  window.addEventListener('wheel', mouseEvent, false);
65
67
  window.addEventListener('touchmove', mouseEvent, false)
66
68
  window.addEventListener('scroll', this.debounce( scrollHandler,50), { passive: true })
67
69
  },
70
+ //Holds the percentage of scrolled viewport
68
71
  percent: 0,
72
+
73
+ //defines the scroll direction
69
74
  scrollingDown: true,
75
+
76
+ //returns true if section navigation is hidden on desktop OR service navigation is hidden on mobile
70
77
  isNavHidden() {
71
78
  if(this.$screen('lg')) {
72
79
  return this.shouldSectionNavBeHidden()
@@ -74,6 +81,8 @@ document.addEventListener('alpine:init', () => {
74
81
  return this.shouldServiceNavBeHidden()
75
82
  }
76
83
  },
84
+
85
+ //returns false if subnav is visible and true if subnav is hidden
77
86
  isSubNavHidden() {
78
87
  if(this.$screen('lg')){
79
88
  if (document.querySelector('.isSelectedAndOpen') !== null) {
@@ -85,9 +94,13 @@ document.addEventListener('alpine:init', () => {
85
94
  return true
86
95
  }
87
96
  },
97
+
98
+ // returns true if the user scrolled at least 1px from top
88
99
  shouldBrandNavBeHidden() {
89
100
  return this.percent > 0
90
101
  },
102
+
103
+ // returns true if user scrolled >50% and scrolls down, no burger menu is open and the screen size is desktop. If scroll was initiated by script, ignore scroll direction.
91
104
  shouldSectionNavBeHidden() {
92
105
  if(window.userScroll == true){
93
106
  return this.percent > 50 && this.scrollingDown && this.$store.burgeropen == false && this.$screen('lg')
@@ -96,6 +109,8 @@ document.addEventListener('alpine:init', () => {
96
109
  }
97
110
 
98
111
  },
112
+
113
+ // returns true if user scrolled >90% and scrolls further down, no burger menu is open and the screen is NOT desktop. If scroll was initiated by script, ignore scroll direction.
99
114
  shouldServiceNavBeHidden() {
100
115
  if(window.userScroll == true) {
101
116
  return (this.percent > 90 && !this.$screen('lg') && this.scrollingDown && this.$store.burgeropen == false)
@@ -103,12 +118,18 @@ document.addEventListener('alpine:init', () => {
103
118
  return (this.percent > 90 && !this.$screen('lg') && this.$store.burgeropen == false)
104
119
  }
105
120
  },
121
+
122
+ //returns true if user scrolled >50% and scrolls further down, no burger menu is open, no serviceNav is open and screen is not larger than mobile. OR: same same, but scrolling up.
106
123
  shouldServiceIconsBeHidden() {
107
124
  return (this.percent > 50 && !this.$screen('md') && this.$store.burgeropen == false && this.$store.serviceNavIsOpen == false && this.scrollingDown == true) || (this.percent > 50 && !this.$screen('md') && this.$store.burgeropen == false && this.$store.serviceNavIsOpen == false && this.scrollingDown == false)
108
125
  },
126
+
127
+ // returns true if user scrolled >50% and scrolls further down and is a desktop viewport
109
128
  shouldFlyoutBeHidden() {
110
129
  return (this.percent > 50 && this.scrollingDown && this.$screen('lg') )
111
130
  },
131
+
132
+ // resets the navigation back to the initial state. Happens f.ex. on resize of window.
112
133
  resetNav() {
113
134
  if(window.innerWidth > 1023) {
114
135
  this.$refs.sectionnavigation.setAttribute("style","")
@@ -143,6 +164,8 @@ document.addEventListener('alpine:init', () => {
143
164
  this.$store.clientWidth = nowClientWidth
144
165
  }
145
166
  },
167
+
168
+ // toggles the maxHeight of the section nav and makes sure there is enough space to display all items.
146
169
  toggleSectionNav() {
147
170
  //false = sectionNav schließt ( mobile/tablet? --> maxHeight = 0 /// desktop? just clear maxHeight attribute )
148
171
  //true = sectionNav öffnet (maxheight = scrollheight)
@@ -168,16 +191,22 @@ document.addEventListener('alpine:init', () => {
168
191
  }
169
192
  }
170
193
  },
194
+
195
+ // no scrolling when overlay is visible
171
196
  disableScrolling() {
172
197
  document.body.classList.add('overflow-hidden','h-full','w-full')
173
198
  this.$refs.myOverlay.ontouchmove = (e) => e.preventDefault();
174
199
  console.log("disableScrolling")
175
200
  },
201
+
202
+ //only scroll when no overlay is visible
176
203
  enableScrolling() {
177
204
  document.body.classList.remove('overflow-hidden','h-full','w-full')
178
205
  this.$refs.myOverlay.ontouchmove = (e) => true;
179
206
  console.log("enableScrolling")
180
207
  },
208
+
209
+ // toggles scrolling ability
181
210
  toggleScrolling(mode){
182
211
  if(this.$screen(0) && !this.$screen('lg')){
183
212
  mode == false ? this.disableScrolling() : this.enableScrolling()
@@ -191,9 +220,13 @@ document.addEventListener('alpine:init', () => {
191
220
 
192
221
  // context for the overlay
193
222
  Alpine.data('overlayHandler', () => ({
223
+
224
+ // show the overlay on mobile and tablet if burger menu is open OR service nav is open OR search field is open
194
225
  shouldOverlayBeShown() {
195
226
  return (!this.$screen('lg') && ( this.$store.burgeropen == true || this.$store.serviceNavIsOpen == true || this.$store.searchFieldOpen == true ))
196
227
  },
228
+
229
+ // on click on overlay change global var for servicenav, dispatch events to close burger and service menu, re-enable scrolling.
197
230
  overlayWasClicked() {
198
231
  this.$store.serviceNavIsOpen ? this.$store.serviceNavIsOpen = false : null
199
232
  this.$dispatch('burger-close')
@@ -203,18 +236,28 @@ document.addEventListener('alpine:init', () => {
203
236
  }
204
237
  }))
205
238
 
206
- // context for all dropdowns
239
+ // context for all dropdowns, used in section nav submenus and service nav flyout submenus
207
240
  Alpine.data('dropdown', () => ({
241
+
242
+ // state of the dropdown
208
243
  dropped: false,
244
+
245
+ // toggle() interpolates state
209
246
  toggle() {
210
247
  this.dropped = ! this.dropped;
211
248
  },
249
+
250
+ // toggles visibility of service nav and sets global variables in stores
212
251
  toggleServiceNav(){
213
252
  this.dropped = ! this.dropped;
253
+
254
+ // close search if open
214
255
  this.$store.searchFieldOpen = false;
215
256
 
257
+ // if clicked element is not the current serviceID, leave the servicenav open, else interpolate servicenav state
216
258
  this.$el.id != this.$store.serviceID.current ? this.$store.serviceNavIsOpen = true : this.$el.id == this.$store.serviceID.current ? this.$store.serviceNavIsOpen = !this.$store.serviceNavIsOpen : null;
217
259
 
260
+ //if burger is open, dispatch event to close it
218
261
  this.$store.burgeropen == true ? this.$dispatch('burger-close') : null
219
262
 
220
263
  console.log('currentID: '+ this.$store.serviceID.current)
@@ -222,10 +265,13 @@ document.addEventListener('alpine:init', () => {
222
265
  console.log('element-id: '+this.$el.id)
223
266
  console.log('serviceNav is open:'+ this.$store.serviceNavIsOpen)
224
267
 
268
+ //set the serviceID to the current element´s ID.
225
269
  this.$store.serviceID.current = this.$el.id
226
270
 
271
+ //enable/disable scrolling
227
272
  this.toggleScrolling(!this.$store.serviceNavIsOpen)
228
273
 
274
+ //defines behaviour for servicenav on mobile viewports, taking care of viewport sizes
229
275
  let myFlyout = document.querySelector('#flyout-'+this.$el.id)
230
276
  let brandNavHeight = this.percent > 0 ? 40 : 0
231
277
 
@@ -252,8 +298,9 @@ document.addEventListener('alpine:init', () => {
252
298
  this.$el.setAttribute("x-collapse","")
253
299
  }
254
300
  },
301
+
302
+ //Adds scrollheight of the flyout to sectionNav container to make sure all following items stay visible
255
303
  sectionNavFlyoutWatcher() {
256
- //Adds scrollheight of the flyout to sectionNav container to make sure all following items stay visible
257
304
  this.$watch('dropped', value => {
258
305
  let a = this.$refs.sectionnavigation.scrollHeight + this.$el.scrollHeight;
259
306
  let brandNavHeight = this.percent > 0 ? 40 : 0
@@ -278,8 +325,9 @@ document.addEventListener('alpine:init', () => {
278
325
 
279
326
  })
280
327
  },
328
+
329
+ //sets/cleansup the x-collapse attributes depending on window.innerWidth, gets fired @resize.window in NavigationFlyout.hbs
281
330
  setFlyoutAnimationStyle() {
282
- //sets/cleansup the x-collapse attributes depending on window.innerWidth, gets fired @resize.window in NavigationFlyout.hbs
283
331
  if(window.innerWidth > 1023) {
284
332
  if(this.$el.hasAttribute("x-collapse.duration.500ms")) {
285
333
  this.$el.removeAttribute("x-collapse.duration.500ms")
@@ -289,7 +337,6 @@ document.addEventListener('alpine:init', () => {
289
337
  if (! this.$el._x_isShown) this.$el.style.display = 'none'
290
338
  if(this.$el.hasAttribute("hidden")) this.$el.removeAttribute("hidden")
291
339
  }
292
-
293
340
  } else {
294
341
  if(!this.$el.hasAttribute("x-collapse.duration.500ms")) this.$el.setAttribute("x-collapse.duration.500ms","")
295
342
  }