Skip to content

Commit 6f002f9

Browse files
authored
Merge pull request #14 from bengry/declarative-pattern
Declarative syntax for Fabric CommandBar
2 parents 64ac354 + 8416429 commit 6f002f9

29 files changed

+665
-230
lines changed

.vscode/launch.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"type": "chrome",
6+
"request": "launch",
7+
"name": "Launch Chrome against localhost",
8+
"url": "http://localhost:4001",
9+
"webRoot": "${workspaceFolder}"
10+
}
11+
]
12+
}

apps/demo/src/app/app.component.html

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,39 @@ <h2>Getting up and running...</h2>
1616
</div>
1717

1818
<div>
19-
<fab-command-bar #commandBar [items]="commandBarItems" [farItems]="commandBarFarItems">
19+
<fab-command-bar>
20+
<items>
21+
<fab-command-bar-item key="run" text="Run" [iconProps]="{ iconName: 'CaretRight' }" [disabled]="runDisabled"></fab-command-bar-item>
22+
<fab-command-bar-item key="new" text="New" [iconProps]="{ iconName: 'Add' }" (click)="onNewClicked()"></fab-command-bar-item>
23+
<fab-command-bar-item key="save" text="Save" [iconProps]="{ iconName: 'Save' }" [subMenuProps]="saveSubMenuProps">
24+
<contextual-menu-item *ngIf="runDisabled" key="sometimesVisible" text="woosh"></contextual-menu-item>
25+
<contextual-menu-item key="save" text="Save"></contextual-menu-item>
26+
<contextual-menu-item key="save-as" text="Save As" (click)="onSaveAsClicked()">
27+
<contextual-menu-item key="save-as-1" text="Save As 1" (click)="onSaveAsFirstClicked()"></contextual-menu-item>
28+
<contextual-menu-item key="save-as-2" text="Save As 2" (click)="onSaveAsSecondClicked()"></contextual-menu-item>
29+
</contextual-menu-item>
30+
</fab-command-bar-item>
31+
<fab-command-bar-item key="copy" text="Copy" [iconProps]="{ iconName: 'Copy' }" (click)="onCopyClicked()"></fab-command-bar-item>
32+
<fab-command-bar-item key="custom" text="custom text" (click)="onCopyClicked()">
33+
<render>
34+
<ng-template let-item="item">
35+
<counter></counter>
36+
</ng-template>
37+
</render>
38+
39+
<!-- <render-icon>
40+
<ng-template let-contextualMenuItemProps="contextualMenuItemProps">
41+
<div>custom icon</div>
42+
</ng-template>
43+
</render-icon> -->
44+
</fab-command-bar-item>
45+
<fab-command-bar-item *ngIf="runDisabled" key="sometimesVisible" text="woosh"></fab-command-bar-item>
46+
</items>
47+
48+
<far-items>
49+
<fab-command-bar-item key="help" text="Help" [iconProps]="{ iconName: 'Help' }"></fab-command-bar-item>
50+
<fab-command-bar-item key="full-screen" [iconOnly]="true" [iconProps]="{ iconName: fullScreenIcon }" (click)="toggleFullScreen()"></fab-command-bar-item>
51+
</far-items>
2052
</fab-command-bar>
2153

2254
<fab-default-button (onClick)="toggleRun()" text="Toggle run"></fab-default-button>

apps/demo/src/app/app.component.ts

Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,33 @@
1-
import { ChangeDetectorRef, ViewEncapsulation, Component, ComponentFactoryResolver, Injector, Input, ComponentRef, TemplateRef, ViewChild, AfterViewInit } from '@angular/core';
2-
import { DialogType, ITheme, IChoiceGroupProps, SpinnerSize, PersonaSize, PersonaPresence, PivotLinkSize, SelectableOptionMenuItemType, PanelType, ICommandBarItemProps, IBreadcrumbItem, IButtonProps, Button, MessageBarType, ShimmerElementType } from 'office-ui-fabric-react';
3-
import { IExpandingCardOptions } from '@angular-react/fabric/src/components/hover-card';
4-
import { ICommandBarItemOptions, FabCommandBarComponent } from '@angular-react/fabric/src/components/command-bar';
5-
1+
import {
2+
ChangeDetectorRef,
3+
ViewEncapsulation,
4+
Component,
5+
ComponentFactoryResolver,
6+
Injector,
7+
Input,
8+
ComponentRef,
9+
TemplateRef,
10+
ViewChild,
11+
AfterViewInit,
12+
} from '@angular/core';
13+
import {
14+
DialogType,
15+
ITheme,
16+
IChoiceGroupProps,
17+
SpinnerSize,
18+
PersonaSize,
19+
PersonaPresence,
20+
PivotLinkSize,
21+
SelectableOptionMenuItemType,
22+
PanelType,
23+
ICommandBarItemProps,
24+
IBreadcrumbItem,
25+
IButtonProps,
26+
Button,
27+
MessageBarType,
28+
ShimmerElementType,
29+
IContextualMenuProps,
30+
} from 'office-ui-fabric-react';
631

732
@Component({
833
selector: 'app-root',
@@ -11,11 +36,73 @@ import { ICommandBarItemOptions, FabCommandBarComponent } from '@angular-react/f
1136
encapsulation: ViewEncapsulation.None,
1237
})
1338
export class AppComponent {
39+
@ViewChild('customRange')
40+
customRangeTemplate: TemplateRef<{ item: any; dismissMenu: (ev?: any, dismissAll?: boolean) => void }>;
41+
42+
runDisabled: boolean;
43+
44+
onNewClicked() {
45+
console.log('New clicked');
46+
}
1447

15-
@ViewChild(FabCommandBarComponent) commandBar: FabCommandBarComponent;
16-
@ViewChild('customRange') customRangeTemplate: TemplateRef<{ item: any, dismissMenu: (ev?: any, dismissAll?: boolean) => void }>;
48+
onCopyClicked() {
49+
console.log('Copy clicked');
50+
}
51+
52+
onSaveAsClicked() {
53+
console.log('Save as clicked');
54+
}
55+
56+
onSaveAsFirstClicked() {
57+
console.log('Save as 1 clicked');
58+
}
59+
60+
onSaveAsSecondClicked() {
61+
console.log('Save as 2 clicked');
62+
}
63+
64+
onCustomItemClick(item: any) {
65+
this.customItemCount++;
66+
console.log('custom item clicked', item);
67+
}
1768

18-
commandBarItems: ReadonlyArray<ICommandBarItemOptions> = [
69+
constructor(private readonly cd: ChangeDetectorRef) {}
70+
71+
customItemCount = 1;
72+
73+
// FIXME: Allow declarative syntax too
74+
saveSubMenuProps: Partial<IContextualMenuProps> = {
75+
gapSpace: 10,
76+
/* items: [
77+
{
78+
key: 'save',
79+
text: 'Save',
80+
onClick: () => console.log('Save clicked'),
81+
},
82+
{
83+
key: 'save-as',
84+
text: 'Save as',
85+
subMenuProps: {
86+
onItemClick: (ev, item) => {
87+
console.log(`${item.text} clicked`);
88+
return true;
89+
},
90+
items: [
91+
{
92+
key: 'save-as-1',
93+
text: 'Save as 1',
94+
},
95+
{
96+
key: 'save-as-2',
97+
text: 'Save as 2',
98+
},
99+
],
100+
},
101+
},
102+
], */
103+
};
104+
105+
/* commandBarItems: ReadonlyArray<ICommandBarItemOptions> = [
19106
{
20107
key: 'run',
21108
text: 'Run',
@@ -155,16 +242,17 @@ export class AppComponent {
155242
},
156243
onClick: () => console.log('Expand clicked'),
157244
}
158-
];
245+
]; */
159246

160247
isPanelOpen = false;
161248

162249
toggleRun() {
163-
this.commandBarItems = this.commandBarItems.map(item =>
164-
item.key === 'run'
165-
? { ...item, disabled: !item.disabled }
166-
: item
167-
);
250+
this.runDisabled = !this.runDisabled;
251+
}
252+
253+
toggleFullScreen() {
254+
this.fullScreenIcon = this.fullScreenIcon === 'BackToWindow' ? 'MiniExpand' : 'BackToWindow';
168255
}
169256

257+
fullScreenIcon = 'MiniExpand';
170258
}

apps/demo/src/app/app.module.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,38 @@
11
import { AngularReactBrowserModule } from '@angular-react/core';
2-
import { FabBreadcrumbModule, FabButtonModule, FabCalloutModule, FabModalModule, FabCheckboxModule, FabChoiceGroupModule, FabComboBoxModule, FabCommandBarModule, FabDatePickerModule, FabDialogModule, FabDividerModule, FabFabricModule, FabGroupedListModule, FabIconModule, FabImageModule, FabPanelModule, FabPersonaModule, FabSpinnerModule, FabMessageBarModule, FabLinkModule, FabToggleModule, FabPivotModule, FabHoverCardModule, FabTooltipModule, FabShimmerModule, FabSliderModule, FabSearchBoxModule } from '@angular-react/fabric';
2+
import {
3+
FabBreadcrumbModule,
4+
FabButtonModule,
5+
FabCalloutModule,
6+
FabModalModule,
7+
FabCheckboxModule,
8+
FabChoiceGroupModule,
9+
FabComboBoxModule,
10+
FabCommandBarModule,
11+
FabDatePickerModule,
12+
FabDialogModule,
13+
FabDividerModule,
14+
FabFabricModule,
15+
FabGroupedListModule,
16+
FabIconModule,
17+
FabImageModule,
18+
FabPanelModule,
19+
FabPersonaModule,
20+
FabSpinnerModule,
21+
FabMessageBarModule,
22+
FabLinkModule,
23+
FabToggleModule,
24+
FabPivotModule,
25+
FabHoverCardModule,
26+
FabTooltipModule,
27+
FabShimmerModule,
28+
FabSliderModule,
29+
FabSearchBoxModule,
30+
} from '@angular-react/fabric';
331
import { NgModule } from '@angular/core';
432
import { NxModule } from '@nrwl/nx';
533
import { initializeIcons } from 'office-ui-fabric-react/lib/Icons';
634
import { AppComponent } from './app.component';
35+
import { CounterComponent } from './counter/counter.component';
736

837
@NgModule({
938
imports: [
@@ -37,7 +66,7 @@ import { AppComponent } from './app.component';
3766
FabSliderModule,
3867
FabSearchBoxModule,
3968
],
40-
declarations: [AppComponent],
69+
declarations: [AppComponent, CounterComponent],
4170
bootstrap: [AppComponent],
4271
})
4372
export class AppModule {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
2+
3+
@Component({
4+
selector: 'counter',
5+
template: `
6+
<div>{{ count }}</div>
7+
<button (click)="onClick()">+</button>
8+
`,
9+
changeDetection: ChangeDetectionStrategy.OnPush,
10+
})
11+
export class CounterComponent {
12+
count = 0;
13+
14+
constructor(private readonly cd: ChangeDetectorRef) {}
15+
16+
onClick() {
17+
this.count++;
18+
this.cd.detectChanges();
19+
}
20+
}

libs/core/package-lock.json

Lines changed: 0 additions & 45 deletions
This file was deleted.

libs/core/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "../../node_modules/ng-packagr/package.schema.json",
33
"name": "@angular-react/core",
4-
"version": "0.2.3",
4+
"version": "0.2.4",
55
"ngPackage": {
66
"lib": {
77
"languageLevel": [
@@ -47,7 +47,6 @@
4747
},
4848
"devDependencies": {
4949
"@types/react-dom": "^16.0.4",
50-
"@types/react": "^16.3.4",
51-
"css-to-style": "^1.2.0"
50+
"@types/react": "^16.3.4"
5251
}
5352
}

libs/core/src/components/wrapper-component.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import toStyle from 'css-to-style';
1616

1717
import { isReactNode } from '../renderer/react-node';
1818
import { renderComponent, renderFunc, renderTemplate } from '../renderer/renderprop-helpers';
19+
import { ExternalReactContentProps } from '../renderer/react-content';
1920
import { unreachable } from '../utils/types/unreachable';
2021

2122
type AttributeNameAlternative = [string, string | undefined];
@@ -133,32 +134,34 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
133134
/**
134135
* Create an JSX renderer for an `@Input` property.
135136
* @param input The input property
137+
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
136138
*/
137139
protected createInputJsxRenderer<TContext extends object>(
138-
input: InputRendererOptions<TContext>
140+
input: InputRendererOptions<TContext>,
141+
additionalProps?: ExternalReactContentProps
139142
): JsxRenderFunc<TContext> | undefined {
140143
if (input === undefined) {
141144
return undefined;
142145
}
143146

144147
if (input instanceof TemplateRef) {
145-
return (context: TContext) => renderTemplate(input, context);
148+
return (context: TContext) => renderTemplate(input, context, additionalProps);
146149
}
147150

148151
if (input instanceof ComponentRef) {
149-
return (context: TContext) => renderComponent(input, context);
152+
return (context: TContext) => renderComponent(input, context, additionalProps);
150153
}
151154

152155
if (input instanceof Function) {
153-
return (context: TContext) => renderFunc(input, context);
156+
return (context: TContext) => renderFunc(input, context, additionalProps);
154157
}
155158

156159
if (typeof input === 'object') {
157160
const { componentType, factoryResolver, injector } = input;
158161
const componentFactory = factoryResolver.resolveComponentFactory(componentType);
159162
const componentRef = componentFactory.create(injector);
160163

161-
return (context: TContext) => renderComponent(componentRef, context);
164+
return (context: TContext) => renderComponent(componentRef, context, additionalProps);
162165
}
163166

164167
unreachable(input);
@@ -168,12 +171,14 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
168171
* Create an event handler for a render prop
169172
* @param renderInputValue the value of the render `@Input` property.
170173
* @param jsxRenderer an optional renderer to use.
174+
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
171175
*/
172176
protected createRenderPropHandler<TProps extends object>(
173177
renderInputValue: InputRendererOptions<TProps>,
174-
jsxRenderer?: JsxRenderFunc<TProps>
178+
jsxRenderer?: JsxRenderFunc<TProps>,
179+
additionalProps?: ExternalReactContentProps
175180
): (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => JSX.Element | null {
176-
const renderer = jsxRenderer || this.createInputJsxRenderer(renderInputValue);
181+
const renderer = jsxRenderer || this.createInputJsxRenderer(renderInputValue, additionalProps);
177182

178183
return (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => {
179184
if (!renderInputValue) {

0 commit comments

Comments
 (0)