@internetarchive/ia-topnav 1.3.30 → 1.4.1-alpha-webdev8259.0

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.
Files changed (53) hide show
  1. package/.prettierignore +1 -1
  2. package/LICENSE +661 -661
  3. package/README.md +147 -147
  4. package/demo/index.html +28 -28
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/src/data/menus.js.map +1 -1
  8. package/dist/src/dropdown-menu.js +26 -26
  9. package/dist/src/dropdown-menu.js.map +1 -1
  10. package/dist/src/ia-topnav.d.ts +4 -1
  11. package/dist/src/ia-topnav.js +96 -81
  12. package/dist/src/ia-topnav.js.map +1 -1
  13. package/dist/src/lib/keyboard-navigation.js.map +1 -1
  14. package/dist/src/login-button.js +17 -17
  15. package/dist/src/login-button.js.map +1 -1
  16. package/dist/src/media-menu.js +21 -21
  17. package/dist/src/media-menu.js.map +1 -1
  18. package/dist/src/models.d.ts +1 -0
  19. package/dist/src/models.js.map +1 -1
  20. package/dist/src/primary-nav.d.ts +3 -1
  21. package/dist/src/primary-nav.js +118 -95
  22. package/dist/src/primary-nav.js.map +1 -1
  23. package/dist/src/styles/login-button.js +87 -87
  24. package/dist/src/styles/login-button.js.map +1 -1
  25. package/dist/src/styles/primary-nav.js +343 -308
  26. package/dist/src/styles/primary-nav.js.map +1 -1
  27. package/dist/src/user-menu.js +13 -13
  28. package/dist/src/user-menu.js.map +1 -1
  29. package/dist/test/ia-topnav.test.js +39 -9
  30. package/dist/test/ia-topnav.test.js.map +1 -1
  31. package/dist/test/primary-nav.test.js +55 -7
  32. package/dist/test/primary-nav.test.js.map +1 -1
  33. package/eslint.config.mjs +53 -53
  34. package/index.ts +4 -3
  35. package/package.json +72 -72
  36. package/prettier.config.js +9 -9
  37. package/src/data/menus.ts +652 -652
  38. package/src/dropdown-menu.ts +132 -132
  39. package/src/ia-topnav.ts +383 -366
  40. package/src/lib/keyboard-navigation.ts +166 -166
  41. package/src/login-button.ts +78 -78
  42. package/src/media-menu.ts +143 -143
  43. package/src/models.ts +65 -63
  44. package/src/primary-nav.ts +324 -296
  45. package/src/styles/login-button.ts +90 -90
  46. package/src/styles/primary-nav.ts +346 -311
  47. package/src/user-menu.ts +32 -32
  48. package/ssl/server.key +28 -28
  49. package/test/ia-topnav.test.ts +381 -343
  50. package/test/primary-nav.test.ts +163 -94
  51. package/tsconfig.json +31 -31
  52. package/web-dev-server.config.mjs +32 -32
  53. package/web-test-runner.config.mjs +41 -41
@@ -1,296 +1,324 @@
1
- import { html, nothing, PropertyValues } from 'lit';
2
- import TrackedElement from './tracked-element';
3
- import icons from './assets/img/icons';
4
- import './assets/img/hamburger';
5
- import './login-button';
6
- import './nav-search';
7
- import './media-menu';
8
- import logoWordmarkStacked from './assets/img/wordmark-stacked';
9
- import primaryNavCSS from './styles/primary-nav';
10
- import locationHandler from './lib/location-handler';
11
- import formatUrl from './lib/format-url';
12
- import { customElement, property } from 'lit/decorators.js';
13
- import { IATopNavConfig, IATopNavSecondIdentitySlotMode } from './models';
14
- import { defaultTopNavConfig } from './data/menus';
15
-
16
- @customElement('primary-nav')
17
- export class PrimaryNav extends TrackedElement {
18
- @property({ type: String }) mediaBaseHost = 'https://archive.org';
19
- @property({ type: String }) baseHost = '';
20
- @property({ type: Boolean }) hideSearch = false;
21
- @property({ type: Object }) config: IATopNavConfig = defaultTopNavConfig;
22
- @property({ type: String }) openMenu = '';
23
- @property({ type: String }) screenName = '';
24
- @property({ type: String }) searchIn = '';
25
- @property({ type: String }) searchQuery = '';
26
- @property({ type: String })
27
- secondIdentitySlotMode: IATopNavSecondIdentitySlotMode = '';
28
- @property({ type: String }) selectedMenuOption = '';
29
- @property({ type: Boolean }) signedOutMenuOpen = false;
30
- @property({ type: Boolean }) userMenuOpen = false;
31
- @property({ type: Boolean }) mediaMenuAnimate = false;
32
- @property({ type: String }) username = '';
33
- @property({ type: String }) userProfileImagePath = '';
34
- @property({ type: Object }) currentTab:
35
- | { mediatype: string; moveTo: string }
36
- | undefined;
37
- signedOutMenuToggled: unknown;
38
-
39
- static get styles() {
40
- return primaryNavCSS;
41
- }
42
-
43
- toggleMediaMenu(e: Event) {
44
- this.trackClick(e);
45
- this.dispatchEvent(
46
- new CustomEvent('menuToggled', {
47
- detail: {
48
- menuName: 'media',
49
- },
50
- }),
51
- );
52
- }
53
-
54
- toggleSearchMenu(e: Event) {
55
- this.trackClick(e);
56
- this.dispatchEvent(
57
- new CustomEvent('menuToggled', {
58
- detail: {
59
- menuName: 'search',
60
- },
61
- }),
62
- );
63
- }
64
-
65
- toggleUserMenu(e: Event) {
66
- this.trackClick(e);
67
- this.dispatchEvent(
68
- new CustomEvent('menuToggled', {
69
- detail: {
70
- menuName: 'user',
71
- },
72
- }),
73
- );
74
- }
75
-
76
- updated(props: PropertyValues) {
77
- if (props.has('currentTab')) {
78
- // early return
79
- if (!this.currentTab || Object.keys(this.currentTab).length === 0)
80
- return nothing;
81
-
82
- const isUserMenuTab =
83
- this.currentTab && this.currentTab.mediatype === 'usermenu';
84
- if (isUserMenuTab) {
85
- const mediaButtons = Array.from(
86
- this.shadowRoot
87
- ?.querySelector('media-menu')
88
- ?.shadowRoot?.querySelectorAll('media-button') ?? [],
89
- );
90
- const lastMediaButton = mediaButtons.filter((element) => {
91
- return element.shadowRoot
92
- ?.querySelector('a')
93
- ?.classList.contains('images');
94
- });
95
-
96
- let nextElement;
97
- if (this.username) {
98
- nextElement = this.shadowRoot?.querySelector('a.upload');
99
- } else {
100
- nextElement = this.shadowRoot
101
- ?.querySelector('login-button')
102
- ?.shadowRoot?.querySelector('span a');
103
- }
104
-
105
- const menuItemElement =
106
- lastMediaButton[0]?.shadowRoot?.querySelector('a.menu-item');
107
-
108
- const focusElement =
109
- this.currentTab.moveTo === 'next' ? nextElement : menuItemElement;
110
-
111
- if (focusElement) {
112
- (focusElement as HTMLElement).focus();
113
- }
114
- } else if (this.currentTab.moveTo === 'next') {
115
- if (this.shadowRoot?.querySelector('.user-menu')) {
116
- (this.shadowRoot?.querySelector('.user-menu') as HTMLElement).focus();
117
- } else {
118
- (
119
- this.shadowRoot
120
- ?.querySelector('login-button')
121
- ?.shadowRoot?.querySelectorAll('span a')[0] as HTMLElement
122
- )?.focus();
123
- }
124
- }
125
- }
126
- }
127
-
128
- get userIcon() {
129
- const userMenuClass = this.openMenu === 'user' ? 'active' : '';
130
- const userMenuToolTip =
131
- this.openMenu === 'user' ? 'Close user menu' : 'Expand user menu';
132
-
133
- return html`
134
- <button
135
- class="user-menu ${userMenuClass}"
136
- title="${userMenuToolTip}"
137
- @click="${this.toggleUserMenu}"
138
- data-event-click-tracking="${this.config?.eventCategory}|NavUserMenu"
139
- >
140
- <img
141
- src="${this.mediaBaseHost}${this.userProfileImagePath}"
142
- alt="Profile picture for ${this.screenName}"
143
- />
144
- <span class="screen-name" dir="auto">${this.screenName}</span>
145
- </button>
146
- `;
147
- }
148
-
149
- get loginIcon() {
150
- return html`
151
- <login-button
152
- .baseHost=${this.baseHost}
153
- .config=${this.config}
154
- .dropdownOpen=${this.signedOutMenuOpen}
155
- .openMenu=${this.openMenu}
156
- @signedOutMenuToggled=${this.signedOutMenuToggled}
157
- ></login-button>
158
- `;
159
- }
160
-
161
- get searchMenuOpen() {
162
- return this.openMenu === 'search';
163
- }
164
-
165
- get allowSecondaryIcon() {
166
- return this.secondIdentitySlotMode === 'allow';
167
- }
168
-
169
- get searchMenu() {
170
- if (this.hideSearch) return nothing;
171
-
172
- return html`
173
- <button
174
- class="search-trigger"
175
- @click="${this.toggleSearchMenu}"
176
- data-event-click-tracking="${this.config?.eventCategory}|NavSearchOpen"
177
- >
178
- ${icons.search}
179
- </button>
180
- <nav-search
181
- .baseHost=${this.baseHost}
182
- .config=${this.config}
183
- .locationHandler=${locationHandler}
184
- .open=${this.searchMenuOpen}
185
- .openMenu=${this.openMenu}
186
- .searchIn=${this.searchIn}
187
- .searchQuery=${this.searchQuery}
188
- @blur=${this.emitNavSearchBlurEvent}
189
- ></nav-search>
190
- `;
191
- }
192
-
193
- private emitNavSearchBlurEvent(e: FocusEvent) {
194
- const relatedTarget = e.relatedTarget as HTMLElement;
195
- const isUploadButton = relatedTarget?.classList.contains('upload');
196
-
197
- if (isUploadButton) {
198
- (this.shadowRoot?.querySelector('a.upload') as HTMLElement).focus();
199
- }
200
-
201
- this.dispatchEvent(
202
- new CustomEvent('navSearchBlur', {
203
- detail: {
204
- isUploadButton: !!isUploadButton,
205
- },
206
- bubbles: true,
207
- composed: true,
208
- }),
209
- );
210
- }
211
-
212
- get mobileDonateHeart() {
213
- return html`
214
- <a
215
- class="mobile-donate-link"
216
- .href=${formatUrl(
217
- '/donate/?origin=iawww-mbhrt' as string & Location,
218
- this.baseHost,
219
- )}
220
- >
221
- ${icons.donateUnpadded}
222
- <span class="sr-only">"Donate to the archive"</span>
223
- </a>
224
- `;
225
- }
226
-
227
- get uploadButtonTemplate() {
228
- return html` <a
229
- .href="${formatUrl('/upload' as string & Location, this.baseHost)}"
230
- class="upload"
231
- @focus=${this.toggleMediaMenu}
232
- >
233
- ${icons.upload}
234
- <span>Upload</span>
235
- </a>`;
236
- }
237
-
238
- get userStateTemplate() {
239
- return html`<div class="user-info">
240
- ${this.username ? this.userIcon : this.loginIcon}
241
- </div>`;
242
- }
243
-
244
- get secondLogoSlot() {
245
- return this.allowSecondaryIcon
246
- ? html`
247
- <slot name="opt-sec-logo"></slot>
248
- <slot name="opt-sec-logo-mobile"></slot>
249
- `
250
- : nothing;
251
- }
252
-
253
- get secondLogoClass() {
254
- return this.allowSecondaryIcon ? 'second-logo' : '';
255
- }
256
-
257
- render() {
258
- // const mediaMenuTabIndex = this.openMenu === 'media' ? '' : '-1';
259
- return html`
260
- <nav class=${this.hideSearch ? 'hide-search' : ''}>
261
- <button
262
- class="hamburger"
263
- @click="${this.toggleMediaMenu}"
264
- data-event-click-tracking="${this.config?.eventCategory}|NavHamburger"
265
- title="Open main menu"
266
- >
267
- <icon-hamburger ?active=${this.openMenu === 'media'}></icon-hamburger>
268
- </button>
269
-
270
- <div class=${`branding ${this.secondLogoClass}`}>
271
- <a
272
- .href=${formatUrl('/' as string & Location, this.baseHost)}
273
- @click=${this.trackClick}
274
- data-event-click-tracking="${this.config?.eventCategory}|NavHome"
275
- title="Go home"
276
- class="link-home"
277
- >${icons.iaLogo}${logoWordmarkStacked}</a
278
- >
279
- ${this.secondLogoSlot}
280
- </div>
281
- <media-menu
282
- .baseHost=${this.baseHost}
283
- .config=${this.config}
284
- ?mediaMenuAnimate=${this.mediaMenuAnimate}
285
- .selectedMenuOption=${this.selectedMenuOption}
286
- .openMenu=${this.openMenu}
287
- .currentTab=${this.currentTab}
288
- ></media-menu>
289
- <div class="right-side-section">
290
- ${this.mobileDonateHeart} ${this.userStateTemplate}
291
- ${this.uploadButtonTemplate} ${this.searchMenu}
292
- </div>
293
- </nav>
294
- `;
295
- }
296
- }
1
+ import { html, nothing, PropertyValues } from 'lit';
2
+ import TrackedElement from './tracked-element';
3
+ import icons from './assets/img/icons';
4
+ import './assets/img/hamburger';
5
+ import './login-button';
6
+ import './nav-search';
7
+ import './media-menu';
8
+ import logoWordmarkStacked from './assets/img/wordmark-stacked';
9
+ import primaryNavCSS from './styles/primary-nav';
10
+ import locationHandler from './lib/location-handler';
11
+ import formatUrl from './lib/format-url';
12
+ import { customElement, property } from 'lit/decorators.js';
13
+ import {
14
+ IATopNavConfig,
15
+ IATopNavSearchSlotMode,
16
+ IATopNavSecondIdentitySlotMode,
17
+ } from './models';
18
+ import { defaultTopNavConfig } from './data/menus';
19
+
20
+ @customElement('primary-nav')
21
+ export class PrimaryNav extends TrackedElement {
22
+ @property({ type: String }) mediaBaseHost = 'https://archive.org';
23
+ @property({ type: String }) baseHost = '';
24
+ @property({ type: Boolean }) hideSearch = false;
25
+ @property({ type: Object }) config: IATopNavConfig = defaultTopNavConfig;
26
+ @property({ type: String }) openMenu = '';
27
+ @property({ type: String }) screenName = '';
28
+ @property({ type: String }) searchIn = '';
29
+ @property({ type: String }) searchQuery = '';
30
+ @property({ type: String })
31
+ searchSlotMode: IATopNavSearchSlotMode = '';
32
+ @property({ type: String })
33
+ secondIdentitySlotMode: IATopNavSecondIdentitySlotMode = '';
34
+ @property({ type: String }) selectedMenuOption = '';
35
+ @property({ type: Boolean }) signedOutMenuOpen = false;
36
+ @property({ type: Boolean }) userMenuOpen = false;
37
+ @property({ type: Boolean }) mediaMenuAnimate = false;
38
+ @property({ type: String }) username = '';
39
+ @property({ type: String }) userProfileImagePath = '';
40
+ @property({ type: Object }) currentTab:
41
+ | { mediatype: string; moveTo: string }
42
+ | undefined;
43
+ signedOutMenuToggled: unknown;
44
+
45
+ static get styles() {
46
+ return primaryNavCSS;
47
+ }
48
+
49
+ toggleMediaMenu(e: Event) {
50
+ this.trackClick(e);
51
+ this.dispatchEvent(
52
+ new CustomEvent('menuToggled', {
53
+ detail: {
54
+ menuName: 'media',
55
+ },
56
+ }),
57
+ );
58
+ }
59
+
60
+ toggleSearchMenu(e: Event) {
61
+ this.trackClick(e);
62
+ this.dispatchEvent(
63
+ new CustomEvent('menuToggled', {
64
+ detail: {
65
+ menuName: 'search',
66
+ },
67
+ }),
68
+ );
69
+ }
70
+
71
+ toggleUserMenu(e: Event) {
72
+ this.trackClick(e);
73
+ this.dispatchEvent(
74
+ new CustomEvent('menuToggled', {
75
+ detail: {
76
+ menuName: 'user',
77
+ },
78
+ }),
79
+ );
80
+ }
81
+
82
+ updated(props: PropertyValues) {
83
+ if (props.has('currentTab')) {
84
+ // early return
85
+ if (!this.currentTab || Object.keys(this.currentTab).length === 0)
86
+ return nothing;
87
+
88
+ const isUserMenuTab =
89
+ this.currentTab && this.currentTab.mediatype === 'usermenu';
90
+ if (isUserMenuTab) {
91
+ const mediaButtons = Array.from(
92
+ this.shadowRoot
93
+ ?.querySelector('media-menu')
94
+ ?.shadowRoot?.querySelectorAll('media-button') ?? [],
95
+ );
96
+ const lastMediaButton = mediaButtons.filter((element) => {
97
+ return element.shadowRoot
98
+ ?.querySelector('a')
99
+ ?.classList.contains('images');
100
+ });
101
+
102
+ let nextElement;
103
+ if (this.username) {
104
+ nextElement = this.shadowRoot?.querySelector('a.upload');
105
+ } else {
106
+ nextElement = this.shadowRoot
107
+ ?.querySelector('login-button')
108
+ ?.shadowRoot?.querySelector('span a');
109
+ }
110
+
111
+ const menuItemElement =
112
+ lastMediaButton[0]?.shadowRoot?.querySelector('a.menu-item');
113
+
114
+ const focusElement =
115
+ this.currentTab.moveTo === 'next' ? nextElement : menuItemElement;
116
+
117
+ if (focusElement) {
118
+ (focusElement as HTMLElement).focus();
119
+ }
120
+ } else if (this.currentTab.moveTo === 'next') {
121
+ if (this.shadowRoot?.querySelector('.user-menu')) {
122
+ (this.shadowRoot?.querySelector('.user-menu') as HTMLElement).focus();
123
+ } else {
124
+ (
125
+ this.shadowRoot
126
+ ?.querySelector('login-button')
127
+ ?.shadowRoot?.querySelectorAll('span a')[0] as HTMLElement
128
+ )?.focus();
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ get userIcon() {
135
+ const userMenuClass = this.openMenu === 'user' ? 'active' : '';
136
+ const userMenuToolTip =
137
+ this.openMenu === 'user' ? 'Close user menu' : 'Expand user menu';
138
+
139
+ return html`
140
+ <button
141
+ class="user-menu ${userMenuClass}"
142
+ title="${userMenuToolTip}"
143
+ @click="${this.toggleUserMenu}"
144
+ data-event-click-tracking="${this.config?.eventCategory}|NavUserMenu"
145
+ >
146
+ <img
147
+ src="${this.mediaBaseHost}${this.userProfileImagePath}"
148
+ alt="Profile picture for ${this.screenName}"
149
+ />
150
+ <span class="screen-name" dir="auto">${this.screenName}</span>
151
+ </button>
152
+ `;
153
+ }
154
+
155
+ get loginIcon() {
156
+ return html`
157
+ <login-button
158
+ .baseHost=${this.baseHost}
159
+ .config=${this.config}
160
+ .dropdownOpen=${this.signedOutMenuOpen}
161
+ .openMenu=${this.openMenu}
162
+ @signedOutMenuToggled=${this.signedOutMenuToggled}
163
+ ></login-button>
164
+ `;
165
+ }
166
+
167
+ get searchMenuOpen() {
168
+ return this.openMenu === 'search';
169
+ }
170
+
171
+ get useCustomSearch() {
172
+ return this.searchSlotMode === 'custom';
173
+ }
174
+
175
+ get allowSecondaryIcon() {
176
+ return this.secondIdentitySlotMode === 'allow';
177
+ }
178
+
179
+ get searchMenu() {
180
+ if (this.hideSearch) return nothing;
181
+
182
+ if (this.useCustomSearch) {
183
+ return html`
184
+ <button
185
+ class="search-trigger"
186
+ @click="${this.toggleSearchMenu}"
187
+ data-event-click-tracking="${this.config
188
+ ?.eventCategory}|NavSearchOpen"
189
+ >
190
+ ${icons.search}
191
+ </button>
192
+ <div
193
+ class="custom-search-container ${this.searchMenuOpen ? 'open' : ''}"
194
+ >
195
+ <slot name="custom-search"></slot>
196
+ </div>
197
+ `;
198
+ }
199
+
200
+ return html`
201
+ <button
202
+ class="search-trigger"
203
+ @click="${this.toggleSearchMenu}"
204
+ data-event-click-tracking="${this.config?.eventCategory}|NavSearchOpen"
205
+ >
206
+ ${icons.search}
207
+ </button>
208
+ <nav-search
209
+ .baseHost=${this.baseHost}
210
+ .config=${this.config}
211
+ .locationHandler=${locationHandler}
212
+ .open=${this.searchMenuOpen}
213
+ .openMenu=${this.openMenu}
214
+ .searchIn=${this.searchIn}
215
+ .searchQuery=${this.searchQuery}
216
+ @blur=${this.emitNavSearchBlurEvent}
217
+ ></nav-search>
218
+ `;
219
+ }
220
+
221
+ private emitNavSearchBlurEvent(e: FocusEvent) {
222
+ const relatedTarget = e.relatedTarget as HTMLElement;
223
+ const isUploadButton = relatedTarget?.classList.contains('upload');
224
+
225
+ if (isUploadButton) {
226
+ (this.shadowRoot?.querySelector('a.upload') as HTMLElement).focus();
227
+ }
228
+
229
+ this.dispatchEvent(
230
+ new CustomEvent('navSearchBlur', {
231
+ detail: {
232
+ isUploadButton: !!isUploadButton,
233
+ },
234
+ bubbles: true,
235
+ composed: true,
236
+ }),
237
+ );
238
+ }
239
+
240
+ get mobileDonateHeart() {
241
+ return html`
242
+ <a
243
+ class="mobile-donate-link"
244
+ .href=${formatUrl(
245
+ '/donate/?origin=iawww-mbhrt' as string & Location,
246
+ this.baseHost,
247
+ )}
248
+ >
249
+ ${icons.donateUnpadded}
250
+ <span class="sr-only">"Donate to the archive"</span>
251
+ </a>
252
+ `;
253
+ }
254
+
255
+ get uploadButtonTemplate() {
256
+ return html` <a
257
+ .href="${formatUrl('/upload' as string & Location, this.baseHost)}"
258
+ class="upload"
259
+ @focus=${this.toggleMediaMenu}
260
+ >
261
+ ${icons.upload}
262
+ <span>Upload</span>
263
+ </a>`;
264
+ }
265
+
266
+ get userStateTemplate() {
267
+ return html`<div class="user-info">
268
+ ${this.username ? this.userIcon : this.loginIcon}
269
+ </div>`;
270
+ }
271
+
272
+ get secondLogoSlot() {
273
+ return this.allowSecondaryIcon
274
+ ? html`
275
+ <slot name="opt-sec-logo"></slot>
276
+ <slot name="opt-sec-logo-mobile"></slot>
277
+ `
278
+ : nothing;
279
+ }
280
+
281
+ get secondLogoClass() {
282
+ return this.allowSecondaryIcon ? 'second-logo' : '';
283
+ }
284
+
285
+ render() {
286
+ // const mediaMenuTabIndex = this.openMenu === 'media' ? '' : '-1';
287
+ return html`
288
+ <nav class=${this.hideSearch ? 'hide-search' : ''}>
289
+ <button
290
+ class="hamburger"
291
+ @click="${this.toggleMediaMenu}"
292
+ data-event-click-tracking="${this.config?.eventCategory}|NavHamburger"
293
+ title="Open main menu"
294
+ >
295
+ <icon-hamburger ?active=${this.openMenu === 'media'}></icon-hamburger>
296
+ </button>
297
+
298
+ <div class=${`branding ${this.secondLogoClass}`}>
299
+ <a
300
+ .href=${formatUrl('/' as string & Location, this.baseHost)}
301
+ @click=${this.trackClick}
302
+ data-event-click-tracking="${this.config?.eventCategory}|NavHome"
303
+ title="Go home"
304
+ class="link-home"
305
+ >${icons.iaLogo}${logoWordmarkStacked}</a
306
+ >
307
+ ${this.secondLogoSlot}
308
+ </div>
309
+ <media-menu
310
+ .baseHost=${this.baseHost}
311
+ .config=${this.config}
312
+ ?mediaMenuAnimate=${this.mediaMenuAnimate}
313
+ .selectedMenuOption=${this.selectedMenuOption}
314
+ .openMenu=${this.openMenu}
315
+ .currentTab=${this.currentTab}
316
+ ></media-menu>
317
+ <div class="right-side-section">
318
+ ${this.mobileDonateHeart} ${this.userStateTemplate}
319
+ ${this.uploadButtonTemplate} ${this.searchMenu}
320
+ </div>
321
+ </nav>
322
+ `;
323
+ }
324
+ }