2
2
// Licensed under the MIT License.
3
3
4
4
import { 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' ;
6
19
import { 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' ;
7
27
8
- export abstract class FabBaseButtonComponent extends ReactWrapperComponent < IButtonProps > implements OnInit {
28
+ export abstract class FabBaseButtonComponent extends ReactWrapperComponent < IButtonProps >
29
+ implements OnInit , AfterContentInit , OnDestroy {
9
30
@Input ( ) componentRef ?: IButtonProps [ 'componentRef' ] ;
10
31
@Input ( ) href ?: IButtonProps [ 'href' ] ;
11
32
@Input ( ) primary ?: IButtonProps [ 'primary' ] ;
@@ -47,13 +68,18 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent<IButt
47
68
@Output ( ) readonly onMenuClick = new EventEmitter < { ev ?: MouseEvent | KeyboardEvent ; button ?: IButtonProps } > ( ) ;
48
69
@Output ( ) readonly onAfterMenuDismiss = new EventEmitter < void > ( ) ;
49
70
71
+ @ContentChildren ( ContextualMenuItemDirective ) readonly menuItemsDirectives ?: QueryList < ContextualMenuItemDirective > ;
72
+
50
73
onRenderIcon : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
51
74
onRenderText : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
52
75
onRenderDescription : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
53
76
onRenderAriaDescription : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
54
77
onRenderChildren : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
55
78
onRenderMenuIcon : ( props ?: IButtonProps , defaultRender ?: JsxRenderFunc < IButtonProps > ) => JSX . Element ;
56
79
80
+ private _changeableItemsHelper : ChangeableItemsHelper < IContextualMenuItem > ;
81
+ private _subscriptions : Subscription [ ] = [ ] ;
82
+
57
83
constructor ( elementRef : ElementRef , changeDetectorRef : ChangeDetectorRef , renderer : Renderer2 , ngZone : NgZone ) {
58
84
super ( elementRef , changeDetectorRef , renderer , { ngZone, setHostDisplay : true } ) ;
59
85
@@ -70,6 +96,50 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent<IButt
70
96
this . onRenderMenuIcon = this . createRenderPropHandler ( this . renderMenuIcon ) ;
71
97
}
72
98
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
+
73
143
onMenuClickHandler ( ev ?: React . MouseEvent < HTMLElement > | React . KeyboardEvent < HTMLElement > , button ?: IButtonProps ) {
74
144
this . onMenuClick . emit ( {
75
145
ev : ev && ev . nativeEvent ,
@@ -80,4 +150,33 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent<IButt
80
150
onClickHandler ( ev ?: React . MouseEvent ) {
81
151
this . onClick . emit ( ev . nativeEvent ) ;
82
152
}
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
+ }
83
182
}
0 commit comments