This repository has been archived by the owner on Mar 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 80
/
header-menu.ts
123 lines (108 loc) · 3.44 KB
/
header-menu.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/**
* @license
*
* Copyright IBM Corp. 2019
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import settings from 'carbon-components/es/globals/js/settings';
import { ifDefined } from 'lit-html/directives/if-defined';
import { html, property, query, customElement, LitElement } from 'lit-element';
import ChevronDownGlyph from '@carbon/icons/lib/chevron--down';
import FocusMixin from '../../globals/mixins/focus';
import HostListenerMixin from '../../globals/mixins/host-listener';
import HostListener from '../../globals/decorators/host-listener';
import styles from './header.scss';
const { prefix } = settings;
/**
* Header menu.
*/
@customElement(`${prefix}-header-menu`)
class BXHeaderMenu extends HostListenerMixin(FocusMixin(LitElement)) {
/**
* The trigger button.
*/
@query('a')
private _trigger!: HTMLElement;
/**
* Handles `click` event handler on this element.
*/
private _handleClick() {
this._handleUserInitiatedToggle();
}
/**
* Handler for the `keydown` event on the trigger button.
*/
private _handleKeydownTrigger({ key }: KeyboardEvent) {
if (key === 'Esc' || key === 'Escape') {
this._handleUserInitiatedToggle(false);
}
}
/**
* Handles user-initiated toggling the open state.
* @param [force] If specified, forces the open state to the given one.
*/
private _handleUserInitiatedToggle(force: boolean = !this.expanded) {
this.expanded = force;
if (!force) {
this._trigger.focus();
}
}
/**
* Handles `blur` event handler on this element.
*/
@HostListener('blur')
// @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to
private _handleBlur({ relatedTarget }: FocusEvent) {
if (!this.contains(relatedTarget as Node)) {
this.expanded = false;
}
}
/**
* `true` if the menu should be expanded. Corresponds to the attribute with the same name.
*/
@property({ type: Boolean, reflect: true })
expanded = false;
/**
* The content of the trigger button. Corresponds to `trigger-content` attribute.
*/
@property({ attribute: 'trigger-content' })
triggerContent = '';
/**
* The `aria-label` attribute for the menu UI. Corresponds to `menu-label` attribute.
*/
@property({ attribute: 'menu-label' })
menuLabel!: string;
createRenderRoot() {
return this.attachShadow({ mode: 'open', delegatesFocus: true });
}
connectedCallback() {
if (!this.hasAttribute('role')) {
this.setAttribute('role', 'listitem');
}
super.connectedCallback();
}
render() {
const { expanded, triggerContent, menuLabel, _handleClick: handleClick, _handleKeydownTrigger: handleKeydownTrigger } = this;
return html`
<a
role="menuitem"
tabindex="0"
class="${prefix}--header__menu-item ${prefix}--header__menu-title"
href="javascript:void 0"
aria-haspopup="menu"
aria-expanded="${String(Boolean(expanded))}"
@click=${handleClick}
@keydown=${handleKeydownTrigger}
>
${triggerContent}${ChevronDownGlyph({ class: `${prefix}--header__menu-arrow` })}
</a>
<ul role="menu" class="${prefix}--header__menu" aria-label="${ifDefined(menuLabel)}">
<slot></slot>
</ul>
`;
}
static styles = styles; // `styles` here is a `CSSResult` generated by custom WebPack loader
}
export default BXHeaderMenu;