office-ui-fabric-react
Version:
Reusable React components for building experiences for Office 365.
307 lines (305 loc) • 14.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var React = require("react");
var Button_1 = require("../../Button");
var Label_1 = require("../../Label");
var Icon_1 = require("../../Icon");
var Utilities_1 = require("../../Utilities");
var positioning_1 = require("../../utilities/positioning");
var stylesImport = require("./SpinButton.scss");
var styles = stylesImport;
var KeyboardSpinDirection;
(function (KeyboardSpinDirection) {
KeyboardSpinDirection[KeyboardSpinDirection["down"] = -1] = "down";
KeyboardSpinDirection[KeyboardSpinDirection["notSpinning"] = 0] = "notSpinning";
KeyboardSpinDirection[KeyboardSpinDirection["up"] = 1] = "up";
})(KeyboardSpinDirection = exports.KeyboardSpinDirection || (exports.KeyboardSpinDirection = {}));
var SpinButton = (function (_super) {
tslib_1.__extends(SpinButton, _super);
function SpinButton(props) {
var _this = _super.call(this, props) || this;
_this._initialStepDelay = 400;
_this._stepDelay = 75;
_this._formattedValidUnitOptions = [];
_this._arrowButtonStyle = {
icon: {
fontSize: '6px',
}
};
/**
* Validate function to use if one is not passed in
*/
_this._defaultOnValidate = function (value) {
if (isNaN(Number(value))) {
return _this._lastValidValue;
}
var newValue = Math.min(_this.props.max, Math.max(_this.props.min, Number(value)));
return String(newValue);
};
/**
* Increment function to use if one is not passed in
*/
_this._defaultOnIncrement = function (value) {
var newValue = Math.min(Number(value) + _this.props.step, _this.props.max);
return String(newValue);
};
/**
* Increment function to use if one is not passed in
*/
_this._defaultOnDecrement = function (value) {
var newValue = Math.max(Number(value) - _this.props.step, _this.props.min);
return String(newValue);
};
_this._warnMutuallyExclusive({
'value': 'defaultValue'
});
var value = props.value || props.defaultValue || String(props.min) || '0';
_this._lastValidValue = value;
_this.state = {
value: value,
keyboardSpinDirection: KeyboardSpinDirection.notSpinning
};
_this._currentStepFunctionHandle = -1;
_this._labelId = Utilities_1.getId('Label');
_this._inputId = Utilities_1.getId('input');
_this._spinningByMouse = false;
if (!props.defaultValue && props.value) {
_this._onValidate = props.onValidate;
_this._onIncrement = props.onIncrement;
_this._onDecrement = props.onDecrement;
}
else {
_this._onValidate = _this._defaultOnValidate;
_this._onIncrement = _this._defaultOnIncrement;
_this._onDecrement = _this._defaultOnDecrement;
}
_this.focus = _this.focus.bind(_this);
return _this;
}
/**
* Invoked when a component is receiving new props. This method is not called for the initial render.
*/
SpinButton.prototype.componentWillReceiveProps = function (newProps) {
this._lastValidValue = this.state.value;
var value = newProps.value ? newProps.value : String(newProps.min);
if (newProps.defaultValue) {
value = String(Math.max(newProps.min, Math.min(newProps.max, Number(newProps.defaultValue))));
}
this.setState({
value: value
});
};
SpinButton.prototype.render = function () {
var _this = this;
var _a = this.props, disabled = _a.disabled, label = _a.label, min = _a.min, max = _a.max, labelPosition = _a.labelPosition, iconProps = _a.iconProps, incrementButtonIcon = _a.incrementButtonIcon, decrementButtonIcon = _a.decrementButtonIcon, title = _a.title, ariaLabel = _a.ariaLabel;
var _b = this.state, value = _b.value, keyboardSpinDirection = _b.keyboardSpinDirection;
return (React.createElement("div", { className: styles.SpinButtonContainer },
labelPosition !== positioning_1.Position.bottom && React.createElement("div", { className: Utilities_1.css(styles.labelWrapper, this._getClassNameForLabelPosition(labelPosition)) },
iconProps && React.createElement(Icon_1.Icon, { iconName: iconProps.iconName, className: Utilities_1.css(styles.SpinButtonIcon), "aria-hidden": 'true' }),
label &&
React.createElement(Label_1.Label, { id: this._labelId, htmlFor: this._inputId, className: styles.SpinButtonLabel }, label)),
React.createElement("div", { className: Utilities_1.css(styles.SpinButtonWrapper, ((labelPosition === positioning_1.Position.top || labelPosition === positioning_1.Position.bottom) ? styles.topBottom : '')), title: title && title, "aria-label": ariaLabel && ariaLabel },
React.createElement("input", { value: value, id: this._inputId, onChange: this._onChange, onInput: this._onInputChange, className: Utilities_1.css(styles.Input, (disabled ? styles.disabled : '')), type: 'text', role: 'spinbutton', "aria-labelledby": label && this._labelId, "aria-valuenow": value, "aria-valuemin": min && String(min), "aria-valuemax": max && String(max), onBlur: this._validate, ref: this._resolveRef('_input'), onFocus: this.focus, onKeyDown: this._handleKeyDown, onKeyUp: this._handleKeyUp, readOnly: disabled, "aria-disabled": disabled }),
React.createElement("span", { className: styles.ArrowBox },
React.createElement(Button_1.IconButton, { className: Utilities_1.css('ms-UpButton', styles.UpButton, (keyboardSpinDirection === KeyboardSpinDirection.up ? styles.active : '')), styles: this._arrowButtonStyle, disabled: disabled, iconProps: incrementButtonIcon, "aria-hidden": 'true', onMouseDown: function () { return _this._onIncrementMouseDown(); }, onMouseLeave: this._stop, onMouseUp: this._stop, tabIndex: -1 }),
React.createElement(Button_1.IconButton, { className: Utilities_1.css('ms-DownButton', styles.DownButton, (keyboardSpinDirection === KeyboardSpinDirection.down ? styles.active : '')), styles: this._arrowButtonStyle, disabled: disabled, iconProps: decrementButtonIcon, "aria-hidden": 'true', onMouseDown: function () { return _this._onDecrementMouseDown(); }, onMouseLeave: this._stop, onMouseUp: this._stop, tabIndex: -1 }))),
labelPosition === positioning_1.Position.bottom && React.createElement("div", { className: Utilities_1.css(styles.labelWrapper, this._getClassNameForLabelPosition(labelPosition)) },
iconProps && React.createElement(Icon_1.Icon, { iconName: iconProps.iconName, className: Utilities_1.css(styles.SpinButtonIcon), "aria-hidden": 'true' }),
label &&
React.createElement(Label_1.Label, { id: this._labelId, htmlFor: this._inputId, className: styles.SpinButtonLabel }, label))));
};
/**
* OnFocus select the contents of the input
*/
SpinButton.prototype.focus = function () {
if (this._spinningByMouse || this.state.keyboardSpinDirection !== KeyboardSpinDirection.notSpinning) {
this._stop();
}
this._input.focus();
this._input.select();
};
/**
* Returns the class name corresponding to the label position
*/
SpinButton.prototype._getClassNameForLabelPosition = function (labelPosition) {
var className = '';
switch (labelPosition) {
case positioning_1.Position.start:
className = styles.start;
break;
case positioning_1.Position.end:
className = styles.end;
break;
case positioning_1.Position.top:
className = styles.top;
break;
case positioning_1.Position.bottom:
className = styles.bottom;
}
return className;
};
SpinButton.prototype._onChange = function () {
/**
* A noop input change handler.
* https://github.com/facebook/react/issues/7027.
* Using the native onInput handler fixes the issue but onChange
* still need to be wired to avoid React console errors
* TODO: Check if issue is resolved when React 16 is available.
*/
};
/**
* This is used when validating text entry
* in the input (not when changed via the buttons)
* @param event - the event that fired
*/
SpinButton.prototype._validate = function (event) {
var element = event.target;
var value = element.value;
if (this.state.value) {
var newValue = this._onValidate(value);
if (newValue) {
this._lastValidValue = newValue;
this.setState({ value: newValue });
}
}
};
/**
* The method is needed to ensure we are updating the actual input value.
* without this our value will never change (and validation will not have the correct number)
* @param event - the event that was fired
*/
SpinButton.prototype._onInputChange = function (event) {
var element = event.target;
var value = element.value;
this.setState({
value: value,
});
};
/**
* Update the value with the given stepFunction
* @param shouldSpin - should we fire off another updateValue when we are done here? This should be true
* when spinning in response to a mouseDown
* @param stepFunction - function to use to step by
*/
SpinButton.prototype._updateValue = function (shouldSpin, stepDelay, stepFunction) {
var _this = this;
var newValue = stepFunction(this.state.value);
if (newValue) {
this._lastValidValue = newValue;
this.setState({ value: newValue });
}
if (this._spinningByMouse !== shouldSpin) {
this._spinningByMouse = shouldSpin;
}
if (shouldSpin) {
this._currentStepFunctionHandle = this._async.setTimeout(function () { _this._updateValue(shouldSpin, _this._stepDelay, stepFunction); }, stepDelay);
}
};
/**
* Stop spinning (clear any currently pending update and set spinning to false)
*/
SpinButton.prototype._stop = function () {
if (this._currentStepFunctionHandle >= 0) {
this._async.clearTimeout(this._currentStepFunctionHandle);
this._currentStepFunctionHandle = -1;
}
if (this._spinningByMouse || this.state.keyboardSpinDirection !== KeyboardSpinDirection.notSpinning) {
this._spinningByMouse = false;
this.setState({ keyboardSpinDirection: KeyboardSpinDirection.notSpinning });
}
};
/**
* Handle keydown on the text field. We need to update
* the value when up or down arrow are depressed
* @param event - the keyboardEvent that was fired
*/
SpinButton.prototype._handleKeyDown = function (event) {
if (this.props.disabled) {
this._stop();
// eat the up and down arrow keys to keep the page from scrolling
if (event.which === 38 /* up */ || event.which === 40 /* down */) {
event.preventDefault();
event.stopPropagation();
}
return;
}
var spinDirection = KeyboardSpinDirection.notSpinning;
if (event.which === 38 /* up */) {
spinDirection = KeyboardSpinDirection.up;
this._updateValue(false /* shouldSpin */, this._initialStepDelay, this._onIncrement);
}
else if (event.which === 40 /* down */) {
spinDirection = KeyboardSpinDirection.down;
this._updateValue(false /* shouldSpin */, this._initialStepDelay, this._onDecrement);
}
else if (event.which === 13 /* enter */) {
event.currentTarget.blur();
this.focus();
}
else if (event.which === 27 /* escape */) {
if (this.state.value !== this._lastValidValue) {
this.setState({ value: this._lastValidValue });
}
}
// style the increment/decrement button to look active
// when the corresponding up/down arrow keys trigger a step
if (this.state.keyboardSpinDirection !== spinDirection) {
this.setState({ keyboardSpinDirection: spinDirection });
}
};
/**
* Make sure that we have stopped spinning on keyUp
* if the up or down arrow fired this event
* @param event stop spinning if we
*/
SpinButton.prototype._handleKeyUp = function (event) {
if (this.props.disabled || event.which === 38 /* up */ || event.which === 40 /* down */) {
this._stop();
return;
}
};
SpinButton.prototype._onIncrementMouseDown = function () {
this._updateValue(true /* shouldSpin */, this._initialStepDelay, this._onIncrement);
};
SpinButton.prototype._onDecrementMouseDown = function () {
this._updateValue(true /* shouldSpin */, this._initialStepDelay, this._onDecrement);
};
return SpinButton;
}(Utilities_1.BaseComponent));
SpinButton.defaultProps = {
step: 1,
min: 0,
max: 100,
disabled: false,
labelPosition: positioning_1.Position.start,
label: null,
incrementButtonIcon: { iconName: 'ChevronUpSmall' },
decrementButtonIcon: { iconName: 'ChevronDownSmall' }
};
tslib_1.__decorate([
Utilities_1.autobind
], SpinButton.prototype, "_validate", null);
tslib_1.__decorate([
Utilities_1.autobind
], SpinButton.prototype, "_onInputChange", null);
tslib_1.__decorate([
Utilities_1.autobind
], SpinButton.prototype, "_updateValue", null);
tslib_1.__decorate([
Utilities_1.autobind
], SpinButton.prototype, "_stop", null);
tslib_1.__decorate([
Utilities_1.autobind
], SpinButton.prototype, "_handleKeyDown", null);
tslib_1.__decorate([
Utilities_1.autobind
], SpinButton.prototype, "_handleKeyUp", null);
tslib_1.__decorate([
Utilities_1.autobind
], SpinButton.prototype, "_onIncrementMouseDown", null);
tslib_1.__decorate([
Utilities_1.autobind
], SpinButton.prototype, "_onDecrementMouseDown", null);
exports.SpinButton = SpinButton;
//# sourceMappingURL=SpinButton.js.map