grommet
Version:
The most advanced UX framework for enterprise applications.
422 lines (354 loc) • 15.7 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _classnames3 = require('classnames');
var _classnames4 = _interopRequireDefault(_classnames3);
var _KeyboardAccelerators = require('../utils/KeyboardAccelerators');
var _KeyboardAccelerators2 = _interopRequireDefault(_KeyboardAccelerators);
var _Drop = require('../utils/Drop');
var _Drop2 = _interopRequireDefault(_Drop);
var _Intl = require('../utils/Intl');
var _Intl2 = _interopRequireDefault(_Intl);
var _Announcer = require('../utils/Announcer');
var _CSSClassnames = require('../utils/CSSClassnames');
var _CSSClassnames2 = _interopRequireDefault(_CSSClassnames);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // (C) Copyright 2014 Hewlett Packard Enterprise Development LP
var CLASS_ROOT = _CSSClassnames2.default.TEXT_INPUT;
var INPUT = _CSSClassnames2.default.INPUT;
var TextInput = function (_Component) {
_inherits(TextInput, _Component);
function TextInput(props, context) {
_classCallCheck(this, TextInput);
var _this = _possibleConstructorReturn(this, (TextInput.__proto__ || Object.getPrototypeOf(TextInput)).call(this, props, context));
_this._onInputChange = _this._onInputChange.bind(_this);
_this._onAddDrop = _this._onAddDrop.bind(_this);
_this._onRemoveDrop = _this._onRemoveDrop.bind(_this);
_this._onNextSuggestion = _this._onNextSuggestion.bind(_this);
_this._onPreviousSuggestion = _this._onPreviousSuggestion.bind(_this);
_this._onEnter = _this._onEnter.bind(_this);
_this._onClickSuggestion = _this._onClickSuggestion.bind(_this);
_this._onFocus = _this._onFocus.bind(_this);
_this._onInputKeyDown = _this._onInputKeyDown.bind(_this);
_this._stopPropagation = _this._stopPropagation.bind(_this);
_this._announceSuggestion = _this._announceSuggestion.bind(_this);
_this.state = {
announceChange: false,
dropActive: false,
activeSuggestionIndex: -1
};
return _this;
}
_createClass(TextInput, [{
key: 'componentDidUpdate',
value: function componentDidUpdate(prevProps, prevState) {
var suggestions = this.props.suggestions;
var _state = this.state,
announceChange = _state.announceChange,
dropActive = _state.dropActive,
focused = _state.focused;
var intl = this.context.intl;
// Set up keyboard listeners appropriate to the current state.
var activeKeyboardHandlers = {
esc: this._onRemoveDrop,
tab: this._onRemoveDrop,
up: this._onPreviousSuggestion,
down: this._onNextSuggestion,
enter: this._onEnter
};
var focusedKeyboardHandlers = {
down: this._onAddDrop
};
// the order here is important, need to turn off keys before turning on
if (!focused && prevState.focused) {
_KeyboardAccelerators2.default.stopListeningToKeyboard(this, focusedKeyboardHandlers);
}
if (!dropActive && prevState.dropActive) {
document.removeEventListener('click', this._onRemoveDrop);
_KeyboardAccelerators2.default.stopListeningToKeyboard(this, activeKeyboardHandlers);
if (this._drop) {
this._drop.remove();
this._drop = undefined;
}
}
if (focused && !prevState.focused) {
_KeyboardAccelerators2.default.startListeningToKeyboard(this, focusedKeyboardHandlers);
}
if (dropActive && !prevState.dropActive) {
document.addEventListener('click', this._onRemoveDrop);
_KeyboardAccelerators2.default.startListeningToKeyboard(this, activeKeyboardHandlers);
// If this is inside a FormField, place the drop in reference to it.
var control = this.componentRef;
this._drop = new _Drop2.default(control, this._renderDropContent(), {
align: { top: 'bottom', left: 'left' },
responsive: false // so suggestion changes don't re-align
});
} else if (dropActive && prevState.dropActive) {
this._drop.render(this._renderDropContent());
}
if (announceChange && suggestions) {
var matchResultsMessage = _Intl2.default.getMessage(intl, 'Match Results', {
count: suggestions.length
});
var navigationHelpMessage = '';
if (suggestions.length) {
navigationHelpMessage = '(' + _Intl2.default.getMessage(intl, 'Navigation Help') + ')';
}
(0, _Announcer.announce)(matchResultsMessage + ' ' + navigationHelpMessage);
this.setState({ announceChange: false });
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
document.removeEventListener('click', this._onRemoveDrop);
if (this._drop) {
this._drop.remove();
}
}
}, {
key: '_stopPropagation',
value: function _stopPropagation() {
if (document.activeElement === this.componentRef) {
return true;
}
}
}, {
key: '_onInputChange',
value: function _onInputChange(event) {
var _props = this.props,
onDOMChange = _props.onDOMChange,
suggestions = _props.suggestions;
if (suggestions && Array.isArray(suggestions)) {
this.setState({
activeSuggestionIndex: -1, announceChange: true, dropActive: true
});
}
if (onDOMChange) {
onDOMChange(event);
}
}
}, {
key: '_announceSuggestion',
value: function _announceSuggestion(index) {
var suggestions = this.props.suggestions;
var intl = this.context.intl;
if (suggestions && suggestions.length > 0) {
var labelMessage = this._renderLabel(suggestions[index]);
var enterSelectMessage = _Intl2.default.getMessage(intl, 'Enter Select');
(0, _Announcer.announce)(labelMessage + ' ' + enterSelectMessage);
}
}
}, {
key: '_onAddDrop',
value: function _onAddDrop(event) {
var _props2 = this.props,
suggestions = _props2.suggestions,
value = _props2.value;
// Get values of suggestions, so we can highlight selected suggestion
if (suggestions) {
event.preventDefault();
var suggestionValues = suggestions.map(function (suggestion) {
if ((typeof suggestion === 'undefined' ? 'undefined' : _typeof(suggestion)) === 'object') {
return suggestion.value;
} else {
return suggestion;
}
});
var activeSuggestionIndex = suggestionValues.indexOf(value);
this.setState({
dropActive: true,
activeSuggestionIndex: activeSuggestionIndex
});
}
}
}, {
key: '_onRemoveDrop',
value: function _onRemoveDrop() {
this.setState({ dropActive: false });
}
}, {
key: '_onNextSuggestion',
value: function _onNextSuggestion() {
var suggestions = this.props.suggestions;
var activeSuggestionIndex = this.state.activeSuggestionIndex;
var index = Math.min(activeSuggestionIndex + 1, suggestions.length - 1);
this.setState({ activeSuggestionIndex: index }, this._announceSuggestion.bind(this, index));
}
}, {
key: '_onPreviousSuggestion',
value: function _onPreviousSuggestion() {
var activeSuggestionIndex = this.state.activeSuggestionIndex;
var index = Math.max(activeSuggestionIndex - 1, 0);
this.setState({ activeSuggestionIndex: index }, this._announceSuggestion.bind(this, index));
}
}, {
key: '_onEnter',
value: function _onEnter(event) {
var _this2 = this;
var _props3 = this.props,
onSelect = _props3.onSelect,
suggestions = _props3.suggestions;
var activeSuggestionIndex = this.state.activeSuggestionIndex;
var intl = this.context.intl;
this.setState({ dropActive: false });
if (activeSuggestionIndex >= 0) {
event.preventDefault(); // prevent submitting forms
var suggestion = suggestions[activeSuggestionIndex];
this.setState({ value: suggestion }, function () {
var suggestionMessage = _this2._renderLabel(suggestion);
var selectedMessage = _Intl2.default.getMessage(intl, 'Selected');
(0, _Announcer.announce)(suggestionMessage + ' ' + selectedMessage);
});
if (onSelect) {
onSelect({
target: this.componentRef, suggestion: suggestion
});
}
}
}
}, {
key: '_onClickSuggestion',
value: function _onClickSuggestion(suggestion) {
var onSelect = this.props.onSelect;
this.setState({ value: suggestion, dropActive: false });
if (onSelect) {
onSelect({
target: this.componentRef, suggestion: suggestion
});
}
}
}, {
key: '_onFocus',
value: function _onFocus(event) {
var onFocus = this.props.onFocus;
this.setState({
focused: true,
activeSuggestionIndex: -1
});
if (onFocus) {
onFocus(event);
}
}
}, {
key: '_onInputKeyDown',
value: function _onInputKeyDown(event) {
var _props4 = this.props,
onKeyDown = _props4.onKeyDown,
suggestions = _props4.suggestions;
var dropActive = this.state.dropActive;
if (suggestions) {
var up = 38;
var down = 40;
var tab = 9;
if (event.keyCode === up || event.keyCode === down) {
// stop the input to move the cursor when suggestions are present
event.preventDefault();
if (event.keyCode === down && !dropActive) {
this._onAddDrop(event);
}
}
if (event.keyCode === tab) {
this.setState({ focused: false });
}
}
if (onKeyDown) {
onKeyDown(event);
}
}
}, {
key: '_renderLabel',
value: function _renderLabel(suggestion) {
if (suggestion && (typeof suggestion === 'undefined' ? 'undefined' : _typeof(suggestion)) === 'object') {
return suggestion.label || suggestion.value;
} else {
return suggestion;
}
}
}, {
key: '_renderDropContent',
value: function _renderDropContent() {
var _this3 = this;
var suggestions = this.props.suggestions;
var activeSuggestionIndex = this.state.activeSuggestionIndex;
var items = void 0;
if (suggestions) {
items = suggestions.map(function (suggestion, index) {
var _classnames;
var classes = (0, _classnames4.default)((_classnames = {}, _defineProperty(_classnames, CLASS_ROOT + '__suggestion', true), _defineProperty(_classnames, CLASS_ROOT + '__suggestion--active', index === activeSuggestionIndex), _classnames));
return _react2.default.createElement(
'li',
{ key: index, className: classes,
onClick: _this3._onClickSuggestion.bind(_this3, suggestion) },
_this3._renderLabel(suggestion)
);
});
}
return _react2.default.createElement(
'ol',
{ className: CLASS_ROOT + '__suggestions', onClick: this._onRemoveDrop },
items
);
}
}, {
key: 'render',
value: function render() {
var _this4 = this;
var _props5 = this.props,
className = _props5.className,
defaultValue = _props5.defaultValue,
value = _props5.value,
placeHolder = _props5.placeHolder,
props = _objectWithoutProperties(_props5, ['className', 'defaultValue', 'value', 'placeHolder']);
delete props.suggestions;
delete props.onDOMChange;
delete props.onSelect;
var classes = (0, _classnames4.default)(CLASS_ROOT, INPUT, _defineProperty({}, CLASS_ROOT + '--active', this.state.active), className);
return _react2.default.createElement('input', _extends({
ref: function ref(_ref) {
return _this4.componentRef = _ref;
},
type: 'text',
autoComplete: 'off'
}, props, {
className: classes,
defaultValue: this._renderLabel(defaultValue),
value: this._renderLabel(value),
placeholder: placeHolder,
onChange: this._onInputChange, onFocus: this._onFocus,
onKeyDown: this._onInputKeyDown }));
}
}]);
return TextInput;
}(_react.Component);
TextInput.displayName = 'TextInput';
exports.default = TextInput;
TextInput.contextTypes = {
intl: _propTypes2.default.object
};
TextInput.propTypes = {
defaultValue: _propTypes2.default.string,
id: _propTypes2.default.string,
name: _propTypes2.default.string,
onDOMChange: _propTypes2.default.func,
onSelect: _propTypes2.default.func,
placeHolder: _propTypes2.default.string,
suggestions: _propTypes2.default.arrayOf(_propTypes2.default.oneOfType([_propTypes2.default.shape({
label: _propTypes2.default.node,
value: _propTypes2.default.any
}), _propTypes2.default.string])),
value: _propTypes2.default.string
};
module.exports = exports['default'];