@internetarchive/ia-topnav 1.3.5-alpha7 → 1.3.5-alpha8

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/ia-topnav",
3
- "version": "1.3.5-alpha7",
3
+ "version": "1.3.5-alpha8",
4
4
  "description": "Top nav for Internet Archive",
5
5
  "license": "AGPL-3.0-only",
6
6
  "main": "index.js",
package/src/ia-topnav.js CHANGED
@@ -62,6 +62,7 @@ export default class IATopNav extends LitElement {
62
62
  username: { type: String },
63
63
  userProfileImagePath: { type: String },
64
64
  secondIdentitySlotMode: { type: String },
65
+ currentTab: { type: Object },
65
66
  };
66
67
  }
67
68
 
@@ -77,6 +78,7 @@ export default class IATopNav extends LitElement {
77
78
  this.searchIn = '';
78
79
  this.selectedMenuOption = '';
79
80
  this.secondIdentitySlotMode = '';
81
+ this.currentTab = {};
80
82
  }
81
83
 
82
84
  updated(props) {
@@ -274,6 +276,7 @@ export default class IATopNav extends LitElement {
274
276
  .selectedMenuOption=${this.selectedMenuOption}
275
277
  .username=${this.username}
276
278
  .userProfileImagePath=${this.userProfileImagePath}
279
+ .currentTab=${this.currentTab}
277
280
  ?hideSearch=${this.hideSearch}
278
281
  @mediaTypeSelected=${this.mediaTypeSelected}
279
282
  @toggleSearchMenu=${this.toggleSearchMenu}
@@ -289,6 +292,8 @@ export default class IATopNav extends LitElement {
289
292
  .selectedMenuOption=${this.selectedMenuOption}
290
293
  .mediaSliderOpen=${this.mediaSliderOpen}
291
294
  .menus=${this.menus}
295
+ tabindex="${this.mediaSliderOpen ? '1' : ''}"
296
+ @moveFocusToOthers=${(e) => this.currentTab = e.detail}
292
297
  ></media-slider>
293
298
  </div>
294
299
  ${this.username ? this.userMenu : this.signedOutDropdown}
@@ -0,0 +1,124 @@
1
+ export default class KeyboardNavigation {
2
+ /**
3
+ * Constructor for the KeyboardNavigation class.
4
+ * @param {HTMLElement} elementsContainer - The container element that holds the focusable elements.
5
+ * @param {string} menuOption - The type of menu option ('web' or 'usermenu').
6
+ */
7
+ constructor(elementsContainer, menuOption) {
8
+ this.elementsContainer = elementsContainer;
9
+ this.menuOption = menuOption;
10
+ this.focusableElements = this.getFocusableElements();
11
+ this.focusedIndex = this.getInitialFocusedIndex();
12
+
13
+ this.focusableElements[this.focusedIndex]?.focus();
14
+ this.handleKeyDown = this.handleKeyDown.bind(this);
15
+ }
16
+
17
+ /**
18
+ * Returns the initial focused index based on the menu option.
19
+ * @returns {number} The initial focused index (0 for 'web', 1 for 'usermenu').
20
+ */
21
+ getInitialFocusedIndex() {
22
+ return this.menuOption === 'usermenu' ? 1 : 0;
23
+ }
24
+
25
+ /**
26
+ * Gets an array of focusable elements within the container.
27
+ * @returns {HTMLElement[]} An array of focusable elements.
28
+ */
29
+ getFocusableElements() {
30
+ const focusableTagSelectors = 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])';
31
+ const isDisabledOrHidden = el => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden');
32
+
33
+ let elements;
34
+ if (this.menuOption === 'web') {
35
+ const waybackSlider = this.elementsContainer.querySelector('wayback-slider').shadowRoot;
36
+ const waybackSearch = waybackSlider.querySelector('wayback-search');
37
+ const waybackSearchElements = Array.from(waybackSearch.shadowRoot.querySelectorAll(focusableTagSelectors));
38
+
39
+ const normalElements = Array.from(waybackSlider.querySelectorAll(focusableTagSelectors));
40
+
41
+ const savePageForm = waybackSlider.querySelector('save-page-form');
42
+ const savePageFormElements = Array.from(savePageForm.shadowRoot.querySelectorAll(focusableTagSelectors));
43
+
44
+ elements = [...waybackSearchElements, ...normalElements, ...savePageFormElements];
45
+ } else {
46
+ elements = this.elementsContainer.querySelectorAll(focusableTagSelectors);
47
+ }
48
+
49
+ return Array.from(elements).filter(isDisabledOrHidden);
50
+ }
51
+
52
+ /**
53
+ * Handles keyboard events and focuses the appropriate element.
54
+ * @param {KeyboardEvent} event - The keyboard event object.
55
+ */
56
+ handleKeyDown(event) {
57
+ const { key } = event;
58
+ const isArrowKey = ['ArrowDown', 'ArrowRight', 'ArrowUp', 'ArrowLeft'].includes(key);
59
+ const isTabKey = key === 'Tab';
60
+
61
+ if (isArrowKey) {
62
+ this.handleArrowKey(key);
63
+ } else if (isTabKey) {
64
+ this.handleTabKey(event);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Handles arrow key events and focuses the next or previous element.
70
+ * @param {string} key - The key that was pressed ('ArrowDown', 'ArrowRight', 'ArrowUp', or 'ArrowLeft').
71
+ */
72
+ handleArrowKey(key) {
73
+ const isDownOrRight = ['ArrowDown', 'ArrowRight'].includes(key);
74
+ isDownOrRight ? this.focusNext() : this.focusPrevious();
75
+ }
76
+
77
+ /**
78
+ * Focuses the previous focusable element in the container.
79
+ */
80
+ focusPrevious() {
81
+ if (this.focusableElements.length === 0) return;
82
+ this.focusedIndex = (this.focusedIndex - 1 + this.focusableElements.length) % this.focusableElements.length;
83
+ this.focusableElements[this.focusedIndex]?.focus();
84
+ }
85
+
86
+ /**
87
+ * Focuses the next focusable element in the container.
88
+ */
89
+ focusNext() {
90
+ if (this.focusableElements.length === 0) return;
91
+ this.focusedIndex = (this.focusedIndex + 1) % this.focusableElements.length;
92
+ this.focusableElements[this.focusedIndex]?.focus();
93
+ }
94
+
95
+ /**
96
+ * Handles the Tab key event and focuses the next or previous menu item.
97
+ * @param {KeyboardEvent} event - The keyboard event object.
98
+ */
99
+ handleTabKey(event) {
100
+ if (this.menuOption !== 'usermenu') {
101
+ const isShiftPressed = event.shiftKey;
102
+ this.focusNextMenuItem(isShiftPressed);
103
+ }
104
+
105
+ event.preventDefault();
106
+ }
107
+
108
+ /**
109
+ * Focuses the next or previous menu item based on the provided flag.
110
+ * @param {boolean} isPrevious - A flag indicating whether to focus the previous menu item.
111
+ */
112
+ focusNextMenuItem(isPrevious = false) {
113
+ this.elementsContainer.dispatchEvent(
114
+ new CustomEvent('moveFocusToOthers', {
115
+ bubbles: true,
116
+ composed: true,
117
+ detail: {
118
+ mediatype: this.menuOption,
119
+ moveTo: isPrevious ? 'prev' : 'next',
120
+ },
121
+ })
122
+ );
123
+ }
124
+ }
@@ -67,9 +67,9 @@ class LoginButton extends TrackedElement {
67
67
  ${icons.user}
68
68
  </a>
69
69
  <span>
70
- <a href="${this.signupPath}">Sign up</a>
70
+ <a href="${this.signupPath}" tabindex="1">Sign up</a>
71
71
  |
72
- <a href="${this.loginPath}">Log in</a>
72
+ <a href="${this.loginPath}" tabindex="1">Log in</a>
73
73
  </span>
74
74
  </div>
75
75
  `;
@@ -18,6 +18,7 @@ class MediaButton extends TrackedElement {
18
18
  mediatype: { type: String },
19
19
  openMenu: { type: String },
20
20
  selected: { type: Boolean },
21
+ selectedMenuOption: { type: String },
21
22
  followable: { type: Boolean },
22
23
  };
23
24
  }
@@ -35,9 +36,16 @@ class MediaButton extends TrackedElement {
35
36
  this.mediatype = '';
36
37
  this.openMenu = '';
37
38
  this.selected = false;
39
+ this.selectedMenuOption = '';
38
40
  this.followable = false;
39
41
  }
40
42
 
43
+ updated() {
44
+ console.log(this.selectedMenuOption)
45
+ // if (this.selectedMenuOption)
46
+ // this.shadowDom.querySelector(`a.${this.mediatype}`).focus();
47
+ }
48
+
41
49
  onClick(e) {
42
50
  this.trackClick(e);
43
51
  e.preventDefault();
@@ -95,15 +103,26 @@ class MediaButton extends TrackedElement {
95
103
  `;
96
104
  }
97
105
 
106
+ emitHandleSubMenuKeyDown(e) {
107
+ this.dispatchEvent(new CustomEvent('subMenuKeyDown1', {
108
+ bubbles: true,
109
+ composed: true,
110
+ detail: {
111
+ e
112
+ }
113
+ }));
114
+ }
115
+
98
116
  render() {
99
117
  return html`
100
118
  <a
101
119
  href="${this.href}"
102
120
  class="menu-item ${this.mediatype} ${this.buttonClass}"
103
121
  @click=${this.followable ? this.trackClick : this.onClick}
122
+ @keydown="${this.emitHandleSubMenuKeyDown}"
104
123
  data-event-click-tracking="${this.analyticsEvent}"
105
124
  title="${this.tooltipPrefix} ${this.mediatype} menu"
106
- tabindex="${this.openMenu === 'media' ? '' : '-1'}"
125
+ tabindex="${this.openMenu === 'media' ? '' : '0'}"
107
126
  >
108
127
  ${this.menuItem}
109
128
  </a>
package/src/media-menu.js CHANGED
@@ -67,6 +67,11 @@ class MediaMenu extends LitElement {
67
67
  config: { type: Object },
68
68
  openMenu: { type: String },
69
69
  selectedMenuOption: { type: String },
70
+
71
+ isSubMenuOpen: { type: Boolean },
72
+ focusedIndex: { type: Number },
73
+
74
+ currentTab: { type: Object },
70
75
  };
71
76
  }
72
77
 
@@ -75,8 +80,95 @@ class MediaMenu extends LitElement {
75
80
  this.config = {};
76
81
  this.openMenu = '';
77
82
  this.selectedMenuOption = '';
83
+
84
+ this.isSubMenuOpen = false;
85
+ this.focusedIndex = -1;
86
+ this.menuRef = [];
87
+ this.subMenuRef = [];
88
+
89
+ this.currentTab = {};
90
+ this.focusOn = 0;
91
+ }
92
+
93
+ firstUpdated() {
94
+ this.menuRef = Array.from(this.shadowRoot.querySelectorAll('media-button'));
95
+ // this.subMenuRef = Array.from(this.shadowRoot.querySelectorAll('.submenu-item'));
96
+ // this.shadowRoot.querySelector('media-button').focus();
97
+ // this.addEventListener('keydown', this.handleKeyDown.bind(this));
98
+
99
+ // this.shadowRoot.querySelector('.menu-group media-button').shadowRoot.querySelector(`${this.selectedMenuOption}`);
100
+ }
101
+
102
+ updated(props) {
103
+ // console.log(props)
104
+ if (props.has('currentTab')) {
105
+ // console.log(this.currentTab.mediatype, this.currentTab.moveTo)
106
+
107
+ // const dd = this.shadowRoot.querySelector('primary-nav').shadowRoot.querySelector('media-menu');
108
+ const mediaButtons = Array.from(this.shadowRoot.querySelectorAll('media-button'));
109
+ // console.log(mediaButtons)
110
+
111
+ // let = newIndex1 = 0;
112
+ mediaButtons.map((button, index) => {
113
+ // console.log(button.shadowRoot.querySelector('a.menu-item'));
114
+
115
+ const linkItem = button.shadowRoot.querySelector('a.menu-item');
116
+ if (linkItem) {
117
+ // console.log(linkItem)
118
+ if (linkItem.classList.contains(`${this.selectedMenuOption}`)) {
119
+ linkItem.classList.remove('selected');
120
+ linkItem.blur();
121
+
122
+ // newIndex1 = this.currentTab.moveTo === 'next' ? index++ : index--;
123
+ // if (this.currentTab.moveTo)
124
+ // mediaButtons[index].shadowRoot.querySelector('a.menu-item').blur();
125
+ // if (this.currentTab.moveTo === 'next') {
126
+ // mediaButtons[index + 1].shadowRoot.querySelector('a.menu-item').focus();
127
+ // } else if (this.currentTab.moveTo === 'prev') {
128
+ mediaButtons[this.currentTab.moveTo === 'next' ? index + 1 : index - 1].shadowRoot.querySelector('a.menu-item').focus();
129
+ // }
130
+ }
131
+ }
132
+ });
133
+
134
+ // console.log('updatedddddddddddd', this.selectedMenuOption, this.shadowRoot.querySelector('.menu-group'));
135
+
136
+
137
+ // this.shadowRoot.querySelector('.menu-group').focus();
138
+ // console.log(props);
139
+ // this.shadowRoot.querySelectorAll('media-button').shadowRoot.querySelector(`${this.selectedMenuOption}`);
140
+
141
+ // this.requestUpdate();
142
+ // this.updateRequest();
143
+ // this.menuRef = Array.from(this.shadowRoot.querySelectorAll('media-button'));
144
+ }
78
145
  }
79
146
 
147
+ handleKeyDown(e) {
148
+ // console.log('dddddddddddd')
149
+ if (e.key === 'Tab') {
150
+ console.log(this.menuRef)
151
+ this.focusedIndex = (this.focusedIndex + 1) % this.menuRef.length;
152
+ this.submenuFocusedIndex = -1; // reset submenu focus index
153
+ this.menuRef[this.focusedIndex].focus();
154
+ }
155
+ }
156
+
157
+ // handleSubMenuKeydown(e) {
158
+ // console.log(e);
159
+ // if (e.key === 'ArrowDown') {
160
+ // this.focusedIndex = (this.focusedIndex + 1) % this.subMenuRef.length;
161
+ // this.subMenuRef[this.focusedIndex].focus();
162
+ // } else if (e.key === 'ArrowUp') {
163
+ // this.focusedIndex = (this.focusedIndex - 1 + this.subMenuRef.length) % this.subMenuRef.length;
164
+ // this.subMenuRef[this.focusedIndex].focus();
165
+ // } else if (e.key === 'Escape') {
166
+ // this.isSubMenuOpen = false;
167
+ // this.requestUpdate();
168
+ // setTimeout(() => this.menuRef[0]?.focus(), 0);
169
+ // }
170
+ // }
171
+
80
172
  get mediaMenuOptionsTemplate() {
81
173
  const buttons = menuSelection.map(({
82
174
  icon,
@@ -86,6 +178,7 @@ class MediaMenu extends LitElement {
86
178
  followable,
87
179
  }) => {
88
180
  const selected = this.selectedMenuOption === menu;
181
+ // this.focusOn =
89
182
  return html`
90
183
  <media-button
91
184
  .config=${this.config}
@@ -96,7 +189,9 @@ class MediaMenu extends LitElement {
96
189
  .mediatype=${menu}
97
190
  .openMenu=${this.openMenu}
98
191
  .selected=${selected}
192
+ .selectedMenuOption=${this.selectedMenuOption}
99
193
  data-mediatype="${menu}"
194
+ .focusOn=${this.focusOn}
100
195
  ></media-button>
101
196
  `;
102
197
  });
@@ -117,7 +212,6 @@ class MediaMenu extends LitElement {
117
212
  <div class="overflow-clip">
118
213
  <nav
119
214
  class="media-menu-inner"
120
- aria-hidden="${!this.menuOpened}"
121
215
  aria-expanded="${this.menuOpened}"
122
216
  >
123
217
  <div class="menu-group">
@@ -1,6 +1,7 @@
1
1
  import { LitElement, html } from 'https://offshoot.prod.archive.org/lit.js';
2
2
  import './media-subnav.js';
3
3
  import mediaSliderCSS from './styles/media-slider.js';
4
+ import KeyboardNavigation from './lib/keyboard-navigation.js';
4
5
 
5
6
  class MediaSlider extends LitElement {
6
7
  static get styles() {
@@ -26,6 +27,19 @@ class MediaSlider extends LitElement {
26
27
  this.selectedMenuOption = 'texts';
27
28
  }
28
29
 
30
+ updated(props) {
31
+ if (props.has('selectedMenuOption') && this.selectedMenuOption) {
32
+ const container = this.shadowRoot?.querySelector('.has-focused')?.shadowRoot;
33
+
34
+ if (container) {
35
+ const keyboardNavigation = new KeyboardNavigation(container, this.selectedMenuOption);
36
+ this.addEventListener('keydown', keyboardNavigation.handleKeyDown);
37
+ this.removeEventListener('keydown', this.previousKeydownListener);
38
+ this.previousKeydownListener = keyboardNavigation.handleKeyDown;
39
+ }
40
+ }
41
+ }
42
+
29
43
  shouldUpdate() {
30
44
  const scrollPane = this.shadowRoot ? this.shadowRoot.querySelector('.information-menu') : null;
31
45
 
@@ -47,49 +61,49 @@ class MediaSlider extends LitElement {
47
61
  <media-subnav
48
62
  .baseHost=${this.baseHost}
49
63
  .config=${this.config}
50
- class="${this.selectedMenuOption === 'audio' ? '' : 'hidden'}"
64
+ class="${this.selectedMenuOption === 'audio' ? 'has-focused' : 'hidden'}"
51
65
  menu="audio"
52
66
  .menuItems=${this.menus.audio}
53
67
  ></media-subnav>
54
68
  <media-subnav
55
69
  .baseHost=${this.baseHost}
56
70
  .config=${this.config}
57
- class="${this.selectedMenuOption === 'images' ? '' : 'hidden'}"
71
+ class="${this.selectedMenuOption === 'images' ? 'has-focused' : 'hidden'}"
58
72
  menu="images"
59
73
  .menuItems=${this.menus.images}
60
74
  ></media-subnav>
61
75
  <media-subnav
62
76
  .baseHost=${this.baseHost}
63
77
  .config=${this.config}
64
- class="${this.selectedMenuOption === 'software' ? '' : 'hidden'}"
78
+ class="${this.selectedMenuOption === 'software' ? 'has-focused' : 'hidden'}"
65
79
  menu="software"
66
80
  .menuItems=${this.menus.software}
67
81
  ></media-subnav>
68
82
  <media-subnav
69
83
  .baseHost=${this.baseHost}
70
84
  .config=${this.config}
71
- class="${this.selectedMenuOption === 'texts' ? '' : 'hidden'}"
85
+ class="${this.selectedMenuOption === 'texts' ? 'has-focused' : 'hidden'}"
72
86
  menu="texts"
73
87
  .menuItems=${this.menus.texts}
74
88
  ></media-subnav>
75
89
  <media-subnav
76
90
  .baseHost=${this.baseHost}
77
91
  .config=${this.config}
78
- class="${this.selectedMenuOption === 'video' ? '' : 'hidden'}"
92
+ class="${this.selectedMenuOption === 'video' ? 'has-focused' : 'hidden'}"
79
93
  menu="video"
80
94
  .menuItems=${this.menus.video}
81
95
  ></media-subnav>
82
96
  <media-subnav
83
97
  .baseHost=${this.baseHost}
84
98
  .config=${this.config}
85
- class="${this.selectedMenuOption === 'web' ? '' : 'hidden'}"
99
+ class="${this.selectedMenuOption === 'web' ? 'has-focused' : 'hidden'}"
86
100
  menu="web"
87
101
  .menuItems=${this.menus.web}
88
102
  ></media-subnav>
89
103
  <media-subnav
90
104
  .baseHost=${this.baseHost}
91
105
  .config=${this.config}
92
- class="${this.selectedMenuOption === 'more' ? '' : 'hidden'}"
106
+ class="${this.selectedMenuOption === 'more' ? 'has-focused' : 'hidden'}"
93
107
  menu="more"
94
108
  .menuItems=${this.menus.more}
95
109
  ></media-subnav>
package/src/nav-search.js CHANGED
@@ -98,7 +98,6 @@ class NavSearch extends TrackedElement {
98
98
  class="search-field"
99
99
  placeholder="Search"
100
100
  autocomplete="off"
101
- tabindex="2"
102
101
  value=${this.searchQuery || ''}
103
102
  @focus=${this.toggleSearchMenu}
104
103
  />
@@ -31,6 +31,7 @@ class PrimaryNav extends TrackedElement {
31
31
  userMenuOpen: { type: Boolean },
32
32
  username: { type: String },
33
33
  userProfileImagePath: { type: String },
34
+ currentTab: { type: Object },
34
35
  };
35
36
  }
36
37
 
@@ -44,6 +45,7 @@ class PrimaryNav extends TrackedElement {
44
45
  this.userMenuOpen = false;
45
46
  this.mediaBaseHost = 'https://archive.org';
46
47
  this.secondIdentitySlotMode = '';
48
+ this.currentTab = {};
47
49
  }
48
50
 
49
51
  toggleMediaMenu(e) {
@@ -87,7 +89,6 @@ class PrimaryNav extends TrackedElement {
87
89
  <button
88
90
  class="user-menu ${userMenuClass}"
89
91
  title="${userMenuToolTip}"
90
- tabindex="-1"
91
92
  @click="${this.toggleUserMenu}"
92
93
  data-event-click-tracking="${this.config.eventCategory}|NavUserMenu"
93
94
  >
@@ -107,7 +108,6 @@ class PrimaryNav extends TrackedElement {
107
108
  .config=${this.config}
108
109
  .dropdownOpen=${this.signedOutMenuOpen}
109
110
  .openMenu=${this.openMenu}
110
- tabindex="-1"
111
111
  @signedOutMenuToggled=${this.signedOutMenuToggled}
112
112
  ></login-button>
113
113
  `;
@@ -157,7 +157,6 @@ class PrimaryNav extends TrackedElement {
157
157
  return html`
158
158
  <a href="${formatUrl('/create', this.baseHost)}"
159
159
  class="upload"
160
- tabindex="1"
161
160
  @focus=${this.toggleMediaMenu}
162
161
  >
163
162
  ${icons.upload}
@@ -188,6 +187,15 @@ class PrimaryNav extends TrackedElement {
188
187
  const mediaMenuTabIndex = this.openMenu === 'media' ? '' : '-1';
189
188
  return html`
190
189
  <nav class=${this.hideSearch ? 'hide-search' : nothing}>
190
+ <button
191
+ class="hamburger"
192
+ @click="${this.toggleMediaMenu}"
193
+ data-event-click-tracking="${this.config.eventCategory}|NavHamburger"
194
+ title="Open main menu"
195
+ >
196
+ <icon-hamburger ?active=${this.openMenu === 'media'}></icon-hamburger>
197
+ </button>
198
+
191
199
  <div class=${`branding ${this.secondLogoClass}`}>
192
200
  <a
193
201
  href=${formatUrl('/', this.baseHost)}
@@ -200,30 +208,20 @@ class PrimaryNav extends TrackedElement {
200
208
  >
201
209
  ${this.secondLogoSlot}
202
210
  </div>
203
-
204
- <div class="right-side-section">
205
- ${this.mobileDonateHeart}
206
- ${this.searchMenu}
207
- ${this.uploadButtonTemplate}
208
- ${this.userStateTemplate}
209
- </div>
210
211
  <media-menu
211
212
  .baseHost=${this.baseHost}
212
213
  .config=${this.config}
213
214
  ?mediaMenuAnimate="${this.mediaMenuAnimate}"
214
- tabindex="${mediaMenuTabIndex}"
215
215
  .selectedMenuOption=${this.selectedMenuOption}
216
216
  .openMenu=${this.openMenu}
217
+ .currentTab=${this.currentTab}
217
218
  ></media-menu>
218
- <button
219
- class="hamburger"
220
- @click="${this.toggleMediaMenu}"
221
- tabindex="1"
222
- data-event-click-tracking="${this.config.eventCategory}|NavHamburger"
223
- title="Open main menu"
224
- >
225
- <icon-hamburger ?active=${this.openMenu === 'media'}></icon-hamburger>
226
- </button>
219
+ <div class="right-side-section">
220
+ ${this.mobileDonateHeart}
221
+ ${this.userStateTemplate}
222
+ ${this.uploadButtonTemplate}
223
+ ${this.searchMenu}
224
+ </div>
227
225
  </nav>
228
226
  `;
229
227
  }
@@ -100,7 +100,7 @@ class SearchMenu extends TrackedElement {
100
100
  }
101
101
  return html`
102
102
  <label @click="${this.selectSearchType}">
103
- <input tabindex="3" form="nav-search" type="radio" name="sin" value="${value}" ?checked=${isDefault} @change=${this.searchInChanged} />
103
+ <input form="nav-search" type="radio" name="sin" value="${value}" ?checked=${isDefault} @change=${this.searchInChanged} />
104
104
  Search ${label}
105
105
  </label>
106
106
  `;
@@ -134,7 +134,6 @@ class SearchMenu extends TrackedElement {
134
134
  href="${formatUrl('/advancedsearch.php', this.baseHost)}"
135
135
  @click=${this.trackClick}
136
136
  data-event-click-tracking="${this.config.eventCategory}|NavAdvancedSearch"
137
- tabindex="4"
138
137
  >Advanced Search</a
139
138
  >
140
139
  </div>
@@ -92,6 +92,7 @@ export default css`
92
92
 
93
93
  @media (min-width: 890px) {
94
94
  nav {
95
+ display: flex;
95
96
  overflow: visible;
96
97
  top: 0;
97
98
  left: auto;
@@ -146,6 +147,7 @@ export default css`
146
147
  a:focus {
147
148
  color: var(--linkHoverColor);
148
149
  background: var(--linkColor);
150
+ outline: none;
149
151
  }
150
152
 
151
153
  .initial,
@@ -24,7 +24,7 @@ export default css`
24
24
  /* Mobile view styles */
25
25
  @media (max-width: 889px) {
26
26
  .media-menu-container {
27
- position: relative;
27
+ // position: relative;
28
28
  }
29
29
 
30
30
  .media-menu-inner {
@@ -39,7 +39,7 @@ export default css`
39
39
  .overflow-clip {
40
40
  position: absolute;
41
41
  z-index: -1; /** needs to be under the navigation, otherwise it intercepts clicks */
42
- top: 0;
42
+ top: 4rem;
43
43
  left: 0;
44
44
  height: 0;
45
45
  width: 100%;
@@ -10,7 +10,8 @@ export default css`
10
10
  nav {
11
11
  position: relative;
12
12
  display: -ms-grid;
13
- display: grid;
13
+ // display: grid;
14
+ display: flex;
14
15
  height: 4rem;
15
16
  grid-template-areas: 'hamburger empty heart search user';
16
17
  -ms-grid-columns: 4rem minmax(1rem, 100%) 4rem 4rem 4rem;
@@ -29,6 +30,7 @@ export default css`
29
30
 
30
31
  .right-side-section {
31
32
  display: flex;
33
+ margin-left: auto;
32
34
  user-select: none;
33
35
  }
34
36
  button {
@@ -86,6 +88,9 @@ export default css`
86
88
  fill: var(--activeColor);
87
89
  }
88
90
 
91
+ .mobile-donate-link {
92
+ display: inline-block;
93
+ }
89
94
  .mobile-donate-link svg {
90
95
  height: 4rem;
91
96
  width: 4rem;
@@ -151,7 +156,8 @@ export default css`
151
156
  height: 100%;
152
157
  }
153
158
 
154
- .user-menu:hover {
159
+ .user-menu:hover,
160
+ .user-menu:focus {
155
161
  color: var(--linkHoverColor);
156
162
  }
157
163
 
@@ -187,6 +193,13 @@ export default css`
187
193
  slot[name='opt-sec-logo'] {
188
194
  display: none;
189
195
  }
196
+
197
+ .right-side-section {
198
+ display: initial;
199
+ }
200
+ .right-side-section .user-info {
201
+ float: right;
202
+ }
190
203
  }
191
204
 
192
205
  @media (min-width: 890px) {
@@ -195,12 +208,8 @@ export default css`
195
208
  --userIconHeight: 3.2rem;
196
209
  }
197
210
 
198
- .right-side-section {
199
- display: contents;
200
- }
201
-
202
211
  nav {
203
- display: block;
212
+ display: flex;
204
213
  z-index: 4;
205
214
  height: 5rem;
206
215
  padding-right: 1.5rem;
package/src/user-menu.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { html } from 'https://offshoot.prod.archive.org/lit.js';
2
2
  import DropdownMenu from './dropdown-menu.js';
3
3
  import userMenuCSS from './styles/user-menu.js';
4
+ import KeyboardNavigation from './lib/keyboard-navigation.js';
4
5
 
5
6
  class UserMenu extends DropdownMenu {
6
7
  static get styles() {
@@ -21,6 +22,19 @@ class UserMenu extends DropdownMenu {
21
22
  this.username = '';
22
23
  }
23
24
 
25
+ updated(props) {
26
+ if (props.has('open')) {
27
+ const container = this.shadowRoot?.querySelector('.nav-container');
28
+
29
+ if (container) {
30
+ const keyboardNavigation = new KeyboardNavigation(container, 'usermenu');
31
+ this.addEventListener('keydown', keyboardNavigation.handleKeyDown);
32
+ this.removeEventListener('keydown', this.previousKeydownListener);
33
+ this.previousKeydownListener = keyboardNavigation.handleKeyDown;
34
+ }
35
+ }
36
+ }
37
+
24
38
  render() {
25
39
  return html`
26
40
  <div class="nav-container">