@workflo/components
Version:
474 lines (398 loc) • 18.9 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 _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 _reactRedux = require('react-redux');
var _reducerAndActions = require('./reducerAndActions');
var _AutoWhatever = require('./AutoWhatever');
var _AutoWhatever2 = _interopRequireDefault(_AutoWhatever);
var _TypeAheadStyles = require('./TypeAheadStyles');
var _TypeAheadStyles2 = _interopRequireDefault(_TypeAheadStyles);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: 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; }
// Based on https://github.com/moroshko/react-autosuggest
function mapStateToProps(state) {
return {
isFocused: state.isFocused,
isCollapsed: state.isCollapsed,
focusedSectionIndex: state.focusedSectionIndex,
focusedSuggestionIndex: state.focusedSuggestionIndex,
valueBeforeUpDown: state.valueBeforeUpDown,
lastAction: state.lastAction
};
}
function mapDispatchToProps(dispatch) {
return {
inputFocused: function inputFocused(shouldRenderSuggestions) {
dispatch((0, _reducerAndActions.inputFocused)(shouldRenderSuggestions));
},
inputBlurred: function inputBlurred() {
dispatch((0, _reducerAndActions.inputBlurred)());
},
inputChanged: function inputChanged(shouldRenderSuggestions, lastAction) {
dispatch((0, _reducerAndActions.inputChanged)(shouldRenderSuggestions, lastAction));
},
updateFocusedSuggestion: function updateFocusedSuggestion(sectionIndex, suggestionIndex, value) {
dispatch((0, _reducerAndActions.updateFocusedSuggestion)(sectionIndex, suggestionIndex, value));
},
revealSuggestions: function revealSuggestions() {
dispatch((0, _reducerAndActions.revealSuggestions)());
},
closeSuggestions: function closeSuggestions(lastAction) {
dispatch((0, _reducerAndActions.closeSuggestions)(lastAction));
}
};
}
var TypeAhead = function (_Component) {
_inherits(TypeAhead, _Component);
function TypeAhead() {
_classCallCheck(this, TypeAhead);
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(TypeAhead).call(this));
_this.saveInput = _this.saveInput.bind(_this);
return _this;
}
_createClass(TypeAhead, [{
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
if (nextProps.suggestions !== this.props.suggestions) {
var suggestions = nextProps.suggestions;
var inputProps = nextProps.inputProps;
var shouldRenderSuggestions = nextProps.shouldRenderSuggestions;
var isCollapsed = nextProps.isCollapsed;
var revealSuggestions = nextProps.revealSuggestions;
var lastAction = nextProps.lastAction;
var value = inputProps.value;
if (isCollapsed && lastAction !== 'click' && lastAction !== 'enter' && suggestions.length > 0 && shouldRenderSuggestions(value)) {
revealSuggestions();
}
}
}
}, {
key: 'getSuggestion',
value: function getSuggestion(sectionIndex, suggestionIndex) {
var _props = this.props;
var suggestions = _props.suggestions;
var multiSection = _props.multiSection;
var getSectionSuggestions = _props.getSectionSuggestions;
if (multiSection) {
return getSectionSuggestions(suggestions[sectionIndex])[suggestionIndex];
}
return suggestions[suggestionIndex];
}
}, {
key: 'getFocusedSuggestion',
value: function getFocusedSuggestion() {
var _props2 = this.props;
var focusedSectionIndex = _props2.focusedSectionIndex;
var focusedSuggestionIndex = _props2.focusedSuggestionIndex;
if (focusedSuggestionIndex === null) {
return null;
}
return this.getSuggestion(focusedSectionIndex, focusedSuggestionIndex);
}
}, {
key: 'getSuggestionValueByIndex',
value: function getSuggestionValueByIndex(sectionIndex, suggestionIndex) {
var getSuggestionValue = this.props.getSuggestionValue;
return getSuggestionValue(this.getSuggestion(sectionIndex, suggestionIndex));
}
}, {
key: 'getSuggestionIndices',
value: function getSuggestionIndices(suggestionElement) {
var sectionIndex = suggestionElement.getAttribute('data-section-index');
var suggestionIndex = suggestionElement.getAttribute('data-suggestion-index');
return {
sectionIndex: typeof sectionIndex === 'string' ? parseInt(sectionIndex, 10) : null,
suggestionIndex: parseInt(suggestionIndex, 10)
};
}
}, {
key: 'findSuggestionElement',
value: function findSuggestionElement(startNode) {
var node = startNode;
do {
if (node.getAttribute('data-suggestion-index') !== null) {
return node;
}
node = node.parentNode;
} while (node !== null);
console.error('Clicked element:', startNode); // eslint-disable-line no-console
throw new Error('Couldn\'t find suggestion element');
}
}, {
key: 'maybeCallOnChange',
value: function maybeCallOnChange(event, newValue, method) {
var _props$inputProps = this.props.inputProps;
var value = _props$inputProps.value;
var onChange = _props$inputProps.onChange;
if (newValue !== value) {
onChange && onChange(event, { newValue: newValue, method: method });
}
}
}, {
key: 'maybeCallOnSuggestionsUpdateRequested',
value: function maybeCallOnSuggestionsUpdateRequested(data) {
var _props3 = this.props;
var onSuggestionsUpdateRequested = _props3.onSuggestionsUpdateRequested;
var shouldRenderSuggestions = _props3.shouldRenderSuggestions;
if (shouldRenderSuggestions(data.value)) {
onSuggestionsUpdateRequested(data);
}
}
}, {
key: 'willRenderSuggestions',
value: function willRenderSuggestions() {
var _props4 = this.props;
var suggestions = _props4.suggestions;
var inputProps = _props4.inputProps;
var shouldRenderSuggestions = _props4.shouldRenderSuggestions;
var value = inputProps.value;
return suggestions.length > 0 && shouldRenderSuggestions(value);
}
}, {
key: 'saveInput',
value: function saveInput(autowhatever) {
if (autowhatever !== null) {
var input = autowhatever.refs.input;
this.input = input;
//this.props.inputRef(input)
}
}
}, {
key: 'render',
value: function render() {
var _this2 = this;
var _props5 = this.props;
var suggestions = _props5.suggestions;
var renderSuggestion = _props5.renderSuggestion;
var inputProps = _props5.inputProps;
var shouldRenderSuggestions = _props5.shouldRenderSuggestions;
var onSuggestionSelected = _props5.onSuggestionSelected;
var multiSection = _props5.multiSection;
var renderSectionTitle = _props5.renderSectionTitle;
var id = _props5.id;
var getSectionSuggestions = _props5.getSectionSuggestions;
var focusInputOnSuggestionClick = _props5.focusInputOnSuggestionClick;
var theme = _props5.theme;
var isFocused = _props5.isFocused;
var isCollapsed = _props5.isCollapsed;
var focusedSectionIndex = _props5.focusedSectionIndex;
var focusedSuggestionIndex = _props5.focusedSuggestionIndex;
var valueBeforeUpDown = _props5.valueBeforeUpDown;
var inputFocused = _props5.inputFocused;
var inputBlurred = _props5.inputBlurred;
var inputChanged = _props5.inputChanged;
var updateFocusedSuggestion = _props5.updateFocusedSuggestion;
var revealSuggestions = _props5.revealSuggestions;
var closeSuggestions = _props5.closeSuggestions;
var value = inputProps.value;
var _onBlur = inputProps.onBlur;
var _onFocus = inputProps.onFocus;
var _onKeyDown = inputProps.onKeyDown;
var isOpen = isFocused && !isCollapsed && this.willRenderSuggestions();
var items = isOpen ? suggestions : [];
// const finalTheme = { ...styles, ...theme }
var autowhateverInputProps = _extends({}, inputProps, {
onFocus: function onFocus(event) {
if (!_this2.justClickedOnSuggestion) {
inputFocused(shouldRenderSuggestions(value));
_onFocus && _onFocus(event);
}
},
onBlur: function onBlur(event) {
_this2.onBlurEvent = event;
if (!_this2.justClickedOnSuggestion) {
inputBlurred();
_onBlur && _onBlur(event);
if (valueBeforeUpDown !== null && value !== valueBeforeUpDown) {
_this2.maybeCallOnSuggestionsUpdateRequested({ value: value, reason: 'blur' });
}
}
},
onChange: function onChange(event) {
var value = event.target.value;
var shouldRenderSuggestions = _this2.props.shouldRenderSuggestions;
_this2.maybeCallOnChange(event, value, 'type');
inputChanged(shouldRenderSuggestions(value), 'type');
_this2.maybeCallOnSuggestionsUpdateRequested({ value: value, reason: 'type' });
},
onKeyDown: function onKeyDown(event, data) {
switch (event.key) {
case 'ArrowDown':
case 'ArrowUp':
if (isCollapsed) {
if (_this2.willRenderSuggestions()) {
revealSuggestions();
}
} else if (suggestions.length > 0) {
var newFocusedSectionIndex = data.newFocusedSectionIndex;
var newFocusedItemIndex = data.newFocusedItemIndex;
var newValue = newFocusedItemIndex === null ? valueBeforeUpDown : _this2.getSuggestionValueByIndex(newFocusedSectionIndex, newFocusedItemIndex);
updateFocusedSuggestion(newFocusedSectionIndex, newFocusedItemIndex, value);
_this2.maybeCallOnChange(event, newValue, event.key === 'ArrowDown' ? 'down' : 'up');
}
event.preventDefault();
break;
case 'Enter':
{
var focusedSuggestion = _this2.getFocusedSuggestion();
closeSuggestions('enter');
if (focusedSuggestion !== null) {
onSuggestionSelected(event, {
suggestion: focusedSuggestion,
suggestionValue: value,
sectionIndex: focusedSectionIndex,
method: 'enter'
});
_this2.maybeCallOnSuggestionsUpdateRequested({ value: value, reason: 'enter' });
}
break;
}
case 'Escape':
if (isOpen) {
// If input.type === 'search', the browser clears the input
// when Escape is pressed. We want to disable this default
// behaviour so that, when suggestions are shown, we just hide
// them, without clearing the input.
event.preventDefault();
}
if (valueBeforeUpDown === null) {
// Didn't interact with Up/Down
if (!isOpen) {
_this2.maybeCallOnChange(event, '', 'escape');
_this2.maybeCallOnSuggestionsUpdateRequested({ value: '', reason: 'escape' });
}
} else {
// Interacted with Up/Down
_this2.maybeCallOnChange(event, valueBeforeUpDown, 'escape');
}
closeSuggestions('escape');
break;
}
_onKeyDown && _onKeyDown(event);
}
});
var onMouseEnter = function onMouseEnter(event, _ref) {
var sectionIndex = _ref.sectionIndex;
var itemIndex = _ref.itemIndex;
updateFocusedSuggestion(sectionIndex, itemIndex);
};
var onMouseLeave = function onMouseLeave() {
updateFocusedSuggestion(null, null);
};
var onMouseDown = function onMouseDown() {
_this2.justClickedOnSuggestion = true;
};
var onClick = function onClick(event) {
var _getSuggestionIndices = _this2.getSuggestionIndices(_this2.findSuggestionElement(event.target));
var sectionIndex = _getSuggestionIndices.sectionIndex;
var suggestionIndex = _getSuggestionIndices.suggestionIndex;
var clickedSuggestion = _this2.getSuggestion(sectionIndex, suggestionIndex);
var clickedSuggestionValue = _this2.props.getSuggestionValue(clickedSuggestion);
_this2.maybeCallOnChange(event, clickedSuggestionValue, 'click');
onSuggestionSelected(event, {
suggestion: clickedSuggestion,
suggestionValue: clickedSuggestionValue,
sectionIndex: sectionIndex,
method: 'click'
});
closeSuggestions('click');
if (focusInputOnSuggestionClick === true) {
_this2.input.focus();
} else {
inputBlurred();
_onBlur && _onBlur(_this2.onBlurEvent);
}
_this2.maybeCallOnSuggestionsUpdateRequested({ value: clickedSuggestionValue, reason: 'click' });
setTimeout(function () {
_this2.justClickedOnSuggestion = false;
});
};
var itemProps = function itemProps(_ref2) {
var sectionIndex = _ref2.sectionIndex;
var itemIndex = _ref2.itemIndex;
return {
'data-section-index': sectionIndex,
'data-suggestion-index': itemIndex,
onMouseEnter: onMouseEnter,
onMouseLeave: onMouseLeave,
onMouseDown: onMouseDown,
onTouchStart: onMouseDown, // Because on iOS `onMouseDown` is not triggered
onClick: onClick
};
};
var renderItem = function renderItem(item) {
return renderSuggestion(item, { value: value, valueBeforeUpDown: valueBeforeUpDown });
};
// console.log('finalTheme: ', finalTheme)
console.log('styles: ', _TypeAheadStyles2.default);
return _react2.default.createElement(_AutoWhatever2.default, {
multiSection: multiSection,
items: items,
renderItem: renderItem,
renderSectionTitle: renderSectionTitle,
getSectionItems: getSectionSuggestions,
focusedSectionIndex: focusedSectionIndex,
focusedItemIndex: focusedSuggestionIndex,
inputProps: autowhateverInputProps,
itemProps: itemProps,
theme: _TypeAheadStyles2.default,
id: id,
ref: this.saveInput
});
}
}]);
return TypeAhead;
}(_react.Component);
TypeAhead.propTypes = {
suggestions: _react.PropTypes.array.isRequired,
onSuggestionsUpdateRequested: _react.PropTypes.func.isRequired,
getSuggestionValue: _react.PropTypes.func.isRequired,
renderSuggestion: _react.PropTypes.func.isRequired,
inputProps: _react.PropTypes.object.isRequired,
shouldRenderSuggestions: _react.PropTypes.func.isRequired,
onSuggestionSelected: _react.PropTypes.func.isRequired,
multiSection: _react.PropTypes.bool.isRequired,
renderSectionTitle: _react.PropTypes.func.isRequired,
getSectionSuggestions: _react.PropTypes.func.isRequired,
focusInputOnSuggestionClick: _react.PropTypes.bool.isRequired,
theme: _react.PropTypes.object.isRequired,
id: _react.PropTypes.string.isRequired,
inputRef: _react.PropTypes.func.isRequired,
isFocused: _react.PropTypes.bool.isRequired,
isCollapsed: _react.PropTypes.bool.isRequired,
focusedSectionIndex: _react.PropTypes.number,
focusedSuggestionIndex: _react.PropTypes.number,
valueBeforeUpDown: _react.PropTypes.string,
lastAction: _react.PropTypes.string,
inputFocused: _react.PropTypes.func.isRequired,
inputBlurred: _react.PropTypes.func.isRequired,
inputChanged: _react.PropTypes.func.isRequired,
updateFocusedSuggestion: _react.PropTypes.func.isRequired,
revealSuggestions: _react.PropTypes.func.isRequired,
closeSuggestions: _react.PropTypes.func.isRequired
};
exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)(TypeAhead);
// The MIT License (MIT)
// Copyright © 2016 Misha Moroshko
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the “Software”), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.