UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

307 lines (305 loc) • 14.6 kB
"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