UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

314 lines • 15.4 kB
import * as tslib_1 from "tslib"; import * as React from 'react'; import { IconButton } from '../../Button'; import { Label } from '../../Label'; import { Icon } from '../../Icon'; import { BaseComponent, getId, autobind, customizable, calculatePrecision, precisionRound } from '../../Utilities'; import { Position } from '../../utilities/positioning'; import { getStyles, getArrowButtonStyles } from './SpinButton.styles'; import { getClassNames } from './SpinButton.classNames'; export var KeyboardSpinDirection; (function (KeyboardSpinDirection) { KeyboardSpinDirection[KeyboardSpinDirection["down"] = -1] = "down"; KeyboardSpinDirection[KeyboardSpinDirection["notSpinning"] = 0] = "notSpinning"; KeyboardSpinDirection[KeyboardSpinDirection["up"] = 1] = "up"; })(KeyboardSpinDirection || (KeyboardSpinDirection = {})); var SpinButton = /** @class */ (function (_super) { tslib_1.__extends(SpinButton, _super); function SpinButton(props) { var _this = _super.call(this, props) || this; _this._initialStepDelay = 400; _this._stepDelay = 75; /** * 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) + Number(_this.props.step), _this.props.max); newValue = precisionRound(newValue, _this.state.precision); return String(newValue); }; /** * Increment function to use if one is not passed in */ _this._defaultOnDecrement = function (value) { var newValue = Math.max(Number(value) - Number(_this.props.step), _this.props.min); newValue = precisionRound(newValue, _this.state.precision); return String(newValue); }; _this._warnMutuallyExclusive({ 'value': 'defaultValue' }); var value = props.value || props.defaultValue || String(props.min) || '0'; _this._lastValidValue = value; // Ensure that the autocalculated precision is not negative. var precision = props.precision || Math.max(calculatePrecision(props.step), 0); _this.state = { isFocused: false, value: value, keyboardSpinDirection: KeyboardSpinDirection.notSpinning, precision: precision }; _this._currentStepFunctionHandle = -1; _this._labelId = getId('Label'); _this._inputId = getId('input'); _this._spinningByMouse = false; if (!props.defaultValue && props.value !== undefined) { _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; } 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, precision: newProps.precision || this.state.precision }); }; SpinButton.prototype.render = function () { 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, incrementButtonAriaLabel = _a.incrementButtonAriaLabel, decrementButtonIcon = _a.decrementButtonIcon, decrementButtonAriaLabel = _a.decrementButtonAriaLabel, title = _a.title, ariaLabel = _a.ariaLabel, customStyles = _a.styles, customUpArrowButtonStyles = _a.upArrowButtonStyles, customDownArrowButtonStyles = _a.downArrowButtonStyles, theme = _a.theme; var _b = this.state, isFocused = _b.isFocused, value = _b.value, keyboardSpinDirection = _b.keyboardSpinDirection; var classNames = getClassNames(getStyles(theme, customStyles), !!disabled, !!isFocused, keyboardSpinDirection, labelPosition); return (React.createElement("div", { className: classNames.root }, labelPosition !== Position.bottom && React.createElement("div", { className: classNames.labelWrapper }, iconProps && React.createElement(Icon, { iconName: iconProps.iconName, className: classNames.icon, "aria-hidden": 'true' }), label && React.createElement(Label, { id: this._labelId, htmlFor: this._inputId, className: classNames.label }, label)), React.createElement("div", { className: classNames.spinButtonWrapper, title: title && title, "aria-label": ariaLabel && ariaLabel }, React.createElement("input", { value: value, id: this._inputId, onChange: this._onChange, onInput: this._onInputChange, className: classNames.input, type: 'text', role: 'spinbutton', "aria-labelledby": label && this._labelId, "aria-valuenow": value, "aria-valuemin": min && String(min), "aria-valuemax": max && String(max), onBlur: this._onBlur, ref: this._resolveRef('_input'), onFocus: this._onFocus, onKeyDown: this._handleKeyDown, onKeyUp: this._handleKeyUp, readOnly: disabled, disabled: disabled, "aria-disabled": disabled, "data-lpignore": true }), React.createElement("span", { className: classNames.arrowBox }, React.createElement(IconButton, { styles: getArrowButtonStyles(theme, true, customUpArrowButtonStyles), className: 'ms-UpButton', checked: keyboardSpinDirection === KeyboardSpinDirection.up, disabled: disabled, iconProps: incrementButtonIcon, onMouseDown: this._onIncrementMouseDown, onMouseLeave: this._stop, onMouseUp: this._stop, tabIndex: -1, ariaLabel: incrementButtonAriaLabel }), React.createElement(IconButton, { styles: getArrowButtonStyles(theme, false, customDownArrowButtonStyles), className: 'ms-DownButton', checked: keyboardSpinDirection === KeyboardSpinDirection.down, disabled: disabled, iconProps: decrementButtonIcon, onMouseDown: this._onDecrementMouseDown, onMouseLeave: this._stop, onMouseUp: this._stop, tabIndex: -1, ariaLabel: decrementButtonAriaLabel }))), labelPosition === Position.bottom && React.createElement("div", { className: classNames.labelWrapper }, iconProps && React.createElement(Icon, { iconName: iconProps.iconName, className: classNames.icon, "aria-hidden": 'true' }), label && React.createElement(Label, { id: this._labelId, htmlFor: this._inputId, className: classNames.label }, label)))); }; SpinButton.prototype.focus = function () { if (this._input) { this._input.focus(); } }; SpinButton.prototype._onFocus = function (ev) { if (this._spinningByMouse || this.state.keyboardSpinDirection !== KeyboardSpinDirection.notSpinning) { this._stop(); } this._input.select(); this.setState({ isFocused: true }); if (this.props.onFocus) { this.props.onFocus(ev); } }; SpinButton.prototype._onBlur = function (ev) { this._validate(ev); this.setState({ isFocused: false }); if (this.props.onBlur) { this.props.onBlur(ev); } }; Object.defineProperty(SpinButton.prototype, "value", { /** * Gets the value of the spin button. */ get: function () { return this.props.value === undefined ? this.state.value : this.props.value; }, enumerable: true, configurable: true }); 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); }; SpinButton.defaultProps = { step: 1, min: 0, max: 100, disabled: false, labelPosition: Position.start, label: '', incrementButtonIcon: { iconName: 'ChevronUpSmall' }, decrementButtonIcon: { iconName: 'ChevronDownSmall' } }; tslib_1.__decorate([ autobind ], SpinButton.prototype, "_onFocus", null); tslib_1.__decorate([ autobind ], SpinButton.prototype, "_onBlur", null); tslib_1.__decorate([ autobind ], SpinButton.prototype, "_validate", null); tslib_1.__decorate([ autobind ], SpinButton.prototype, "_onInputChange", null); tslib_1.__decorate([ autobind ], SpinButton.prototype, "_updateValue", null); tslib_1.__decorate([ autobind ], SpinButton.prototype, "_stop", null); tslib_1.__decorate([ autobind ], SpinButton.prototype, "_handleKeyDown", null); tslib_1.__decorate([ autobind ], SpinButton.prototype, "_handleKeyUp", null); tslib_1.__decorate([ autobind ], SpinButton.prototype, "_onIncrementMouseDown", null); tslib_1.__decorate([ autobind ], SpinButton.prototype, "_onDecrementMouseDown", null); SpinButton = tslib_1.__decorate([ customizable('SpinButton', ['theme']) ], SpinButton); return SpinButton; }(BaseComponent)); export { SpinButton }; //# sourceMappingURL=SpinButton.js.map