diff --git a/.vscode/settings.json b/.vscode/settings.json index 41752295..d3ecd8cc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,7 @@ "messagebar", "nrwl", "renderprop", + "resizable", "scrollable", "stylenames", "submenu", diff --git a/apps/demo/src/app/app.component.html b/apps/demo/src/app/app.component.html index a00b374c..ac8ff54d 100644 --- a/apps/demo/src/app/app.component.html +++ b/apps/demo/src/app/app.component.html @@ -12,6 +12,10 @@

Getting up and running...

+ + +{{ textFieldValue }} +
Tab 1's content
diff --git a/apps/demo/src/app/app.component.ts b/apps/demo/src/app/app.component.ts index be78a6f4..b18e9637 100644 --- a/apps/demo/src/app/app.component.ts +++ b/apps/demo/src/app/app.component.ts @@ -21,6 +21,8 @@ export class AppComponent { console.log('onMouseOver', { ev }); } + textFieldValue = 'Hello'; + marqueeEnabled: boolean; runDisabled: boolean; selection: ISelection; diff --git a/apps/demo/src/app/app.module.ts b/apps/demo/src/app/app.module.ts index 0f363758..0dcc981b 100644 --- a/apps/demo/src/app/app.module.ts +++ b/apps/demo/src/app/app.module.ts @@ -31,6 +31,7 @@ import { FabSpinnerModule, FabToggleModule, FabTooltipModule, + FabTextFieldModule, } from '@angular-react/fabric'; import { NgModule } from '@angular/core'; import { NxModule } from '@nrwl/nx'; @@ -73,6 +74,7 @@ import { CounterComponent } from './counter/counter.component'; FabDetailsListModule, FabGroupModule, FabMarqueeSelectionModule, + FabTextFieldModule, ], declarations: [AppComponent, CounterComponent], bootstrap: [AppComponent], diff --git a/libs/fabric/src/lib/components/text-field/base-text-field.component.ts b/libs/fabric/src/lib/components/text-field/base-text-field.component.ts new file mode 100644 index 00000000..72859e82 --- /dev/null +++ b/libs/fabric/src/lib/components/text-field/base-text-field.component.ts @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { ReactWrapperComponent, InputRendererOptions, JsxRenderFunc } from '@angular-react/core'; +import { + ChangeDetectorRef, + EventEmitter, + ElementRef, + Input, + Renderer2, + ViewChild, + OnInit, + Output, +} from '@angular/core'; +import { ITextFieldProps } from 'office-ui-fabric-react/lib/TextField'; + +export class FabBaseTextFieldComponent extends ReactWrapperComponent implements OnInit { + @ViewChild('reactNode') protected reactNodeRef: ElementRef; + + @Input() componentRef?: ITextFieldProps['componentRef']; + @Input() multiline?: ITextFieldProps['multiline']; + @Input() resizable?: ITextFieldProps['resizable']; + @Input() autoAdjustHeight?: ITextFieldProps['autoAdjustHeight']; + @Input() underlined?: ITextFieldProps['underlined']; + @Input() borderless?: ITextFieldProps['borderless']; + @Input() label?: ITextFieldProps['label']; + @Input() description?: ITextFieldProps['description']; + @Input() prefix?: ITextFieldProps['prefix']; + @Input() suffix?: ITextFieldProps['suffix']; + @Input() iconProps?: ITextFieldProps['iconProps']; + @Input() defaultValue?: ITextFieldProps['defaultValue']; + @Input() value?: ITextFieldProps['value']; + @Input() disabled?: ITextFieldProps['disabled']; + @Input() readOnly?: ITextFieldProps['readOnly']; + @Input() errorMessage?: ITextFieldProps['errorMessage']; + @Input() deferredValidationTime?: ITextFieldProps['deferredValidationTime']; + @Input() className?: ITextFieldProps['className']; + @Input() inputClassName?: ITextFieldProps['inputClassName']; + @Input() ariaLabel?: ITextFieldProps['ariaLabel']; + @Input() validateOnFocusIn?: ITextFieldProps['validateOnFocusIn']; + @Input() validateOnFocusOut?: ITextFieldProps['validateOnFocusOut']; + @Input() validateOnLoad?: ITextFieldProps['validateOnLoad']; + @Input() theme?: ITextFieldProps['theme']; + @Input() styles?: ITextFieldProps['styles']; + @Input() autoComplete?: ITextFieldProps['autoComplete']; + @Input() mask?: ITextFieldProps['mask']; + @Input() maskChar?: ITextFieldProps['maskChar']; + @Input() maskFormat?: ITextFieldProps['maskFormat']; + @Input() getErrorMessage?: ITextFieldProps['onGetErrorMessage']; + + @Input() renderLabel?: InputRendererOptions; + @Input() renderDescription?: InputRendererOptions; + @Input() renderPrefix?: InputRendererOptions; + @Input() renderSuffix?: InputRendererOptions; + + @Output() readonly onChange = new EventEmitter<{ event: Event; newValue?: string }>(); + @Output() readonly onBeforeChange = new EventEmitter<{ newValue: any }>(); + @Output() readonly onNotifyValidationResult = new EventEmitter<{ errorMessage: string; value: string | undefined }>(); + + /* Non-React props, more native support for Angular */ + // support for two-way data binding for `@Input() checked`. + @Output() readonly valueChange = new EventEmitter(); + + onRenderLabel: (props?: ITextFieldProps, defaultRender?: JsxRenderFunc) => JSX.Element; + onRenderDescription: (props?: ITextFieldProps, defaultRender?: JsxRenderFunc) => JSX.Element; + onRenderPrefix: (props?: ITextFieldProps, defaultRender?: JsxRenderFunc) => JSX.Element; + onRenderSuffix: (props?: ITextFieldProps, defaultRender?: JsxRenderFunc) => JSX.Element; + + constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) { + super(elementRef, changeDetectorRef, renderer); + + this.onChangeHandler = this.onChangeHandler.bind(this); + this.onBeforeChangeHandler = this.onBeforeChangeHandler.bind(this); + this.onNotifyValidationResultHandler = this.onNotifyValidationResultHandler.bind(this); + } + + ngOnInit() { + this.onRenderLabel = this.createRenderPropHandler(this.renderLabel); + this.onRenderDescription = this.createRenderPropHandler(this.renderDescription); + this.onRenderPrefix = this.createRenderPropHandler(this.renderPrefix); + this.onRenderSuffix = this.createRenderPropHandler(this.renderSuffix); + } + + onChangeHandler(event: React.FormEvent, newValue?: string) { + this.onChange.emit({ event: event.nativeEvent, newValue }); + + this.valueChange.emit(newValue); + } + + onBeforeChangeHandler(newValue: any) { + this.onBeforeChange.emit({ newValue }); + } + + onNotifyValidationResultHandler(errorMessage: string, value: string | undefined) { + this.onNotifyValidationResult.emit({ errorMessage, value }); + } +} diff --git a/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts b/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts new file mode 100644 index 00000000..511370c4 --- /dev/null +++ b/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Renderer2, ViewChild } from '@angular/core'; +import { FabBaseTextFieldComponent } from './base-text-field.component'; + +@Component({ + selector: 'fab-masked-text-field', + exportAs: 'fabMaskedTextField', + template: ` + + + `, + styles: ['react-renderer'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FabMaskedTextFieldComponent extends FabBaseTextFieldComponent { + @ViewChild('reactNode') protected reactNodeRef: ElementRef; + + constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) { + super(elementRef, changeDetectorRef, renderer); + } +} diff --git a/libs/fabric/src/lib/components/text-field/public-api.ts b/libs/fabric/src/lib/components/text-field/public-api.ts new file mode 100644 index 00000000..6c31f2b6 --- /dev/null +++ b/libs/fabric/src/lib/components/text-field/public-api.ts @@ -0,0 +1,3 @@ +export * from './text-field.module'; +export * from './masked-text-field.component'; +export * from './text-field.component'; diff --git a/libs/fabric/src/lib/components/text-field/text-field.component.ts b/libs/fabric/src/lib/components/text-field/text-field.component.ts new file mode 100644 index 00000000..350e9ea0 --- /dev/null +++ b/libs/fabric/src/lib/components/text-field/text-field.component.ts @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Renderer2, ViewChild } from '@angular/core'; +import { FabBaseTextFieldComponent } from './base-text-field.component'; + +@Component({ + selector: 'fab-text-field', + exportAs: 'fabTextField', + template: ` + + + `, + styles: ['react-renderer'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FabTextFieldComponent extends FabBaseTextFieldComponent { + @ViewChild('reactNode') protected reactNodeRef: ElementRef; + + constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) { + super(elementRef, changeDetectorRef, renderer); + } +} diff --git a/libs/fabric/src/lib/components/text-field/text-field.module.ts b/libs/fabric/src/lib/components/text-field/text-field.module.ts new file mode 100644 index 00000000..49b8a82e --- /dev/null +++ b/libs/fabric/src/lib/components/text-field/text-field.module.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { registerElement } from '@angular-react/core'; +import { CommonModule } from '@angular/common'; +import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; +import { TextField, MaskedTextField } from 'office-ui-fabric-react/lib/TextField'; +import { FabTextFieldComponent } from './text-field.component'; +import { FabMaskedTextFieldComponent } from './masked-text-field.component'; + +const components = [FabTextFieldComponent, FabMaskedTextFieldComponent]; + +@NgModule({ + imports: [CommonModule], + declarations: components, + exports: components, + schemas: [NO_ERRORS_SCHEMA], +}) +export class FabTextFieldModule { + constructor() { + // Add any React elements to the registry (used by the renderer). + registerElement('TextField', () => TextField); + registerElement('MaskedTextField', () => MaskedTextField); + } +} diff --git a/libs/fabric/src/public-api.ts b/libs/fabric/src/public-api.ts index e0784193..5ad2d68b 100644 --- a/libs/fabric/src/public-api.ts +++ b/libs/fabric/src/public-api.ts @@ -31,6 +31,7 @@ export * from './lib/components/search-box/public-api'; export * from './lib/components/shimmer/public-api'; export * from './lib/components/slider/public-api'; export * from './lib/components/spinner/public-api'; +export * from './lib/components/text-field/public-api'; export * from './lib/components/toggle/public-api'; export * from './lib/components/tooltip/public-api';