@internetarchive/ia-topnav 1.3.5-alpha9 → 1.3.5

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-alpha9",
3
+ "version": "1.3.5",
4
4
  "description": "Top nav for Internet Archive",
5
5
  "license": "AGPL-3.0-only",
6
6
  "main": "index.js",
package/src/data/menus.js CHANGED
@@ -48,10 +48,6 @@ export function buildTopNavMenus(userid = '___USERID___', localLinks = true, way
48
48
  title: 'All Audio',
49
49
  url: `${prefix}/details/audio`,
50
50
  },
51
- {
52
- title: 'This Just In',
53
- url: `${prefix}/search.php?query=mediatype:audio&sort=-publicdate`,
54
- },
55
51
  {
56
52
  title: 'Grateful Dead',
57
53
  url: `${prefix}/details/GratefulDead`,
@@ -119,10 +115,6 @@ export function buildTopNavMenus(userid = '___USERID___', localLinks = true, way
119
115
  title: 'All Images',
120
116
  url: `${prefix}/details/image`,
121
117
  },
122
- {
123
- title: 'This Just In',
124
- url: `${prefix}/search.php?query=mediatype:image&sort=-publicdate`,
125
- },
126
118
  {
127
119
  title: 'Flickr Commons',
128
120
  url: `${prefix}/details/flickrcommons`,
@@ -212,10 +204,6 @@ export function buildTopNavMenus(userid = '___USERID___', localLinks = true, way
212
204
  title: 'All Software',
213
205
  url: `${prefix}/details/software`,
214
206
  },
215
- {
216
- title: 'This Just In',
217
- url: `${prefix}/search.php?query=mediatype:software&sort=-publicdate`,
218
- },
219
207
  {
220
208
  title: 'Old School Emulation',
221
209
  url: `${prefix}/details/tosec`,
@@ -293,32 +281,24 @@ export function buildTopNavMenus(userid = '___USERID___', localLinks = true, way
293
281
  ],
294
282
  },
295
283
  texts: {
296
- heading: 'Books',
284
+ heading: 'Texts',
297
285
  iconLinks: [
298
- {
299
- title: 'Books to Borrow',
300
- icon: `${prefix}/images/book-lend.png`,
301
- url: `${prefix}/details/inlibrary`,
302
- },
303
286
  {
304
287
  title: 'Open Library',
305
288
  icon: `${prefix}/images/widgetOL.png`,
306
289
  url: 'https://openlibrary.org/',
307
290
  },
308
- ],
309
- featuredLinks: [
310
291
  {
311
- title: 'All Books',
312
- url: `${prefix}/details/books`,
292
+ title: 'American Libraries',
293
+ icon: `${prefix}/services/img/americana`,
294
+ url: `${prefix}/details/americana`,
313
295
  },
296
+ ],
297
+ featuredLinks: [
314
298
  {
315
299
  title: 'All Texts',
316
300
  url: `${prefix}/details/texts`,
317
301
  },
318
- {
319
- title: 'This Just In',
320
- url: `${prefix}/search.php?query=mediatype:texts&sort=-publicdate`,
321
- },
322
302
  {
323
303
  title: 'Smithsonian Libraries',
324
304
  url: `${prefix}/details/smithsonian`,
@@ -443,10 +423,6 @@ export function buildTopNavMenus(userid = '___USERID___', localLinks = true, way
443
423
  title: 'All Video',
444
424
  url: `${prefix}/details/movies`,
445
425
  },
446
- {
447
- title: 'This Just In',
448
- url: `${prefix}/search.php?query=mediatype:movies&sort=-publicdate`,
449
- },
450
426
  {
451
427
  title: 'Prelinger Archives',
452
428
  url: `${prefix}/details/prelinger`,
@@ -57,7 +57,6 @@ class DropdownMenu extends TrackedElement {
57
57
  return html`<a
58
58
  href="${formatUrl(link.url, this.baseHost)}"
59
59
  class="${link.class}"
60
- tabindex="${this.open ? '' : '-1'}"
61
60
  @click=${this.trackClick}
62
61
  data-event-click-tracking="${this.config.eventCategory}|Nav${link.analyticsEvent}"
63
62
  aria-label=${calloutText ? `New feature: ${link.title}` : nothing}>
package/src/ia-topnav.js CHANGED
@@ -62,7 +62,6 @@ 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 },
66
65
  };
67
66
  }
68
67
 
@@ -78,12 +77,11 @@ export default class IATopNav extends LitElement {
78
77
  this.searchIn = '';
79
78
  this.selectedMenuOption = '';
80
79
  this.secondIdentitySlotMode = '';
81
- this.currentTab = {};
82
80
  }
83
81
 
84
82
  updated(props) {
85
83
  if (props.has('username') || props.has('localLinks') || props.has('baseHost') ||
86
- props.has('waybackPagesArchived') || props.has('itemIdentifier')) {
84
+ props.has('waybackPagesArchived') || props.has('itemIdentifier')) {
87
85
  this.menuSetup();
88
86
  }
89
87
  }
@@ -201,7 +199,6 @@ export default class IATopNav extends LitElement {
201
199
  tabindex="${this.userMenuTabIndex}"
202
200
  @menuToggled=${this.menuToggled}
203
201
  @trackClick=${this.trackClick}
204
- @moveFocusToOthers=${(e) => this.currentTab = e.detail}
205
202
  ></user-menu>
206
203
  `;
207
204
  }
@@ -277,7 +274,6 @@ export default class IATopNav extends LitElement {
277
274
  .selectedMenuOption=${this.selectedMenuOption}
278
275
  .username=${this.username}
279
276
  .userProfileImagePath=${this.userProfileImagePath}
280
- .currentTab=${this.currentTab}
281
277
  ?hideSearch=${this.hideSearch}
282
278
  @mediaTypeSelected=${this.mediaTypeSelected}
283
279
  @toggleSearchMenu=${this.toggleSearchMenu}
@@ -293,8 +289,6 @@ export default class IATopNav extends LitElement {
293
289
  .selectedMenuOption=${this.selectedMenuOption}
294
290
  .mediaSliderOpen=${this.mediaSliderOpen}
295
291
  .menus=${this.menus}
296
- tabindex="${this.mediaSliderOpen ? '1' : ''}"
297
- @moveFocusToOthers=${(e) => this.currentTab = e.detail}
298
292
  ></media-slider>
299
293
  </div>
300
294
  ${this.username ? this.userMenu : this.signedOutDropdown}
@@ -311,7 +305,6 @@ export default class IATopNav extends LitElement {
311
305
  <desktop-subnav
312
306
  .baseHost=${this.baseHost}
313
307
  .menuItems=${this.desktopSubnavMenuItems}
314
- @focus=${this.closeMenus}
315
308
  ></desktop-subnav>
316
309
  <div id="close-layer" class="${this.closeLayerClass}" @click=${this.closeMenus}></div>
317
310
  `;
@@ -103,7 +103,6 @@ class MediaButton extends TrackedElement {
103
103
  @click=${this.followable ? this.trackClick : this.onClick}
104
104
  data-event-click-tracking="${this.analyticsEvent}"
105
105
  title="${this.tooltipPrefix} ${this.mediatype} menu"
106
- tabindex="${this.openMenu === 'media' ? '' : '0'}"
107
106
  >
108
107
  ${this.menuItem}
109
108
  </a>
package/src/media-menu.js CHANGED
@@ -15,7 +15,7 @@ const menuSelection = [
15
15
  icon: 'texts',
16
16
  menu: 'texts',
17
17
  href: '/details/texts',
18
- label: 'Books',
18
+ label: 'Texts',
19
19
  },
20
20
  {
21
21
  icon: 'video',
@@ -67,7 +67,6 @@ class MediaMenu extends LitElement {
67
67
  config: { type: Object },
68
68
  openMenu: { type: String },
69
69
  selectedMenuOption: { type: String },
70
- currentTab: { type: Object },
71
70
  };
72
71
  }
73
72
 
@@ -76,25 +75,6 @@ class MediaMenu extends LitElement {
76
75
  this.config = {};
77
76
  this.openMenu = '';
78
77
  this.selectedMenuOption = '';
79
- this.currentTab = {};
80
- }
81
-
82
- updated(props) {
83
- if (props.has('currentTab')) {
84
- const mediaButtons = Array.from(this.shadowRoot.querySelectorAll('media-button'));
85
-
86
- mediaButtons.map((button, index) => {
87
- const linkItem = button.shadowRoot.querySelector('a.menu-item');
88
- if (linkItem) {
89
- if (linkItem.classList.contains(`${this.selectedMenuOption}`)) {
90
- linkItem.classList.remove('selected');
91
- linkItem.blur();
92
-
93
- mediaButtons[this.currentTab.moveTo === 'next' ? index + 1 : index - 1].shadowRoot.querySelector('a.menu-item').focus();
94
- }
95
- }
96
- });
97
- }
98
78
  }
99
79
 
100
80
  get mediaMenuOptionsTemplate() {
@@ -116,7 +96,6 @@ class MediaMenu extends LitElement {
116
96
  .mediatype=${menu}
117
97
  .openMenu=${this.openMenu}
118
98
  .selected=${selected}
119
- .selectedMenuOption=${this.selectedMenuOption}
120
99
  data-mediatype="${menu}"
121
100
  ></media-button>
122
101
  `;
@@ -138,6 +117,7 @@ class MediaMenu extends LitElement {
138
117
  <div class="overflow-clip">
139
118
  <nav
140
119
  class="media-menu-inner"
120
+ aria-hidden="${!this.menuOpened}"
141
121
  aria-expanded="${this.menuOpened}"
142
122
  >
143
123
  <div class="menu-group">
@@ -1,7 +1,6 @@
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';
5
4
 
6
5
  class MediaSlider extends LitElement {
7
6
  static get styles() {
@@ -27,19 +26,6 @@ class MediaSlider extends LitElement {
27
26
  this.selectedMenuOption = 'texts';
28
27
  }
29
28
 
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
-
43
29
  shouldUpdate() {
44
30
  const scrollPane = this.shadowRoot ? this.shadowRoot.querySelector('.information-menu') : null;
45
31
 
@@ -61,49 +47,49 @@ class MediaSlider extends LitElement {
61
47
  <media-subnav
62
48
  .baseHost=${this.baseHost}
63
49
  .config=${this.config}
64
- class="${this.selectedMenuOption === 'audio' ? 'has-focused' : 'hidden'}"
50
+ class="${this.selectedMenuOption === 'audio' ? '' : 'hidden'}"
65
51
  menu="audio"
66
52
  .menuItems=${this.menus.audio}
67
53
  ></media-subnav>
68
54
  <media-subnav
69
55
  .baseHost=${this.baseHost}
70
56
  .config=${this.config}
71
- class="${this.selectedMenuOption === 'images' ? 'has-focused' : 'hidden'}"
57
+ class="${this.selectedMenuOption === 'images' ? '' : 'hidden'}"
72
58
  menu="images"
73
59
  .menuItems=${this.menus.images}
74
60
  ></media-subnav>
75
61
  <media-subnav
76
62
  .baseHost=${this.baseHost}
77
63
  .config=${this.config}
78
- class="${this.selectedMenuOption === 'software' ? 'has-focused' : 'hidden'}"
64
+ class="${this.selectedMenuOption === 'software' ? '' : 'hidden'}"
79
65
  menu="software"
80
66
  .menuItems=${this.menus.software}
81
67
  ></media-subnav>
82
68
  <media-subnav
83
69
  .baseHost=${this.baseHost}
84
70
  .config=${this.config}
85
- class="${this.selectedMenuOption === 'texts' ? 'has-focused' : 'hidden'}"
71
+ class="${this.selectedMenuOption === 'texts' ? '' : 'hidden'}"
86
72
  menu="texts"
87
73
  .menuItems=${this.menus.texts}
88
74
  ></media-subnav>
89
75
  <media-subnav
90
76
  .baseHost=${this.baseHost}
91
77
  .config=${this.config}
92
- class="${this.selectedMenuOption === 'video' ? 'has-focused' : 'hidden'}"
78
+ class="${this.selectedMenuOption === 'video' ? '' : 'hidden'}"
93
79
  menu="video"
94
80
  .menuItems=${this.menus.video}
95
81
  ></media-subnav>
96
82
  <media-subnav
97
83
  .baseHost=${this.baseHost}
98
84
  .config=${this.config}
99
- class="${this.selectedMenuOption === 'web' ? 'has-focused' : 'hidden'}"
85
+ class="${this.selectedMenuOption === 'web' ? '' : 'hidden'}"
100
86
  menu="web"
101
87
  .menuItems=${this.menus.web}
102
88
  ></media-subnav>
103
89
  <media-subnav
104
90
  .baseHost=${this.baseHost}
105
91
  .config=${this.config}
106
- class="${this.selectedMenuOption === 'more' ? 'has-focused' : 'hidden'}"
92
+ class="${this.selectedMenuOption === 'more' ? '' : 'hidden'}"
107
93
  menu="more"
108
94
  .menuItems=${this.menus.more}
109
95
  ></media-subnav>
package/src/nav-search.js CHANGED
@@ -34,6 +34,13 @@ class NavSearch extends TrackedElement {
34
34
  this.initSearchBetaOptIn();
35
35
  }
36
36
 
37
+ updated() {
38
+ if (this.open) {
39
+ this.shadowRoot.querySelector('[name=query]').focus();
40
+ }
41
+ return true;
42
+ }
43
+
37
44
  initSearchBetaOptIn() {
38
45
  this.inSearchBeta = !!window.localStorage?.getItem('SearchBeta-opt-in') ||
39
46
  !!window.localStorage?.getItem('SearchBeta-launched');
@@ -98,14 +105,13 @@ class NavSearch extends TrackedElement {
98
105
  class="search-field"
99
106
  placeholder="Search"
100
107
  autocomplete="off"
101
- value=${this.searchQuery || ''}
102
108
  @focus=${this.toggleSearchMenu}
109
+ value=${this.searchQuery || ''}
103
110
  />
104
111
  ${this.searchInsideInput}
105
112
  <button
106
113
  type="submit"
107
114
  class="search"
108
- tabindex="-1"
109
115
  data-event-click-tracking="${this.config.eventCategory}|NavSearchClose"
110
116
  >
111
117
  ${icons.search}
@@ -31,7 +31,6 @@ class PrimaryNav extends TrackedElement {
31
31
  userMenuOpen: { type: Boolean },
32
32
  username: { type: String },
33
33
  userProfileImagePath: { type: String },
34
- currentTab: { type: Object },
35
34
  };
36
35
  }
37
36
 
@@ -45,7 +44,6 @@ class PrimaryNav extends TrackedElement {
45
44
  this.userMenuOpen = false;
46
45
  this.mediaBaseHost = 'https://archive.org';
47
46
  this.secondIdentitySlotMode = '';
48
- this.currentTab = {};
49
47
  }
50
48
 
51
49
  toggleMediaMenu(e) {
@@ -81,21 +79,6 @@ class PrimaryNav extends TrackedElement {
81
79
  );
82
80
  }
83
81
 
84
- updated(props) {
85
- const { currentTab } = this;
86
- const isUserMenuTab = currentTab && currentTab.mediatype === 'usermenu';
87
- if (props.has('currentTab') && isUserMenuTab) {
88
- console.log(currentTab)
89
- const focusElement = currentTab.moveTo === 'next'
90
- ? this.shadowRoot.querySelector('a.upload')
91
- : this.shadowRoot.querySelector('.media-menu-container');
92
-
93
- if (focusElement) {
94
- focusElement.focus();
95
- }
96
- }
97
- }
98
-
99
82
  get userIcon() {
100
83
  const userMenuClass = this.openMenu === 'user' ? 'active' : '';
101
84
  const userMenuToolTip = this.openMenu === 'user' ? 'Close user menu' : 'Expand user menu';
@@ -169,11 +152,7 @@ class PrimaryNav extends TrackedElement {
169
152
  }
170
153
 
171
154
  get uploadButtonTemplate() {
172
- return html`
173
- <a href="${formatUrl('/create', this.baseHost)}"
174
- class="upload"
175
- @focus=${this.toggleMediaMenu}
176
- >
155
+ return html`<a href="${formatUrl('/create', this.baseHost)}" class="upload">
177
156
  ${icons.upload}
178
157
  <span>Upload</span>
179
158
  </a>`;
@@ -202,15 +181,6 @@ class PrimaryNav extends TrackedElement {
202
181
  const mediaMenuTabIndex = this.openMenu === 'media' ? '' : '-1';
203
182
  return html`
204
183
  <nav class=${this.hideSearch ? 'hide-search' : nothing}>
205
- <button
206
- class="hamburger"
207
- @click="${this.toggleMediaMenu}"
208
- data-event-click-tracking="${this.config.eventCategory}|NavHamburger"
209
- title="Open main menu"
210
- >
211
- <icon-hamburger ?active=${this.openMenu === 'media'}></icon-hamburger>
212
- </button>
213
-
214
184
  <div class=${`branding ${this.secondLogoClass}`}>
215
185
  <a
216
186
  href=${formatUrl('/', this.baseHost)}
@@ -218,25 +188,34 @@ class PrimaryNav extends TrackedElement {
218
188
  data-event-click-tracking="${this.config.eventCategory}|NavHome"
219
189
  title="Go home"
220
190
  class="link-home"
221
- tabindex="-1"
222
191
  >${icons.iaLogo}${logoWordmarkStacked}</a
223
192
  >
224
193
  ${this.secondLogoSlot}
225
194
  </div>
195
+
196
+ <div class="right-side-section">
197
+ ${this.mobileDonateHeart}
198
+ ${this.searchMenu}
199
+ ${this.uploadButtonTemplate}
200
+ ${this.userStateTemplate}
201
+ </div>
226
202
  <media-menu
227
203
  .baseHost=${this.baseHost}
228
204
  .config=${this.config}
229
205
  ?mediaMenuAnimate="${this.mediaMenuAnimate}"
206
+ tabindex="${mediaMenuTabIndex}"
230
207
  .selectedMenuOption=${this.selectedMenuOption}
231
208
  .openMenu=${this.openMenu}
232
- .currentTab=${this.currentTab}
233
209
  ></media-menu>
234
- <div class="right-side-section">
235
- ${this.mobileDonateHeart}
236
- ${this.userStateTemplate}
237
- ${this.uploadButtonTemplate}
238
- ${this.searchMenu}
239
- </div>
210
+ <button
211
+ class="hamburger"
212
+ @click="${this.toggleMediaMenu}"
213
+ tabindex="1"
214
+ data-event-click-tracking="${this.config.eventCategory}|NavHamburger"
215
+ title="Open main menu"
216
+ >
217
+ <icon-hamburger ?active=${this.openMenu === 'media'}></icon-hamburger>
218
+ </button>
240
219
  </nav>
241
220
  `;
242
221
  }
@@ -29,36 +29,6 @@ class SearchMenu extends TrackedElement {
29
29
  this.selectedSearchType = '';
30
30
  }
31
31
 
32
- firstUpdated() {
33
- this.shadowRoot.addEventListener('keydown', e => this.handleKeyDownEvent(e));
34
- }
35
-
36
- disconnectedCallback() {
37
- // Clean up event listener when the element is removed
38
- this.shadowRoot.removeEventListener('keydown', e => this.handleKeyDownEvent(e));
39
- }
40
-
41
- handleKeyDownEvent(e) {
42
- const searchTypes = this.shadowRoot.querySelectorAll('.search-menu-inner label input[type=radio]');
43
-
44
- const length = searchTypes.length - 1;
45
- if (!length) return;
46
-
47
- const searchTypeHandler = (index) => {
48
- e.preventDefault();
49
- const searchType = searchTypes[index];
50
- searchType.checked = true;
51
- searchType.dispatchEvent(new Event('change'));
52
- searchType.focus();
53
- };
54
-
55
- if (e.key === 'Home') {
56
- searchTypeHandler(0);
57
- } else if (e.key === 'End') {
58
- searchTypeHandler(length);
59
- }
60
- }
61
-
62
32
  selectSearchType(e) {
63
33
  this.selectedSearchType = e.target.value;
64
34
  }
@@ -92,7 +92,6 @@ export default css`
92
92
 
93
93
  @media (min-width: 890px) {
94
94
  nav {
95
- display: flex;
96
95
  overflow: visible;
97
96
  top: 0;
98
97
  left: auto;
@@ -147,7 +146,6 @@ export default css`
147
146
  a:focus {
148
147
  color: var(--linkHoverColor);
149
148
  background: var(--linkColor);
150
- outline: none;
151
149
  }
152
150
 
153
151
  .initial,
@@ -23,6 +23,10 @@ export default css`
23
23
 
24
24
  /* Mobile view styles */
25
25
  @media (max-width: 889px) {
26
+ .media-menu-container {
27
+ position: relative;
28
+ }
29
+
26
30
  .media-menu-inner {
27
31
  position: absolute;
28
32
  width: 100%;
@@ -35,7 +39,7 @@ export default css`
35
39
  .overflow-clip {
36
40
  position: absolute;
37
41
  z-index: -1; /** needs to be under the navigation, otherwise it intercepts clicks */
38
- top: 4rem;
42
+ top: 0;
39
43
  left: 0;
40
44
  height: 0;
41
45
  width: 100%;
@@ -9,7 +9,8 @@ export default css`
9
9
 
10
10
  nav {
11
11
  position: relative;
12
- display: flex;
12
+ display: -ms-grid;
13
+ display: grid;
13
14
  height: 4rem;
14
15
  grid-template-areas: 'hamburger empty heart search user';
15
16
  -ms-grid-columns: 4rem minmax(1rem, 100%) 4rem 4rem 4rem;
@@ -28,7 +29,6 @@ export default css`
28
29
 
29
30
  .right-side-section {
30
31
  display: flex;
31
- margin-left: auto;
32
32
  user-select: none;
33
33
  }
34
34
  button {
@@ -86,9 +86,6 @@ export default css`
86
86
  fill: var(--activeColor);
87
87
  }
88
88
 
89
- .mobile-donate-link {
90
- display: inline-block;
91
- }
92
89
  .mobile-donate-link svg {
93
90
  height: 4rem;
94
91
  width: 4rem;
@@ -154,8 +151,7 @@ export default css`
154
151
  height: 100%;
155
152
  }
156
153
 
157
- .user-menu:hover,
158
- .user-menu:focus {
154
+ .user-menu:hover {
159
155
  color: var(--linkHoverColor);
160
156
  }
161
157
 
@@ -191,13 +187,6 @@ export default css`
191
187
  slot[name='opt-sec-logo'] {
192
188
  display: none;
193
189
  }
194
-
195
- .right-side-section {
196
- display: initial;
197
- }
198
- .right-side-section .user-info {
199
- float: right;
200
- }
201
190
  }
202
191
 
203
192
  @media (min-width: 890px) {
@@ -206,8 +195,12 @@ export default css`
206
195
  --userIconHeight: 3.2rem;
207
196
  }
208
197
 
198
+ .right-side-section {
199
+ display: contents;
200
+ }
201
+
209
202
  nav {
210
- display: flex;
203
+ display: block;
211
204
  z-index: 4;
212
205
  height: 5rem;
213
206
  padding-right: 1.5rem;
@@ -43,9 +43,6 @@ export default css`
43
43
  .advanced-search {
44
44
  text-decoration: none;
45
45
  color: var(--linkColor);
46
- line-height: normal;
47
- padding: 0.5rem;
48
- margin-top: 5px;
49
46
  }
50
47
 
51
48
  @media (min-width: 890px) {
@@ -89,8 +86,6 @@ export default css`
89
86
 
90
87
  label {
91
88
  padding: 0;
92
- font-weight: normal;
93
- margin: 0;
94
89
  }
95
90
 
96
91
  label + label {
package/src/user-menu.js CHANGED
@@ -1,7 +1,6 @@
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';
5
4
 
6
5
  class UserMenu extends DropdownMenu {
7
6
  static get styles() {
@@ -22,19 +21,6 @@ class UserMenu extends DropdownMenu {
22
21
  this.username = '';
23
22
  }
24
23
 
25
- updated(props) {
26
- if (props.has('open') && this.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
-
38
24
  render() {
39
25
  return html`
40
26
  <div class="nav-container">
@@ -1,130 +0,0 @@
1
-
2
-
3
-
4
- export default class KeyboardNavigation {
5
- /**
6
- * Constructor for the KeyboardNavigation class.
7
- * @param {HTMLElement} elementsContainer - The container element that holds the focusable elements.
8
- * @param {string} menuOption - The type of menu option ('web' or 'usermenu').
9
- */
10
- constructor(elementsContainer, menuOption) {
11
- this.elementsContainer = elementsContainer;
12
- this.menuOption = menuOption;
13
- this.focusableElements = this.getFocusableElements();
14
- this.focusedIndex = this.getInitialFocusedIndex();
15
-
16
- this.focusableElements[this.focusedIndex]?.focus();
17
- this.handleKeyDown = this.handleKeyDown.bind(this);
18
- }
19
-
20
- /**
21
- * Returns the initial focused index based on the menu option.
22
- * @returns {number} The initial focused index (0 for 'web', 1 for 'usermenu').
23
- */
24
- getInitialFocusedIndex() {
25
- return this.menuOption === 'usermenu' ? 1 : 0;
26
- }
27
-
28
- /**
29
- * Gets an array of focusable elements within the container.
30
- * @returns {HTMLElement[]} An array of focusable elements.
31
- */
32
- getFocusableElements() {
33
- const focusableTagSelectors = 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])';
34
- const isDisabledOrHidden = el => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden');
35
-
36
- let elements;
37
- if (this.menuOption === 'web') {
38
- const waybackSlider = this.elementsContainer.querySelector('wayback-slider').shadowRoot;
39
- const waybackSearch = waybackSlider.querySelector('wayback-search');
40
- const waybackSearchElements = Array.from(waybackSearch.shadowRoot.querySelectorAll(focusableTagSelectors));
41
-
42
- const normalElements = Array.from(waybackSlider.querySelectorAll(focusableTagSelectors));
43
-
44
- const savePageForm = waybackSlider.querySelector('save-page-form');
45
- const savePageFormElements = Array.from(savePageForm.shadowRoot.querySelectorAll(focusableTagSelectors));
46
-
47
- elements = [...waybackSearchElements, ...normalElements, ...savePageFormElements];
48
- } else {
49
- elements = this.elementsContainer.querySelectorAll(focusableTagSelectors);
50
- }
51
-
52
- return Array.from(elements).filter(isDisabledOrHidden);
53
- }
54
-
55
- /**
56
- * Handles keyboard events and focuses the appropriate element.
57
- * @param {KeyboardEvent} event - The keyboard event object.
58
- */
59
- handleKeyDown(event) {
60
- const { key } = event;
61
- const isArrowKey = ['ArrowDown', 'ArrowRight', 'ArrowUp', 'ArrowLeft'].includes(key);
62
- const isTabKey = key === 'Tab';
63
-
64
- if (isArrowKey) {
65
- this.handleArrowKey(key);
66
- event.preventDefault();
67
- } else if (isTabKey) {
68
- this.handleTabKey(event);
69
- }
70
- }
71
-
72
- /**
73
- * Handles arrow key events and focuses the next or previous element.
74
- * @param {string} key - The key that was pressed ('ArrowDown', 'ArrowRight', 'ArrowUp', or 'ArrowLeft').
75
- */
76
- handleArrowKey(key) {
77
- const isDownOrRight = ['ArrowDown', 'ArrowRight'].includes(key);
78
- isDownOrRight ? this.focusNext() : this.focusPrevious();
79
- }
80
-
81
- /**
82
- * Focuses the previous focusable element in the container.
83
- */
84
- focusPrevious() {
85
- if (this.focusableElements.length === 0) return;
86
- this.focusedIndex = (this.focusedIndex - 1 + this.focusableElements.length) % this.focusableElements.length;
87
- this.focusableElements[this.focusedIndex]?.focus();
88
- }
89
-
90
- /**
91
- * Focuses the next focusable element in the container.
92
- */
93
- focusNext() {
94
- if (this.focusableElements.length === 0) return;
95
- this.focusedIndex = (this.focusedIndex + 1) % this.focusableElements.length;
96
- this.focusableElements[this.focusedIndex]?.focus();
97
- }
98
-
99
- /**
100
- * Handles the Tab key event and focuses the next or previous menu item.
101
- * @param {KeyboardEvent} event - The keyboard event object.
102
- */
103
- handleTabKey(event) {
104
- console.log(this.menuOption)
105
- if (this.menuOption) {
106
- const isShiftPressed = event.shiftKey;
107
- this.focusNextMenuItem(isShiftPressed);
108
- }
109
-
110
- this.focusableElements[this.focusedIndex]?.blur();
111
- event.preventDefault();
112
- }
113
-
114
- /**
115
- * Focuses the next or previous menu item based on the provided flag.
116
- * @param {boolean} isPrevious - A flag indicating whether to focus the previous menu item.
117
- */
118
- focusNextMenuItem(isPrevious = false) {
119
- this.elementsContainer.dispatchEvent(
120
- new CustomEvent('moveFocusToOthers', {
121
- bubbles: true,
122
- composed: true,
123
- detail: {
124
- mediatype: this.menuOption,
125
- moveTo: isPrevious ? 'prev' : 'next',
126
- },
127
- })
128
- );
129
- }
130
- }