@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 +1 -1
- package/src/ia-topnav.js +5 -0
- package/src/lib/keyboard-navigation.js +124 -0
- package/src/login-button.js +2 -2
- package/src/media-button.js +20 -1
- package/src/media-menu.js +95 -1
- package/src/media-slider.js +21 -7
- package/src/nav-search.js +0 -1
- package/src/primary-nav.js +18 -20
- package/src/search-menu.js +1 -2
- package/src/styles/dropdown-menu.js +2 -0
- package/src/styles/media-menu.js +2 -2
- package/src/styles/primary-nav.js +16 -7
- package/src/user-menu.js +14 -0
package/package.json
CHANGED
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
|
+
}
|
package/src/login-button.js
CHANGED
|
@@ -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
|
`;
|
package/src/media-button.js
CHANGED
|
@@ -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' ? '' : '
|
|
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">
|
package/src/media-slider.js
CHANGED
|
@@ -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
package/src/primary-nav.js
CHANGED
|
@@ -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
|
-
<
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
}
|
package/src/search-menu.js
CHANGED
|
@@ -100,7 +100,7 @@ class SearchMenu extends TrackedElement {
|
|
|
100
100
|
}
|
|
101
101
|
return html`
|
|
102
102
|
<label @click="${this.selectSearchType}">
|
|
103
|
-
<input
|
|
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,
|
package/src/styles/media-menu.js
CHANGED
|
@@ -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:
|
|
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:
|
|
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">
|