diff --git a/.vscode/settings.json b/.vscode/settings.json index d3ecd8c..4dcdac2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,12 @@ { "cSpell.words": [ "Callout", + "Droppable", "Focusable", "Injectable", "Packagr", "Renderable", + "Reselect", "Selectable", "VDOM", "borderless", diff --git a/apps/demo/src/app/app.component.html b/apps/demo/src/app/app.component.html index cd0e184..b55900f 100644 --- a/apps/demo/src/app/app.component.html +++ b/apps/demo/src/app/app.component.html @@ -9,6 +9,16 @@ + + diff --git a/apps/demo/src/app/app.component.ts b/apps/demo/src/app/app.component.ts index f25a917..2893476 100644 --- a/apps/demo/src/app/app.component.ts +++ b/apps/demo/src/app/app.component.ts @@ -1,5 +1,13 @@ import { ChangeDetectorRef, Component, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; -import { ICalendarStrings, IContextualMenuProps, ISelection, Selection } from 'office-ui-fabric-react'; +import { + ICalendarStrings, + IContextualMenuProps, + ISelection, + Selection, + DropdownMenuItemType, + IDropdownOption, +} from 'office-ui-fabric-react'; +import { FabDropdownComponent } from '@angular-react/fabric'; const suffix = ' cm'; @@ -23,6 +31,22 @@ export class AppComponent { console.log('onMouseOver', { ev }); } + logEvent(...args: any[]) { + console.log(args); + } + + selectedItem?: IDropdownOption; + options: FabDropdownComponent['options'] = [ + { key: 'A', text: 'Option a' }, + { key: 'B', text: 'Option b' }, + { key: 'C', text: 'Option c' }, + { key: 'D', text: 'Option d' }, + { key: 'divider_1', text: '-', itemType: DropdownMenuItemType.Divider }, + { key: 'E', text: 'Option e' }, + { key: 'F', text: 'Option f' }, + { key: 'G', text: 'Option g' }, + ]; + textFieldValue = 'Hello'; marqueeEnabled: boolean; diff --git a/apps/demo/src/app/app.module.ts b/apps/demo/src/app/app.module.ts index 245b583..3bae67a 100644 --- a/apps/demo/src/app/app.module.ts +++ b/apps/demo/src/app/app.module.ts @@ -13,6 +13,7 @@ import { FabDialogModule, FabDividerModule, FabFabricModule, + FabDropdownModule, FabGroupModule, FabGroupedListModule, FabHoverCardModule, @@ -49,6 +50,7 @@ import { CounterComponent } from './counter/counter.component'; FabButtonModule, FabDialogModule, FabImageModule, + FabDropdownModule, FabPanelModule, FabCommandBarModule, FabBreadcrumbModule, diff --git a/libs/fabric/src/lib/components/dropdown/dropdown.component.ts b/libs/fabric/src/lib/components/dropdown/dropdown.component.ts new file mode 100644 index 0000000..3b717d8 --- /dev/null +++ b/libs/fabric/src/lib/components/dropdown/dropdown.component.ts @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { InputRendererOptions, JsxRenderFunc, ReactWrapperComponent } from '@angular-react/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnInit, + Renderer2, + ViewChild, + Output, + EventEmitter, +} from '@angular/core'; +import { IDropdownProps, IDropdownOption, IDropdown } from 'office-ui-fabric-react/lib/Dropdown'; +import { ISelectableDroppableTextProps, ISelectableOption } from 'office-ui-fabric-react'; + +@Component({ + selector: 'fab-dropdown', + exportAs: 'fabDropdown', + template: ` + + `, + styles: ['react-renderer'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FabDropdownComponent extends ReactWrapperComponent implements OnInit { + @ViewChild('reactNode') protected reactNodeRef: ElementRef; + + @Input() componentRef?: IDropdownProps['componentRef']; + @Input() label?: IDropdownProps['label']; + @Input() ariaLabel?: IDropdownProps['ariaLabel']; + @Input() id?: IDropdownProps['id']; + @Input() className?: IDropdownProps['className']; + @Input() defaultSelectedKey?: IDropdownProps['defaultSelectedKey']; + @Input() selectedKey?: IDropdownProps['selectedKey']; + @Input() disabled?: IDropdownProps['disabled']; + @Input() required?: IDropdownProps['required']; + @Input() calloutProps?: IDropdownProps['calloutProps']; + @Input() panelProps?: IDropdownProps['panelProps']; + @Input() errorMessage?: IDropdownProps['errorMessage']; + + @Input() placeholder: IDropdownProps['placeholder']; + @Input() options: IDropdownProps['options']; + @Input() dropdownWidth?: IDropdownProps['dropdownWidth']; + @Input() responsiveMode?: IDropdownProps['responsiveMode']; + @Input() multiSelect?: IDropdownProps['multiSelect']; + @Input() defaultSelectedKeys?: IDropdownProps['defaultSelectedKeys']; + @Input() selectedKeys?: IDropdownProps['selectedKeys']; + @Input() multiSelectDelimiter?: IDropdownProps['multiSelectDelimiter']; + @Input() notifyOnReselect?: IDropdownProps['notifyOnReselect']; + @Input() keytipProps?: IDropdownProps['keytipProps']; + @Input() theme?: IDropdownProps['theme']; + @Input() styles?: IDropdownProps['styles']; + + @Input() renderContainer?: InputRendererOptions>; + @Input() renderList?: InputRendererOptions>; + @Input() renderItem?: InputRendererOptions; + @Input() renderOption?: InputRendererOptions; + @Input() renderPlaceHolder?: InputRendererOptions; + @Input() renderTitle?: InputRendererOptions; + @Input() renderCaretDown?: InputRendererOptions; + + @Output() readonly onChange = new EventEmitter<{ event: Event; option?: IDropdownOption; index?: number }>(); + @Output() readonly onDismiss = new EventEmitter(); + + onRenderContainer: ( + props?: ISelectableDroppableTextProps, + defaultRender?: JsxRenderFunc> + ) => JSX.Element; + onRenderList: ( + props?: ISelectableDroppableTextProps, + defaultRender?: JsxRenderFunc> + ) => JSX.Element; + onRenderItem: (props?: ISelectableOption, defaultRender?: JsxRenderFunc) => JSX.Element; + onRenderOption: (props?: ISelectableOption, defaultRender?: JsxRenderFunc) => JSX.Element; + onRenderPlaceHolder: (props?: IDropdownProps, defaultRender?: JsxRenderFunc) => JSX.Element; + onRenderTitle: ( + props?: IDropdownOption | IDropdownOption[], + defaultRender?: JsxRenderFunc + ) => JSX.Element; + onRenderCaretDown: (props?: IDropdownProps, defaultRender?: JsxRenderFunc) => JSX.Element; + + constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) { + super(elementRef, changeDetectorRef, renderer, { setHostDisplay: true }); + + this.onChangeHandler = this.onChangeHandler.bind(this); + this.onDismissHandler = this.onDismissHandler.bind(this); + } + + ngOnInit() { + this.onRenderContainer = this.createRenderPropHandler(this.renderContainer); + this.onRenderList = this.createRenderPropHandler(this.renderList); + this.onRenderItem = this.createRenderPropHandler(this.renderItem); + this.onRenderOption = this.createRenderPropHandler(this.renderOption); + this.onRenderPlaceHolder = this.createRenderPropHandler(this.renderPlaceHolder); + this.onRenderTitle = this.createRenderPropHandler(this.renderTitle); + this.onRenderCaretDown = this.createRenderPropHandler(this.renderCaretDown); + } + + onChangeHandler(event: React.FormEvent, option?: IDropdownOption, index?: number) { + this.onChange.emit({ + event: event && event.nativeEvent, + option, + index, + }); + } + + onDismissHandler() { + this.onDismiss.emit(); + } +} diff --git a/libs/fabric/src/lib/components/dropdown/dropdown.module.ts b/libs/fabric/src/lib/components/dropdown/dropdown.module.ts new file mode 100644 index 0000000..6a95191 --- /dev/null +++ b/libs/fabric/src/lib/components/dropdown/dropdown.module.ts @@ -0,0 +1,23 @@ +// 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 { Dropdown } from 'office-ui-fabric-react/lib/Dropdown'; +import { FabDropdownComponent } from './dropdown.component'; + +const components = [FabDropdownComponent]; + +@NgModule({ + imports: [CommonModule], + declarations: components, + exports: components, + schemas: [NO_ERRORS_SCHEMA], +}) +export class FabDropdownModule { + constructor() { + // Add any React elements to the registry (used by the renderer). + registerElement('Dropdown', () => Dropdown); + } +} diff --git a/libs/fabric/src/lib/components/dropdown/public-api.ts b/libs/fabric/src/lib/components/dropdown/public-api.ts new file mode 100644 index 0000000..c45bf36 --- /dev/null +++ b/libs/fabric/src/lib/components/dropdown/public-api.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +export * from './dropdown.component'; +export * from './dropdown.module'; 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 index 72859e8..1e0e041 100644 --- 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 @@ -67,7 +67,7 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent) => JSX.Element; constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) { - super(elementRef, changeDetectorRef, renderer); + super(elementRef, changeDetectorRef, renderer, { setHostDisplay: true }); this.onChangeHandler = this.onChangeHandler.bind(this); this.onBeforeChangeHandler = this.onBeforeChangeHandler.bind(this); diff --git a/libs/fabric/src/public-api.ts b/libs/fabric/src/public-api.ts index dd69a26..f2ba220 100644 --- a/libs/fabric/src/public-api.ts +++ b/libs/fabric/src/public-api.ts @@ -14,6 +14,7 @@ export * from './lib/components/date-picker/public-api'; export * from './lib/components/details-list/public-api'; export * from './lib/components/dialog/public-api'; export * from './lib/components/divider/public-api'; +export * from './lib/components/dropdown/public-api'; export * from './lib/components/fabric/public-api'; export * from './lib/components/group/public-api'; export * from './lib/components/grouped-list/public-api';