@@ -10,6 +10,9 @@ import { property } from 'lit/decorators.js';
1010import { classMap } from 'lit/directives/class-map.js' ;
1111import { MgtBaseComponent , customElement } from '@microsoft/mgt-element' ;
1212import { styles } from './mgt-arrow-options-css' ;
13+ import { registerFluentComponents } from '../../../utils/FluentComponents' ;
14+ import { fluentMenu , fluentMenuItem , fluentButton } from '@fluentui/web-components' ;
15+ registerFluentComponents ( fluentMenu , fluentMenuItem , fluentButton ) ;
1316
1417/*
1518 Ok, the name here deserves a bit of explanation,
@@ -22,6 +25,12 @@ import { styles } from './mgt-arrow-options-css';
2225/**
2326 * Custom Component used to handle an arrow rendering for TaskGroups utilized in the task component.
2427 *
28+ * @cssprop --arrow-options-left {Length} The distance of the dropdown menu from the left in absolute position. Default is 0.
29+ * @cssprop --arrow-options-button-background-color {Color} The background color of the arrow options button.
30+ * @cssprop --arrow-options-button-font-size {Length} The font size of the button text. Default is large.
31+ * @cssprop --arrow-options-button-font-weight {Length} The font weight of the button text. Default is 600.
32+ * @cssprop --arrow-options-button-font-color {Color} The font color of the text in the button.
33+ *
2534 * @export MgtArrowOptions
2635 * @class MgtArrowOptions
2736 * @extends {MgtBaseComponent }
@@ -53,20 +62,21 @@ export class MgtArrowOptions extends MgtBaseComponent {
5362 @property ( { type : String } ) public value : string ;
5463
5564 /**
56- * Menu options to be rendered with an attached MouseEvent handler for expansion of details
65+ * Menu options to be rendered with an attached UIEvent handler for expansion of details
5766 *
5867 * @type {object }
5968 * @memberof MgtArrowOptions
6069 */
61- @property ( { type : Object } ) public options : { [ name : string ] : ( e : MouseEvent ) => any | void } ;
70+ @property ( { type : Object } ) public options : { [ name : string ] : ( e : UIEvent ) => any | void } ;
6271
63- private _clickHandler : ( e : MouseEvent ) => void | any ;
72+ private _clickHandler : ( e : UIEvent ) => void | any ;
6473
6574 constructor ( ) {
6675 super ( ) ;
6776 this . value = '' ;
6877 this . options = { } ;
69- this . _clickHandler = ( e : MouseEvent ) => ( this . open = false ) ;
78+ this . _clickHandler = ( ) => ( this . open = false ) ;
79+ window . addEventListener ( 'onblur' , ( ) => ( this . open = false ) ) ;
7080 }
7181
7282 // eslint-disable-next-line @typescript-eslint/tslint/config
@@ -96,41 +106,90 @@ export class MgtArrowOptions extends MgtBaseComponent {
96106 }
97107 } ;
98108
109+ /**
110+ * Handles key down presses done on the header element.
111+ *
112+ * @param {KeyboardEvent } e
113+ */
114+ private onHeaderKeyDown = ( e : KeyboardEvent ) => {
115+ if ( e . key === 'Enter' ) {
116+ e . preventDefault ( ) ;
117+ e . stopPropagation ( ) ;
118+ this . open = ! this . open ;
119+
120+ // Manually adding the 'open' class to display the menu because
121+ // by the time I set the first element's focus, the classes are not
122+ // updated and that has no effect. You can't set focus on elements
123+ // that have no display.
124+ const fluentMenuEl : HTMLElement = this . renderRoot . querySelector ( 'fluent-menu' ) ;
125+ if ( fluentMenuEl ) {
126+ fluentMenuEl . classList . remove ( 'closed' ) ;
127+ fluentMenuEl . classList . add ( 'open' ) ;
128+ }
129+
130+ const header : HTMLButtonElement = e . target as HTMLButtonElement ;
131+ if ( header ) {
132+ const firstMenuItem : HTMLElement = this . renderRoot . querySelector ( "fluent-menu-item[tabindex='0']" ) ;
133+ if ( firstMenuItem ) {
134+ header . blur ( ) ;
135+ firstMenuItem . focus ( ) ;
136+ }
137+ }
138+ }
139+ } ;
140+
99141 /**
100142 * Invoked on each update to perform rendering tasks. This method must return
101143 * a lit-html TemplateResult. Setting properties inside this method will *not*
102144 * trigger the element to update.
103145 */
104146 public render ( ) {
105147 return html `
106- < span class ="header " @click =${ this . onHeaderClick } >
107- < span class ="current-value "> ${ this . value } </ span >
108- </ span >
109- < div class =${ classMap ( { menu : true , open : this . open , closed : ! this . open } ) } >
110- ${ this . getMenuOptions ( ) }
111- </ div >
112- ` ;
148+ < fluent-button
149+ class ="header "
150+ @click =${ this . onHeaderClick }
151+ @keydown =${ this . onHeaderKeyDown }
152+ appearance="lightweight">
153+ ${ this . value }
154+ </ fluent-button >
155+ < fluent-menu
156+ class =${ classMap ( { menu : true , open : this . open , closed : ! this . open } ) } >
157+ ${ this . getMenuOptions ( ) }
158+ </ fluent-menu > ` ;
113159 }
114160
115161 private getMenuOptions ( ) {
116162 const keys = Object . keys ( this . options ) ;
117- const funcs = this . options ;
118-
119- return keys . map (
120- opt => html `
121- < div
122- class ="menu-option "
123- @click ="${ ( e : MouseEvent ) => {
124- this . open = false ;
125- funcs [ opt ] ( e ) ;
126- } } "
127- >
128- < span class =${ classMap ( { 'menu-option-check' : true , 'current-value' : this . value === opt } ) } >
129- \uE73E
130- </ span >
131- < span class ="menu-option-name "> ${ opt } </ span >
132- </ div >
133- `
134- ) ;
163+
164+ return keys . map ( ( opt : string ) => {
165+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
166+ const clickFn = ( e : MouseEvent ) => {
167+ this . open = false ;
168+ this . options [ opt ] ( e ) ;
169+ } ;
170+
171+ const keyDownFn = ( e : KeyboardEvent ) => {
172+ const header : HTMLButtonElement = this . renderRoot . querySelector < HTMLButtonElement > ( '.header' ) ;
173+ if ( e . key === 'Enter' ) {
174+ this . open = false ;
175+ this . options [ opt ] ( e ) ;
176+ header . focus ( ) ;
177+ } else if ( e . key === 'Tab' ) {
178+ this . open = false ;
179+ } else if ( e . key === 'Escape' ) {
180+ this . open = false ;
181+ if ( header ) {
182+ header . focus ( ) ;
183+ }
184+ }
185+ } ;
186+
187+ return html `
188+ < fluent-menu-item
189+ @click =${ clickFn }
190+ @keydown =${ keyDownFn } >
191+ ${ opt }
192+ </ fluent-menu-item > ` ;
193+ } ) ;
135194 }
136195}
0 commit comments