22// Licensed under the MIT License.
33
44import { InputRendererOptions , JsxRenderFunc , ReactWrapperComponent } from '@angular-react/core' ;
5- import { ChangeDetectorRef , ElementRef , EventEmitter , Input , NgZone , OnInit , Output , Renderer2 } from '@angular/core' ;
5+ import {
6+ ChangeDetectorRef ,
7+ ElementRef ,
8+ EventEmitter ,
9+ Input ,
10+ NgZone ,
11+ OnInit ,
12+ Output ,
13+ Renderer2 ,
14+ ContentChildren ,
15+ QueryList ,
16+ AfterContentInit ,
17+ OnDestroy ,
18+ } from '@angular/core' ;
619import { IButtonProps } from 'office-ui-fabric-react/lib/Button' ;
20+ import { ContextualMenuItemDirective , IContextualMenuItemOptions } from '../contextual-menu/public-api' ;
21+ import { ChangeableItemsHelper } from '../core/shared/changeable-helper' ;
22+ import { IContextualMenuItem } from 'office-ui-fabric-react' ;
23+ import { Subscription } from 'rxjs' ;
24+ import { CommandBarItemChangedPayload } from '../command-bar/directives/command-bar-item.directives' ;
25+ import { mergeItemChanges } from '../core/declarative/item-changed' ;
26+ import { omit } from '../../utils/omit' ;
727
8- export abstract class FabBaseButtonComponent extends ReactWrapperComponent < IButtonProps > implements OnInit {
28+ export abstract class FabBaseButtonComponent extends ReactWrapperComponent < IButtonProps >
29+ implements OnInit , AfterContentInit , OnDestroy {
930 @Input ( ) componentRef ?: IButtonProps [ 'componentRef' ] ;
1031 @Input ( ) href ?: IButtonProps [ 'href' ] ;
1132 @Input ( ) primary ?: IButtonProps [ 'primary' ] ;
@@ -47,13 +68,18 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent<IButt
4768 @Output ( ) readonly onMenuClick = new EventEmitter < { ev ?: MouseEvent | KeyboardEvent ; button ?: IButtonProps } > ( ) ;
4869 @Output ( ) readonly onAfterMenuDismiss = new EventEmitter < void > ( ) ;
4970
71+ @ContentChildren ( ContextualMenuItemDirective ) readonly menuItemsDirectives ?: QueryList < ContextualMenuItemDirective > ;
72+
5073 onRenderIcon : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
5174 onRenderText : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
5275 onRenderDescription : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
5376 onRenderAriaDescription : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
5477 onRenderChildren : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
5578 onRenderMenuIcon : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
5679
80+ private _changeableItemsHelper : ChangeableItemsHelper < IContextualMenuItem > ;
81+ private _subscriptions : Subscription [ ] = [ ] ;
82+
5783 constructor ( elementRef : ElementRef , changeDetectorRef : ChangeDetectorRef , renderer : Renderer2 , ngZone : NgZone ) {
5884 super ( elementRef , changeDetectorRef , renderer , { ngZone, setHostDisplay : true } ) ;
5985
@@ -70,6 +96,50 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent<IButt
7096 this . onRenderMenuIcon = this . createRenderPropHandler ( this . renderMenuIcon ) ;
7197 }
7298
99+ ngAfterContentInit ( ) {
100+ if ( this . menuItemsDirectives && this . menuItemsDirectives . length > 0 ) {
101+ const setItems = ( directiveItems : ReadonlyArray < ContextualMenuItemDirective > ) => {
102+ const items = directiveItems . map ( directive =>
103+ this . _transformContextualMenuItemOptionsToProps ( this . _directiveToContextualMenuItem ( directive ) )
104+ ) ;
105+ if ( ! this . menuProps ) {
106+ this . menuProps = { items : items } ;
107+ } else {
108+ this . menuProps . items = items ;
109+ }
110+
111+ this . markForCheck ( ) ;
112+ } ;
113+
114+ this . _changeableItemsHelper = new ChangeableItemsHelper ( this . menuItemsDirectives ) ;
115+ this . _subscriptions . push (
116+ this . _changeableItemsHelper . onItemsChanged . subscribe ( ( newItems : QueryList < ContextualMenuItemDirective > ) => {
117+ setItems ( newItems . toArray ( ) ) ;
118+ } ) ,
119+ this . _changeableItemsHelper . onChildItemChanged . subscribe ( ( { key, changes } : CommandBarItemChangedPayload ) => {
120+ const newItems = this . menuItemsDirectives . map ( item =>
121+ item . key === key ? mergeItemChanges ( item , changes ) : item
122+ ) ;
123+ setItems ( newItems ) ;
124+
125+ this . markForCheck ( ) ;
126+ } )
127+ ) ;
128+
129+ setItems ( this . menuItemsDirectives . toArray ( ) ) ;
130+ }
131+ }
132+
133+ ngOnDestroy ( ) {
134+ if ( this . _changeableItemsHelper ) {
135+ this . _changeableItemsHelper . destroy ( ) ;
136+ }
137+
138+ if ( this . _subscriptions ) {
139+ this . _subscriptions . forEach ( subscription => subscription . unsubscribe ( ) ) ;
140+ }
141+ }
142+
73143 onMenuClickHandler ( ev ?: React . MouseEvent < HTMLElement > | React . KeyboardEvent < HTMLElement > , button ?: IButtonProps ) {
74144 this . onMenuClick . emit ( {
75145 ev : ev && ev . nativeEvent ,
@@ -80,4 +150,33 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent<IButt
80150 onClickHandler ( ev ?: React . MouseEvent ) {
81151 this . onClick . emit ( ev . nativeEvent ) ;
82152 }
153+
154+ private _directiveToContextualMenuItem ( directive : ContextualMenuItemDirective ) : IContextualMenuItemOptions {
155+ return {
156+ ...directive ,
157+ onClick : ( ev , item ) => {
158+ directive . click . emit ( { ev : ev && ev . nativeEvent , item : item } ) ;
159+ } ,
160+ } ;
161+ }
162+
163+ private _transformContextualMenuItemOptionsToProps ( itemOptions : IContextualMenuItemOptions ) : IContextualMenuItem {
164+ const sharedProperties = omit ( itemOptions , 'renderIcon' , 'render' ) ;
165+
166+ // Legacy render mode is used for the icon because otherwise the icon is to the right of the text (instead of the usual left)
167+ const iconRenderer = this . createInputJsxRenderer ( itemOptions . renderIcon , { legacyRenderMode : true } ) ;
168+ const renderer = this . createInputJsxRenderer ( itemOptions . render ) ;
169+
170+ return Object . assign (
171+ { } ,
172+ sharedProperties ,
173+ iconRenderer && {
174+ onRenderIcon : ( item : IContextualMenuItem ) => iconRenderer ( { contextualMenuItem : item } ) ,
175+ } ,
176+ renderer &&
177+ ( {
178+ onRender : ( item , dismissMenu ) => renderer ( { item, dismissMenu } ) ,
179+ } as Pick < IContextualMenuItem , 'onRender' > )
180+ ) as IContextualMenuItem ;
181+ }
83182}
0 commit comments