@internetarchive/ia-topnav 1.4.1-alpha-webdev8259.0 → 1.4.1-alpha-webdev8259.1
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/dist/index.d.ts +0 -1
- package/dist/index.js.map +1 -1
- package/dist/src/data/menus.js.map +1 -1
- package/dist/src/dropdown-menu.js +26 -26
- package/dist/src/dropdown-menu.js.map +1 -1
- package/dist/src/ia-topnav.d.ts +2 -13
- package/dist/src/ia-topnav.js +70 -140
- package/dist/src/ia-topnav.js.map +1 -1
- package/dist/src/lib/keyboard-navigation.js.map +1 -1
- package/dist/src/login-button.js +17 -17
- package/dist/src/login-button.js.map +1 -1
- package/dist/src/media-menu.js +21 -21
- package/dist/src/media-menu.js.map +1 -1
- package/dist/src/models.d.ts +0 -1
- package/dist/src/models.js.map +1 -1
- package/dist/src/primary-nav.d.ts +6 -7
- package/dist/src/primary-nav.js +98 -142
- package/dist/src/primary-nav.js.map +1 -1
- package/dist/src/styles/login-button.js +87 -87
- package/dist/src/styles/login-button.js.map +1 -1
- package/dist/src/styles/primary-nav.js +343 -343
- package/dist/src/styles/primary-nav.js.map +1 -1
- package/dist/src/user-menu.js +13 -13
- package/dist/src/user-menu.js.map +1 -1
- package/dist/test/ia-topnav.test.js +15 -87
- package/dist/test/ia-topnav.test.js.map +1 -1
- package/dist/test/primary-nav.test.js +16 -34
- package/dist/test/primary-nav.test.js.map +1 -1
- package/index.ts +3 -4
- package/package.json +1 -1
- package/src/data/menus.ts +652 -652
- package/src/dropdown-menu.ts +132 -132
- package/src/ia-topnav.ts +301 -383
- package/src/lib/keyboard-navigation.ts +166 -166
- package/src/login-button.ts +78 -78
- package/src/media-menu.ts +143 -143
- package/src/models.ts +63 -65
- package/src/primary-nav.ts +277 -324
- package/src/styles/login-button.ts +90 -90
- package/src/styles/primary-nav.ts +346 -346
- package/src/user-menu.ts +32 -32
- package/test/ia-topnav.test.ts +282 -381
- package/test/primary-nav.test.ts +136 -163
|
@@ -1,166 +1,166 @@
|
|
|
1
|
-
export default class KeyboardNavigation {
|
|
2
|
-
elementsContainer: HTMLElement;
|
|
3
|
-
menuOption: string;
|
|
4
|
-
focusableElements: HTMLElement[];
|
|
5
|
-
focusedIndex: number;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Constructor for the KeyboardNavigation class.
|
|
9
|
-
* @param {HTMLElement} elementsContainer - The container element that holds the focusable elements.
|
|
10
|
-
* @param {string} menuOption - The type of menu option ('web' or 'usermenu').
|
|
11
|
-
*/
|
|
12
|
-
constructor(elementsContainer: HTMLElement, menuOption: string) {
|
|
13
|
-
this.elementsContainer = elementsContainer;
|
|
14
|
-
this.menuOption = menuOption;
|
|
15
|
-
this.focusableElements = this.getFocusableElements();
|
|
16
|
-
this.focusedIndex = 0; // always start from first element
|
|
17
|
-
|
|
18
|
-
if (menuOption !== 'search') {
|
|
19
|
-
this.focusableElements[this.focusedIndex]?.focus();
|
|
20
|
-
}
|
|
21
|
-
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Gets an array of focusable elements within the container.
|
|
26
|
-
* @returns {HTMLElement[]} An array of focusable elements.
|
|
27
|
-
*/
|
|
28
|
-
getFocusableElements(): HTMLElement[] {
|
|
29
|
-
const focusableTagSelectors = 'a[href], button, input, [tabindex]';
|
|
30
|
-
|
|
31
|
-
const isFocusable = (el: Element) =>
|
|
32
|
-
!el.hasAttribute('disabled') &&
|
|
33
|
-
el.getAttribute('aria-hidden') !== 'true' &&
|
|
34
|
-
el.getAttribute('tabindex') !== '-1';
|
|
35
|
-
|
|
36
|
-
let elements;
|
|
37
|
-
if (this.menuOption === 'web') {
|
|
38
|
-
// wayback focusable elements
|
|
39
|
-
const waybackSlider =
|
|
40
|
-
this.elementsContainer.querySelector('wayback-slider')?.shadowRoot;
|
|
41
|
-
const waybackSearch = waybackSlider?.querySelector('wayback-search');
|
|
42
|
-
const waybackSearchElements = Array.from(
|
|
43
|
-
waybackSearch?.shadowRoot?.querySelectorAll(focusableTagSelectors) ??
|
|
44
|
-
[],
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
const normalElements = Array.from(
|
|
48
|
-
waybackSlider?.querySelectorAll(focusableTagSelectors) ?? [],
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// wayback save-form focusable elements
|
|
52
|
-
const savePageForm = waybackSlider?.querySelector('save-page-form');
|
|
53
|
-
const savePageFormElements = Array.from(
|
|
54
|
-
savePageForm?.shadowRoot?.querySelectorAll(focusableTagSelectors) ?? [],
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
elements = [
|
|
58
|
-
...waybackSearchElements,
|
|
59
|
-
...normalElements,
|
|
60
|
-
...savePageFormElements,
|
|
61
|
-
];
|
|
62
|
-
} else {
|
|
63
|
-
elements = this.elementsContainer.querySelectorAll(focusableTagSelectors);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return Array.from(elements ?? []).filter(isFocusable) as HTMLElement[];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Handles keyboard events and focuses the appropriate element.
|
|
71
|
-
* @param {KeyboardEvent} event - The keyboard event object.
|
|
72
|
-
*/
|
|
73
|
-
handleKeyDown(event: KeyboardEvent) {
|
|
74
|
-
const target = event.composedPath()[0] as HTMLElement;
|
|
75
|
-
|
|
76
|
-
// Ignore events from editable fields
|
|
77
|
-
if (
|
|
78
|
-
target instanceof HTMLInputElement ||
|
|
79
|
-
target instanceof HTMLTextAreaElement ||
|
|
80
|
-
target.isContentEditable
|
|
81
|
-
) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const { key } = event;
|
|
86
|
-
const isArrowKey = [
|
|
87
|
-
'ArrowDown',
|
|
88
|
-
'ArrowRight',
|
|
89
|
-
'ArrowUp',
|
|
90
|
-
'ArrowLeft',
|
|
91
|
-
].includes(key);
|
|
92
|
-
const isTabKey = key === 'Tab';
|
|
93
|
-
|
|
94
|
-
if (isArrowKey) {
|
|
95
|
-
this.handleArrowKey(key);
|
|
96
|
-
event.preventDefault();
|
|
97
|
-
} else if (isTabKey) {
|
|
98
|
-
this.handleTabKey(event);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Handles arrow key events and focuses the next or previous element for topnav sub-nav and usermenu
|
|
104
|
-
* @param {string} key - The key that was pressed ('ArrowDown', 'ArrowRight', 'ArrowUp', or 'ArrowLeft').
|
|
105
|
-
*/
|
|
106
|
-
handleArrowKey(key: string) {
|
|
107
|
-
const isDownOrRight = ['ArrowDown', 'ArrowRight'].includes(key);
|
|
108
|
-
if (isDownOrRight) {
|
|
109
|
-
this.focusNext();
|
|
110
|
-
} else {
|
|
111
|
-
this.focusPrevious();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Handles the Tab key event and focuses the next or previous menu item.
|
|
117
|
-
* @param {KeyboardEvent} event - The keyboard event object.
|
|
118
|
-
*/
|
|
119
|
-
handleTabKey(event: KeyboardEvent) {
|
|
120
|
-
const isShiftPressed = event.shiftKey;
|
|
121
|
-
|
|
122
|
-
this.emitFocusToOtherMenuItems(isShiftPressed);
|
|
123
|
-
|
|
124
|
-
this.focusableElements[this.focusedIndex]?.blur();
|
|
125
|
-
if (!['search'].includes(this.menuOption)) {
|
|
126
|
-
event.preventDefault();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Focuses the previous focusable element in the container.
|
|
132
|
-
*/
|
|
133
|
-
focusPrevious() {
|
|
134
|
-
if (this.focusableElements.length === 0) return;
|
|
135
|
-
this.focusedIndex =
|
|
136
|
-
(this.focusedIndex - 1 + this.focusableElements.length) %
|
|
137
|
-
this.focusableElements.length;
|
|
138
|
-
this.focusableElements[this.focusedIndex]?.focus();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Focuses the next focusable element in the container.
|
|
143
|
-
*/
|
|
144
|
-
focusNext() {
|
|
145
|
-
if (this.focusableElements.length === 0) return;
|
|
146
|
-
this.focusedIndex = (this.focusedIndex + 1) % this.focusableElements.length;
|
|
147
|
-
this.focusableElements[this.focusedIndex]?.focus();
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Focuses the other parent menu items based on the provided flag.
|
|
152
|
-
* @param {boolean} isPrevious - A flag indicating whether to focus the previous menu item.
|
|
153
|
-
*/
|
|
154
|
-
emitFocusToOtherMenuItems(isPrevious: boolean = false) {
|
|
155
|
-
this.elementsContainer.dispatchEvent(
|
|
156
|
-
new CustomEvent('focusToOtherMenuItem', {
|
|
157
|
-
bubbles: true,
|
|
158
|
-
composed: true,
|
|
159
|
-
detail: {
|
|
160
|
-
mediatype: this.menuOption,
|
|
161
|
-
moveTo: isPrevious ? 'prev' : 'next',
|
|
162
|
-
},
|
|
163
|
-
}),
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
1
|
+
export default class KeyboardNavigation {
|
|
2
|
+
elementsContainer: HTMLElement;
|
|
3
|
+
menuOption: string;
|
|
4
|
+
focusableElements: HTMLElement[];
|
|
5
|
+
focusedIndex: number;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Constructor for the KeyboardNavigation class.
|
|
9
|
+
* @param {HTMLElement} elementsContainer - The container element that holds the focusable elements.
|
|
10
|
+
* @param {string} menuOption - The type of menu option ('web' or 'usermenu').
|
|
11
|
+
*/
|
|
12
|
+
constructor(elementsContainer: HTMLElement, menuOption: string) {
|
|
13
|
+
this.elementsContainer = elementsContainer;
|
|
14
|
+
this.menuOption = menuOption;
|
|
15
|
+
this.focusableElements = this.getFocusableElements();
|
|
16
|
+
this.focusedIndex = 0; // always start from first element
|
|
17
|
+
|
|
18
|
+
if (menuOption !== 'search') {
|
|
19
|
+
this.focusableElements[this.focusedIndex]?.focus();
|
|
20
|
+
}
|
|
21
|
+
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gets an array of focusable elements within the container.
|
|
26
|
+
* @returns {HTMLElement[]} An array of focusable elements.
|
|
27
|
+
*/
|
|
28
|
+
getFocusableElements(): HTMLElement[] {
|
|
29
|
+
const focusableTagSelectors = 'a[href], button, input, [tabindex]';
|
|
30
|
+
|
|
31
|
+
const isFocusable = (el: Element) =>
|
|
32
|
+
!el.hasAttribute('disabled') &&
|
|
33
|
+
el.getAttribute('aria-hidden') !== 'true' &&
|
|
34
|
+
el.getAttribute('tabindex') !== '-1';
|
|
35
|
+
|
|
36
|
+
let elements;
|
|
37
|
+
if (this.menuOption === 'web') {
|
|
38
|
+
// wayback focusable elements
|
|
39
|
+
const waybackSlider =
|
|
40
|
+
this.elementsContainer.querySelector('wayback-slider')?.shadowRoot;
|
|
41
|
+
const waybackSearch = waybackSlider?.querySelector('wayback-search');
|
|
42
|
+
const waybackSearchElements = Array.from(
|
|
43
|
+
waybackSearch?.shadowRoot?.querySelectorAll(focusableTagSelectors) ??
|
|
44
|
+
[],
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const normalElements = Array.from(
|
|
48
|
+
waybackSlider?.querySelectorAll(focusableTagSelectors) ?? [],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// wayback save-form focusable elements
|
|
52
|
+
const savePageForm = waybackSlider?.querySelector('save-page-form');
|
|
53
|
+
const savePageFormElements = Array.from(
|
|
54
|
+
savePageForm?.shadowRoot?.querySelectorAll(focusableTagSelectors) ?? [],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
elements = [
|
|
58
|
+
...waybackSearchElements,
|
|
59
|
+
...normalElements,
|
|
60
|
+
...savePageFormElements,
|
|
61
|
+
];
|
|
62
|
+
} else {
|
|
63
|
+
elements = this.elementsContainer.querySelectorAll(focusableTagSelectors);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return Array.from(elements ?? []).filter(isFocusable) as HTMLElement[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Handles keyboard events and focuses the appropriate element.
|
|
71
|
+
* @param {KeyboardEvent} event - The keyboard event object.
|
|
72
|
+
*/
|
|
73
|
+
handleKeyDown(event: KeyboardEvent) {
|
|
74
|
+
const target = event.composedPath()[0] as HTMLElement;
|
|
75
|
+
|
|
76
|
+
// Ignore events from editable fields
|
|
77
|
+
if (
|
|
78
|
+
target instanceof HTMLInputElement ||
|
|
79
|
+
target instanceof HTMLTextAreaElement ||
|
|
80
|
+
target.isContentEditable
|
|
81
|
+
) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const { key } = event;
|
|
86
|
+
const isArrowKey = [
|
|
87
|
+
'ArrowDown',
|
|
88
|
+
'ArrowRight',
|
|
89
|
+
'ArrowUp',
|
|
90
|
+
'ArrowLeft',
|
|
91
|
+
].includes(key);
|
|
92
|
+
const isTabKey = key === 'Tab';
|
|
93
|
+
|
|
94
|
+
if (isArrowKey) {
|
|
95
|
+
this.handleArrowKey(key);
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
} else if (isTabKey) {
|
|
98
|
+
this.handleTabKey(event);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Handles arrow key events and focuses the next or previous element for topnav sub-nav and usermenu
|
|
104
|
+
* @param {string} key - The key that was pressed ('ArrowDown', 'ArrowRight', 'ArrowUp', or 'ArrowLeft').
|
|
105
|
+
*/
|
|
106
|
+
handleArrowKey(key: string) {
|
|
107
|
+
const isDownOrRight = ['ArrowDown', 'ArrowRight'].includes(key);
|
|
108
|
+
if (isDownOrRight) {
|
|
109
|
+
this.focusNext();
|
|
110
|
+
} else {
|
|
111
|
+
this.focusPrevious();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Handles the Tab key event and focuses the next or previous menu item.
|
|
117
|
+
* @param {KeyboardEvent} event - The keyboard event object.
|
|
118
|
+
*/
|
|
119
|
+
handleTabKey(event: KeyboardEvent) {
|
|
120
|
+
const isShiftPressed = event.shiftKey;
|
|
121
|
+
|
|
122
|
+
this.emitFocusToOtherMenuItems(isShiftPressed);
|
|
123
|
+
|
|
124
|
+
this.focusableElements[this.focusedIndex]?.blur();
|
|
125
|
+
if (!['search'].includes(this.menuOption)) {
|
|
126
|
+
event.preventDefault();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Focuses the previous focusable element in the container.
|
|
132
|
+
*/
|
|
133
|
+
focusPrevious() {
|
|
134
|
+
if (this.focusableElements.length === 0) return;
|
|
135
|
+
this.focusedIndex =
|
|
136
|
+
(this.focusedIndex - 1 + this.focusableElements.length) %
|
|
137
|
+
this.focusableElements.length;
|
|
138
|
+
this.focusableElements[this.focusedIndex]?.focus();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Focuses the next focusable element in the container.
|
|
143
|
+
*/
|
|
144
|
+
focusNext() {
|
|
145
|
+
if (this.focusableElements.length === 0) return;
|
|
146
|
+
this.focusedIndex = (this.focusedIndex + 1) % this.focusableElements.length;
|
|
147
|
+
this.focusableElements[this.focusedIndex]?.focus();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Focuses the other parent menu items based on the provided flag.
|
|
152
|
+
* @param {boolean} isPrevious - A flag indicating whether to focus the previous menu item.
|
|
153
|
+
*/
|
|
154
|
+
emitFocusToOtherMenuItems(isPrevious: boolean = false) {
|
|
155
|
+
this.elementsContainer.dispatchEvent(
|
|
156
|
+
new CustomEvent('focusToOtherMenuItem', {
|
|
157
|
+
bubbles: true,
|
|
158
|
+
composed: true,
|
|
159
|
+
detail: {
|
|
160
|
+
mediatype: this.menuOption,
|
|
161
|
+
moveTo: isPrevious ? 'prev' : 'next',
|
|
162
|
+
},
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
package/src/login-button.ts
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import { html } from 'lit';
|
|
2
|
-
import TrackedElement from './tracked-element';
|
|
3
|
-
import icons from './assets/img/icons';
|
|
4
|
-
import loginButtonCSS from './styles/login-button';
|
|
5
|
-
import formatUrl from './lib/format-url';
|
|
6
|
-
import { makeBooleanString } from './lib/make-boolean-string';
|
|
7
|
-
import { customElement, property, state } from 'lit/decorators.js';
|
|
8
|
-
import { IATopNavConfig } from './models';
|
|
9
|
-
import { defaultTopNavConfig } from './data/menus';
|
|
10
|
-
|
|
11
|
-
@customElement('login-button')
|
|
12
|
-
export class LoginButton extends TrackedElement {
|
|
13
|
-
@property({ type: String }) baseHost = '';
|
|
14
|
-
@property({ type: Object }) config: IATopNavConfig = defaultTopNavConfig;
|
|
15
|
-
@property({ type: String }) openMenu = '';
|
|
16
|
-
|
|
17
|
-
@state() private dropdownTabIndex = '';
|
|
18
|
-
|
|
19
|
-
static get styles() {
|
|
20
|
-
return loginButtonCSS;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
get signupPath() {
|
|
24
|
-
return formatUrl('/account/signup', this.baseHost);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
get loginPath() {
|
|
28
|
-
return formatUrl('/login', this.baseHost);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get analyticsEvent() {
|
|
32
|
-
return `${this.config?.eventCategory}|NavLoginIcon`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
get menuOpened(): boolean {
|
|
36
|
-
return this.openMenu === 'login';
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
get avatarClass() {
|
|
40
|
-
return `dropdown-toggle${this.menuOpened ? ' active' : ''}`;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
toggleDropdown(e: Event) {
|
|
44
|
-
e.preventDefault();
|
|
45
|
-
this.trackClick(e);
|
|
46
|
-
this.dropdownTabIndex = this.menuOpened ? '' : '-1';
|
|
47
|
-
this.dispatchEvent(
|
|
48
|
-
new CustomEvent('menuToggled', {
|
|
49
|
-
bubbles: true,
|
|
50
|
-
composed: true,
|
|
51
|
-
detail: {
|
|
52
|
-
menuName: 'login',
|
|
53
|
-
},
|
|
54
|
-
}),
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
render() {
|
|
59
|
-
return html`
|
|
60
|
-
<div class="logged-out-toolbar">
|
|
61
|
-
<button
|
|
62
|
-
class="logged-out-menu ${this.avatarClass}"
|
|
63
|
-
@click=${this.toggleDropdown}
|
|
64
|
-
data-event-click-tracking="${this.analyticsEvent}"
|
|
65
|
-
aria-label="Toggle login menu"
|
|
66
|
-
aria-expanded="${makeBooleanString(this.menuOpened)}"
|
|
67
|
-
>
|
|
68
|
-
${icons.user}
|
|
69
|
-
</button>
|
|
70
|
-
<span>
|
|
71
|
-
<a href="${this.signupPath}">Sign up</a>
|
|
72
|
-
|
|
|
73
|
-
<a href="${this.loginPath}">Log in</a>
|
|
74
|
-
</span>
|
|
75
|
-
</div>
|
|
76
|
-
`;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import TrackedElement from './tracked-element';
|
|
3
|
+
import icons from './assets/img/icons';
|
|
4
|
+
import loginButtonCSS from './styles/login-button';
|
|
5
|
+
import formatUrl from './lib/format-url';
|
|
6
|
+
import { makeBooleanString } from './lib/make-boolean-string';
|
|
7
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
8
|
+
import { IATopNavConfig } from './models';
|
|
9
|
+
import { defaultTopNavConfig } from './data/menus';
|
|
10
|
+
|
|
11
|
+
@customElement('login-button')
|
|
12
|
+
export class LoginButton extends TrackedElement {
|
|
13
|
+
@property({ type: String }) baseHost = '';
|
|
14
|
+
@property({ type: Object }) config: IATopNavConfig = defaultTopNavConfig;
|
|
15
|
+
@property({ type: String }) openMenu = '';
|
|
16
|
+
|
|
17
|
+
@state() private dropdownTabIndex = '';
|
|
18
|
+
|
|
19
|
+
static get styles() {
|
|
20
|
+
return loginButtonCSS;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get signupPath() {
|
|
24
|
+
return formatUrl('/account/signup', this.baseHost);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get loginPath() {
|
|
28
|
+
return formatUrl('/login', this.baseHost);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get analyticsEvent() {
|
|
32
|
+
return `${this.config?.eventCategory}|NavLoginIcon`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get menuOpened(): boolean {
|
|
36
|
+
return this.openMenu === 'login';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get avatarClass() {
|
|
40
|
+
return `dropdown-toggle${this.menuOpened ? ' active' : ''}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
toggleDropdown(e: Event) {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
this.trackClick(e);
|
|
46
|
+
this.dropdownTabIndex = this.menuOpened ? '' : '-1';
|
|
47
|
+
this.dispatchEvent(
|
|
48
|
+
new CustomEvent('menuToggled', {
|
|
49
|
+
bubbles: true,
|
|
50
|
+
composed: true,
|
|
51
|
+
detail: {
|
|
52
|
+
menuName: 'login',
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
render() {
|
|
59
|
+
return html`
|
|
60
|
+
<div class="logged-out-toolbar">
|
|
61
|
+
<button
|
|
62
|
+
class="logged-out-menu ${this.avatarClass}"
|
|
63
|
+
@click=${this.toggleDropdown}
|
|
64
|
+
data-event-click-tracking="${this.analyticsEvent}"
|
|
65
|
+
aria-label="Toggle login menu"
|
|
66
|
+
aria-expanded="${makeBooleanString(this.menuOpened)}"
|
|
67
|
+
>
|
|
68
|
+
${icons.user}
|
|
69
|
+
</button>
|
|
70
|
+
<span>
|
|
71
|
+
<a href="${this.signupPath}">Sign up</a>
|
|
72
|
+
|
|
|
73
|
+
<a href="${this.loginPath}">Log in</a>
|
|
74
|
+
</span>
|
|
75
|
+
</div>
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
}
|