office-ui-fabric-react
Version:
Reusable React components for building experiences for Office 365.
366 lines • 17.4 kB
JavaScript
import * as tslib_1 from "tslib";
import * as React from 'react';
import { Label } from '../../Label';
import { Icon } from '../../Icon';
import { DelayedRender, BaseComponent, getId, getNativeProps, inputProperties, textAreaProperties, createRef, classNamesFunction } from '../../Utilities';
var getClassNames = classNamesFunction();
var DEFAULT_STATE_VALUE = '';
var TextFieldBase = /** @class */ (function (_super) {
tslib_1.__extends(TextFieldBase, _super);
function TextFieldBase(props) {
var _this = _super.call(this, props) || this;
_this._textElement = createRef();
_this._onRenderLabel = function (props) {
var label = props.label, required = props.required;
// IProcessedStyleSet definition requires casting for what Label expects as its styles prop
var labelStyles = _this._classNames.subComponentStyles
? _this._classNames.subComponentStyles.label
: undefined;
if (label) {
return (React.createElement(Label, { required: required, htmlFor: _this._id, styles: labelStyles }, props.label));
}
return null;
};
_this._onRenderDescription = function (props) {
if (props.description) {
return React.createElement("span", { className: _this._classNames.description }, props.description);
}
return null;
};
_this._warnDeprecations({
iconClass: 'iconProps',
addonString: 'prefix',
onRenderAddon: 'onRenderPrefix',
onChanged: 'onChange'
});
_this._warnMutuallyExclusive({
value: 'defaultValue'
});
_this._id = props.id || getId('TextField');
_this._descriptionId = getId('TextFieldDescription');
if (props.value !== undefined) {
_this._latestValue = props.value;
}
else if (props.defaultValue !== undefined) {
_this._latestValue = props.defaultValue;
}
else {
_this._latestValue = DEFAULT_STATE_VALUE;
}
_this.state = {
value: _this._latestValue,
isFocused: false,
errorMessage: ''
};
_this._onInputChange = _this._onInputChange.bind(_this);
_this._onFocus = _this._onFocus.bind(_this);
_this._onBlur = _this._onBlur.bind(_this);
_this._delayedValidate = _this._async.debounce(_this._validate, _this.props.deferredValidationTime);
_this._lastValidation = 0;
_this._isDescriptionAvailable = false;
return _this;
}
Object.defineProperty(TextFieldBase.prototype, "value", {
/**
* Gets the current value of the text field.
*/
get: function () {
return this.state.value;
},
enumerable: true,
configurable: true
});
TextFieldBase.prototype.componentDidMount = function () {
this._isMounted = true;
this._adjustInputHeight();
if (this.props.validateOnLoad) {
this._validate(this.state.value);
}
};
TextFieldBase.prototype.componentWillReceiveProps = function (newProps) {
var onBeforeChange = this.props.onBeforeChange;
// If old value prop was undefined, then component is controlled and we should
// respect new undefined value and update state accordingly.
if (newProps.value !== this.state.value && (newProps.value !== undefined || this.props.value !== undefined)) {
if (onBeforeChange) {
onBeforeChange(newProps.value);
}
this._id = newProps.id || this._id;
this._setValue(newProps.value);
var validateOnFocusIn = newProps.validateOnFocusIn, validateOnFocusOut = newProps.validateOnFocusOut;
if (!(validateOnFocusIn || validateOnFocusOut)) {
this._delayedValidate(newProps.value);
}
}
// If component is not currently controlled and defaultValue changes, set value to new defaultValue.
if (newProps.defaultValue !== this.props.defaultValue && newProps.value === undefined) {
this._setValue(newProps.defaultValue);
}
};
TextFieldBase.prototype.componentWillUnmount = function () {
this._isMounted = false;
};
TextFieldBase.prototype.render = function () {
var _a = this.props, borderless = _a.borderless, className = _a.className, description = _a.description, disabled = _a.disabled, iconClass = _a.iconClass, iconProps = _a.iconProps, inputClassName = _a.inputClassName, label = _a.label, multiline = _a.multiline, required = _a.required, underlined = _a.underlined, addonString = _a.addonString, // @deprecated
prefix = _a.prefix, resizable = _a.resizable, suffix = _a.suffix, theme = _a.theme, styles = _a.styles, _b = _a.onRenderAddon, onRenderAddon = _b === void 0 ? this._onRenderAddon : _b, // @deprecated
_c = _a.onRenderPrefix, // @deprecated
onRenderPrefix = _c === void 0 ? this._onRenderPrefix : _c, _d = _a.onRenderSuffix, onRenderSuffix = _d === void 0 ? this._onRenderSuffix : _d, _e = _a.onRenderLabel, onRenderLabel = _e === void 0 ? this._onRenderLabel : _e, _f = _a.onRenderDescription, onRenderDescription = _f === void 0 ? this._onRenderDescription : _f;
var isFocused = this.state.isFocused;
var errorMessage = this._errorMessage;
this._classNames = getClassNames(styles, {
theme: theme,
className: className,
disabled: disabled,
focused: isFocused,
required: required,
multiline: multiline,
hasLabel: !!label,
hasErrorMessage: !!errorMessage,
borderless: borderless,
resizable: resizable,
hasIcon: !!iconProps,
underlined: underlined,
iconClass: iconClass,
inputClassName: inputClassName
});
// If a custom description render function is supplied then treat description as always available.
// Otherwise defer to the presence of description or error message text.
this._isDescriptionAvailable = Boolean(this.props.onRenderDescription || description || errorMessage);
return (React.createElement("div", { className: this._classNames.root },
React.createElement("div", { className: this._classNames.wrapper },
onRenderLabel(this.props, this._onRenderLabel),
React.createElement("div", { className: this._classNames.fieldGroup },
(addonString !== undefined || this.props.onRenderAddon) && (React.createElement("div", { className: this._classNames.prefix }, onRenderAddon(this.props, this._onRenderAddon))),
(prefix !== undefined || this.props.onRenderPrefix) && (React.createElement("div", { className: this._classNames.prefix }, onRenderPrefix(this.props, this._onRenderPrefix))),
multiline ? this._renderTextArea() : this._renderInput(),
(iconClass || iconProps) && React.createElement(Icon, tslib_1.__assign({ className: this._classNames.icon }, iconProps)),
(suffix !== undefined || this.props.onRenderSuffix) && (React.createElement("div", { className: this._classNames.suffix }, onRenderSuffix(this.props, this._onRenderSuffix))))),
this._isDescriptionAvailable && (React.createElement("span", { id: this._descriptionId },
onRenderDescription(this.props, this._onRenderDescription),
errorMessage && (React.createElement("div", { role: "alert" },
React.createElement(DelayedRender, null,
React.createElement("p", { className: this._classNames.errorMessage },
React.createElement("span", { "data-automation-id": "error-message" }, errorMessage)))))))));
};
/**
* Sets focus on the text field
*/
TextFieldBase.prototype.focus = function () {
if (this._textElement.current) {
this._textElement.current.focus();
}
};
/**
* Selects the text field
*/
TextFieldBase.prototype.select = function () {
if (this._textElement.current) {
this._textElement.current.select();
}
};
/**
* Sets the selection start of the text field to a specified value
*/
TextFieldBase.prototype.setSelectionStart = function (value) {
if (this._textElement.current) {
this._textElement.current.selectionStart = value;
}
};
/**
* Sets the selection end of the text field to a specified value
*/
TextFieldBase.prototype.setSelectionEnd = function (value) {
if (this._textElement.current) {
this._textElement.current.selectionEnd = value;
}
};
Object.defineProperty(TextFieldBase.prototype, "selectionStart", {
/**
* Gets the selection start of the text field
*/
get: function () {
return this._textElement.current ? this._textElement.current.selectionStart : -1;
},
enumerable: true,
configurable: true
});
Object.defineProperty(TextFieldBase.prototype, "selectionEnd", {
/**
* Gets the selection end of the text field
*/
get: function () {
return this._textElement.current ? this._textElement.current.selectionEnd : -1;
},
enumerable: true,
configurable: true
});
/**
* Sets the start and end positions of a selection in a text field.
* @param start Index of the start of the selection.
* @param end Index of the end of the selection.
*/
TextFieldBase.prototype.setSelectionRange = function (start, end) {
if (this._textElement.current) {
this._textElement.current.setSelectionRange(start, end);
}
};
TextFieldBase.prototype._setValue = function (value) {
var _this = this;
this._latestValue = value;
this.setState({
value: value || DEFAULT_STATE_VALUE,
errorMessage: ''
}, function () {
_this._adjustInputHeight();
});
};
TextFieldBase.prototype._onFocus = function (ev) {
if (this.props.onFocus) {
this.props.onFocus(ev);
}
this.setState({ isFocused: true });
if (this.props.validateOnFocusIn) {
this._validate(this.state.value);
}
};
TextFieldBase.prototype._onBlur = function (ev) {
if (this.props.onBlur) {
this.props.onBlur(ev);
}
this.setState({ isFocused: false });
if (this.props.validateOnFocusOut) {
this._validate(this.state.value);
}
};
// @deprecated
TextFieldBase.prototype._onRenderAddon = function (props) {
var addonString = props.addonString;
return React.createElement("span", { style: { paddingBottom: '1px' } }, addonString);
};
TextFieldBase.prototype._onRenderPrefix = function (props) {
var prefix = props.prefix;
return React.createElement("span", { style: { paddingBottom: '1px' } }, prefix);
};
TextFieldBase.prototype._onRenderSuffix = function (props) {
var suffix = props.suffix;
return React.createElement("span", { style: { paddingBottom: '1px' } }, suffix);
};
Object.defineProperty(TextFieldBase.prototype, "_errorMessage", {
get: function () {
var errorMessage = this.state.errorMessage;
if (!errorMessage && this.props.errorMessage) {
errorMessage = this.props.errorMessage;
}
return errorMessage;
},
enumerable: true,
configurable: true
});
TextFieldBase.prototype._renderTextArea = function () {
var textAreaProps = getNativeProps(this.props, textAreaProperties, ['defaultValue']);
return (React.createElement("textarea", tslib_1.__assign({ id: this._id }, textAreaProps, { ref: this._textElement, value: this.state.value, onInput: this._onInputChange, onChange: this._onInputChange, className: this._classNames.field, "aria-describedby": this._isDescriptionAvailable ? this._descriptionId : this.props['aria-describedby'], "aria-invalid": !!this.state.errorMessage, "aria-label": this.props.ariaLabel, readOnly: this.props.readOnly, onFocus: this._onFocus, onBlur: this._onBlur })));
};
TextFieldBase.prototype._renderInput = function () {
var inputProps = getNativeProps(this.props, inputProperties, [
'defaultValue'
]);
return (React.createElement("input", tslib_1.__assign({ type: 'text', id: this._id }, inputProps, { ref: this._textElement, value: this.state.value, onInput: this._onInputChange, onChange: this._onInputChange, className: this._classNames.field, "aria-label": this.props.ariaLabel, "aria-describedby": this._isDescriptionAvailable ? this._descriptionId : this.props['aria-describedby'], "aria-invalid": !!this.state.errorMessage, readOnly: this.props.readOnly, onFocus: this._onFocus, onBlur: this._onBlur })));
};
TextFieldBase.prototype._onInputChange = function (event) {
var _this = this;
event.persist();
var element = event.target;
var value = element.value;
// Avoid doing unnecessary work when the value has not changed.
if (value === this._latestValue) {
return;
}
this._latestValue = value;
this.setState({
value: value
}, function () {
_this._adjustInputHeight();
if (_this.props.onChange) {
_this.props.onChange(event, value);
}
if (_this.props.onChanged) {
_this.props.onChanged(value);
}
});
var _a = this.props, validateOnFocusIn = _a.validateOnFocusIn, validateOnFocusOut = _a.validateOnFocusOut;
if (!(validateOnFocusIn || validateOnFocusOut)) {
this._delayedValidate(value);
}
var onBeforeChange = this.props.onBeforeChange;
onBeforeChange(value);
};
TextFieldBase.prototype._validate = function (value) {
var _this = this;
var _a = this.props, validateOnFocusIn = _a.validateOnFocusIn, validateOnFocusOut = _a.validateOnFocusOut;
// In case of _validate called multi-times during executing validate logic with promise return.
if (this._latestValidateValue === value && !(validateOnFocusIn || validateOnFocusOut)) {
return;
}
this._latestValidateValue = value;
var onGetErrorMessage = this.props.onGetErrorMessage;
var result = onGetErrorMessage(value || '');
if (result !== undefined) {
if (typeof result === 'string') {
this.setState({
errorMessage: result
});
this._notifyAfterValidate(value, result);
}
else {
var currentValidation_1 = ++this._lastValidation;
result.then(function (errorMessage) {
if (_this._isMounted && currentValidation_1 === _this._lastValidation) {
_this.setState({ errorMessage: errorMessage });
}
_this._notifyAfterValidate(value, errorMessage);
});
}
}
else {
this._notifyAfterValidate(value, '');
}
};
TextFieldBase.prototype._notifyAfterValidate = function (value, errorMessage) {
if (this._isMounted && value === this.state.value && this.props.onNotifyValidationResult) {
this.props.onNotifyValidationResult(errorMessage, value);
}
};
TextFieldBase.prototype._adjustInputHeight = function () {
if (this._textElement.current && this.props.autoAdjustHeight && this.props.multiline) {
var textField = this._textElement.current;
textField.style.height = '';
var scrollHeight = textField.scrollHeight + 2; // +2 to avoid vertical scroll bars
textField.style.height = scrollHeight + 'px';
}
};
TextFieldBase.defaultProps = {
multiline: false,
resizable: true,
autoAdjustHeight: false,
underlined: false,
borderless: false,
onChange: function () {
/* noop */
},
onBeforeChange: function () {
/* noop */
},
onNotifyValidationResult: function () {
/* noop */
},
onGetErrorMessage: function () { return undefined; },
deferredValidationTime: 200,
errorMessage: '',
validateOnFocusIn: false,
validateOnFocusOut: false,
validateOnLoad: true
};
return TextFieldBase;
}(BaseComponent));
export { TextFieldBase };
//# sourceMappingURL=TextField.base.js.map