Skip to content

Commit 35dbbbe

Browse files
authored
[Fabric] Add SpinButton component (#59)
1 parent d18320e commit 35dbbbe

File tree

7 files changed

+192
-0
lines changed

7 files changed

+192
-0
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ <h2>Getting up and running...</h2>
1616

1717
<span>{{ textFieldValue }}</span>
1818

19+
<fab-spin-button
20+
label="my label"
21+
[value]="7 + ' cm'"
22+
[validate]="onValidate"
23+
[increment]="onIncrement"
24+
[decrement]="onDecrement"
25+
></fab-spin-button>
26+
1927
<div>
2028
<fab-pivot>
2129
<fab-pivot-item headerText="Tab 1"> <div>Tab 1's content</div> </fab-pivot-item>

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { ChangeDetectorRef, Component, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';
22
import { ICalendarStrings, IContextualMenuProps, ISelection, Selection } from 'office-ui-fabric-react';
33

4+
const suffix = ' cm';
5+
46
@Component({
57
selector: 'app-root',
68
templateUrl: './app.component.html',
@@ -94,8 +96,53 @@ export class AppComponent {
9496
console.log('Column header clicked', event);
9597
}
9698

99+
onIncrement(value: string): string | void {
100+
value = this._removeSuffix(value, suffix);
101+
102+
if (+value >= 13) {
103+
return value + suffix;
104+
}
105+
106+
return String(+value + 2) + suffix;
107+
}
108+
109+
onDecrement(value: string): string | void {
110+
value = this._removeSuffix(value, suffix);
111+
112+
if (+value <= 3) {
113+
return value + suffix;
114+
}
115+
return String(+value - 2) + suffix;
116+
}
117+
118+
onValidate(value: string, event: Event): string | void {
119+
value = this._removeSuffix(value, suffix);
120+
if (value.trim().length === 0 || isNaN(+value)) {
121+
return '0' + suffix;
122+
}
123+
124+
return String(value) + suffix;
125+
}
126+
127+
private _hasSuffix(value: string, suffix: string): Boolean {
128+
const subString = value.substr(value.length - suffix.length);
129+
return subString === suffix;
130+
}
131+
132+
private _removeSuffix(value: string, suffix: string): string {
133+
if (!this._hasSuffix(value, suffix)) {
134+
return value;
135+
}
136+
137+
return value.substr(0, value.length - suffix.length);
138+
}
139+
97140
constructor(private readonly cd: ChangeDetectorRef) {
98141
this.selection = new Selection();
142+
143+
this.onValidate = this.onValidate.bind(this);
144+
this.onIncrement = this.onIncrement.bind(this);
145+
this.onDecrement = this.onDecrement.bind(this);
99146
}
100147

101148
customItemCount = 1;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
FabSpinnerModule,
3232
FabToggleModule,
3333
FabTooltipModule,
34+
FabSpinButtonModule,
3435
FabTextFieldModule,
3536
} from '@angular-react/fabric';
3637
import { NgModule } from '@angular/core';
@@ -74,6 +75,7 @@ import { CounterComponent } from './counter/counter.component';
7475
FabDetailsListModule,
7576
FabGroupModule,
7677
FabMarqueeSelectionModule,
78+
FabSpinButtonModule,
7779
FabTextFieldModule,
7880
],
7981
declarations: [AppComponent, CounterComponent],
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
export * from './spin-button.component';
5+
export * from './spin-button.module';
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { ReactWrapperComponent } from '@angular-react/core';
5+
import {
6+
ChangeDetectionStrategy,
7+
ChangeDetectorRef,
8+
Component,
9+
ElementRef,
10+
Input,
11+
Renderer2,
12+
ViewChild,
13+
EventEmitter,
14+
Output,
15+
OnInit,
16+
} from '@angular/core';
17+
import { ISpinButtonProps } from 'office-ui-fabric-react/lib/SpinButton';
18+
19+
@Component({
20+
selector: 'fab-spin-button',
21+
exportAs: 'fabSpinButton',
22+
template: `
23+
<SpinButton
24+
#reactNode
25+
[componentRef]="componentRef"
26+
[defaultValue]="defaultValue"
27+
[value]="value"
28+
[min]="min"
29+
[max]="max"
30+
[step]="step"
31+
[ariaLabel]="ariaLabel"
32+
[title]="title"
33+
[disabled]="disabled"
34+
[className]="className"
35+
[label]="label"
36+
[labelPosition]="labelPosition"
37+
[iconProps]="iconProps"
38+
[incrementButtonIcon]="incrementButtonIcon"
39+
[decrementButtonIcon]="decrementButtonIcon"
40+
[styles]="styles"
41+
[getClassNames]="getClassNames"
42+
[upArrowButtonStyles]="upArrowButtonStyles"
43+
[downArrowButtonStyles]="downArrowButtonStyles"
44+
[theme]="theme"
45+
[incrementButtonAriaLabel]="incrementButtonAriaLabel"
46+
[decrementButtonAriaLabel]="decrementButtonAriaLabel"
47+
[precision]="precision"
48+
[ariaPositionInSet]="ariaPositionInSet"
49+
[ariaSetSize]="ariaSetSize"
50+
[ariaValueNow]="ariaValueNow"
51+
[ariaValueText]="ariaValueText"
52+
[keytipProps]="keytipProps"
53+
[Validate]="validate"
54+
[Increment]="increment"
55+
[Decrement]="decrement"
56+
(onFocus)="onFocus.emit($event)"
57+
(onBlur)="onBlur.emit($event)"
58+
>
59+
</SpinButton>
60+
`,
61+
styles: ['react-renderer'],
62+
changeDetection: ChangeDetectionStrategy.OnPush,
63+
})
64+
export class FabSpinButtonComponent extends ReactWrapperComponent<ISpinButtonProps> {
65+
@ViewChild('reactNode') protected reactNodeRef: ElementRef;
66+
67+
@Input() componentRef?: ISpinButtonProps['componentRef'];
68+
@Input() defaultValue?: ISpinButtonProps['defaultValue'];
69+
@Input() value?: ISpinButtonProps['value'];
70+
@Input() min?: ISpinButtonProps['min'];
71+
@Input() max?: ISpinButtonProps['max'];
72+
@Input() step?: ISpinButtonProps['step'];
73+
@Input() ariaLabel?: ISpinButtonProps['ariaLabel'];
74+
@Input() title?: ISpinButtonProps['title'];
75+
@Input() disabled?: ISpinButtonProps['disabled'];
76+
@Input() className?: ISpinButtonProps['className'];
77+
@Input() label: ISpinButtonProps['label'];
78+
@Input() labelPosition?: ISpinButtonProps['labelPosition'];
79+
@Input() iconProps?: ISpinButtonProps['iconProps'];
80+
@Input() incrementButtonIcon?: ISpinButtonProps['incrementButtonIcon'];
81+
@Input() decrementButtonIcon?: ISpinButtonProps['decrementButtonIcon'];
82+
@Input() styles?: ISpinButtonProps['styles'];
83+
@Input() getClassNames?: ISpinButtonProps['getClassNames'];
84+
@Input() upArrowButtonStyles?: ISpinButtonProps['upArrowButtonStyles'];
85+
@Input() downArrowButtonStyles?: ISpinButtonProps['downArrowButtonStyles'];
86+
@Input() theme?: ISpinButtonProps['theme'];
87+
@Input() incrementButtonAriaLabel?: ISpinButtonProps['incrementButtonAriaLabel'];
88+
@Input() decrementButtonAriaLabel?: ISpinButtonProps['decrementButtonAriaLabel'];
89+
@Input() precision?: ISpinButtonProps['precision'];
90+
@Input() ariaPositionInSet?: ISpinButtonProps['ariaPositionInSet'];
91+
@Input() ariaSetSize?: ISpinButtonProps['ariaSetSize'];
92+
@Input() ariaValueNow?: ISpinButtonProps['ariaValueNow'];
93+
@Input() ariaValueText?: ISpinButtonProps['ariaValueText'];
94+
@Input() keytipProps?: ISpinButtonProps['keytipProps'];
95+
96+
@Input() validate?: ISpinButtonProps['onValidate'];
97+
@Input() increment?: ISpinButtonProps['onIncrement'];
98+
@Input() decrement?: ISpinButtonProps['onDecrement'];
99+
100+
@Output() readonly onFocus = new EventEmitter<Event>();
101+
@Output() readonly onBlur = new EventEmitter<Event>();
102+
103+
constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) {
104+
super(elementRef, changeDetectorRef, renderer);
105+
}
106+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { registerElement } from '@angular-react/core';
5+
import { CommonModule } from '@angular/common';
6+
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
7+
import { SpinButton } from 'office-ui-fabric-react/lib/SpinButton';
8+
import { FabSpinButtonComponent } from './spin-button.component';
9+
10+
const components = [FabSpinButtonComponent];
11+
12+
@NgModule({
13+
imports: [CommonModule],
14+
declarations: components,
15+
exports: components,
16+
schemas: [NO_ERRORS_SCHEMA],
17+
})
18+
export class FabSpinButtonModule {
19+
constructor() {
20+
// Add any React elements to the registry (used by the renderer).
21+
registerElement('SpinButton', () => SpinButton);
22+
}
23+
}

libs/fabric/src/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export * from './lib/components/pivot/public-api';
3030
export * from './lib/components/search-box/public-api';
3131
export * from './lib/components/shimmer/public-api';
3232
export * from './lib/components/slider/public-api';
33+
export * from './lib/components/spin-button/public-api';
3334
export * from './lib/components/spinner/public-api';
3435
export * from './lib/components/text-field/public-api';
3536
export * from './lib/components/toggle/public-api';

0 commit comments

Comments
 (0)