wix-style-react
Version:
wix-style-react
447 lines (388 loc) • 14.9 kB
JavaScript
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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
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; }; }();
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; }
import React from 'react';
import PropTypes from 'prop-types';
import WixComponent from '../BaseComponents/WixComponent';
import Input from '../Input';
import omit from 'omit';
import DropdownLayout, { DIVIDER_OPTION_VALUE } from '../DropdownLayout/DropdownLayout';
import Highlighter from '../Highlighter/Highlighter';
import { chainEventHandlers } from '../utils/ChainEventHandlers';
export var DEFAULT_VALUE_PARSER = function DEFAULT_VALUE_PARSER(option) {
return option.value;
};
var InputWithOptions = function (_WixComponent) {
_inherits(InputWithOptions, _WixComponent);
_createClass(InputWithOptions, [{
key: 'inputClasses',
// Abstraction
value: function inputClasses() {}
}, {
key: 'dropdownClasses',
value: function dropdownClasses() {}
}, {
key: 'dropdownAdditionalProps',
value: function dropdownAdditionalProps() {}
}, {
key: 'inputAdditionalProps',
value: function inputAdditionalProps() {}
/**
* An array of key codes that act as manual submit. Will be used within
* onKeyDown(event).
*
* @returns {KeyboardEvent.key[]}
*/
}, {
key: 'getManualSubmitKeys',
value: function getManualSubmitKeys() {
return ['Enter', 'Tab'];
}
}]);
function InputWithOptions(props) {
_classCallCheck(this, InputWithOptions);
var _this = _possibleConstructorReturn(this, (InputWithOptions.__proto__ || Object.getPrototypeOf(InputWithOptions)).call(this, props));
_this.state = {
inputValue: props.value || '',
showOptions: false,
lastOptionsShow: 0,
isEditing: false
};
_this._onSelect = _this._onSelect.bind(_this);
_this._onFocus = _this._onFocus.bind(_this);
_this._onBlur = _this._onBlur.bind(_this);
_this._onChange = _this._onChange.bind(_this);
_this._onKeyDown = _this._onKeyDown.bind(_this);
_this.focus = _this.focus.bind(_this);
_this.blur = _this.blur.bind(_this);
_this.select = _this.select.bind(_this);
_this.hideOptions = _this.hideOptions.bind(_this);
_this.showOptions = _this.showOptions.bind(_this);
_this._onManuallyInput = _this._onManuallyInput.bind(_this);
_this._renderDropdownLayout = _this._renderDropdownLayout.bind(_this);
_this._onInputClicked = _this._onInputClicked.bind(_this);
_this.closeOnSelect = _this.closeOnSelect.bind(_this);
_this.onCompositionChange = _this.onCompositionChange.bind(_this);
return _this;
}
_createClass(InputWithOptions, [{
key: 'componentDidUpdate',
value: function componentDidUpdate(prevProps, prevState) {
_get(InputWithOptions.prototype.__proto__ || Object.getPrototypeOf(InputWithOptions.prototype), 'componentDidUpdate', this).call(this, prevProps);
if (!this.props.showOptionsIfEmptyInput && (!prevProps.value && this.props.value || !prevState.inputValue && this.state.inputValue)) {
this.showOptions();
}
}
}, {
key: 'onCompositionChange',
value: function onCompositionChange(isComposing) {
this.setState({ isComposing: isComposing });
}
}, {
key: 'onClickOutside',
value: function onClickOutside() {
this.hideOptions();
}
}, {
key: 'renderInput',
value: function renderInput() {
var _this2 = this;
var inputAdditionalProps = this.inputAdditionalProps();
var inputProps = Object.assign(omit(Object.keys(DropdownLayout.propTypes).concat(['onChange', 'dataHook']), this.props), inputAdditionalProps);
var inputElement = inputProps.inputElement;
return React.cloneElement(inputElement, _extends({
menuArrow: true,
ref: function ref(input) {
return _this2.input = input;
}
}, inputProps, {
onKeyDown: chainEventHandlers(inputAdditionalProps && inputAdditionalProps.onKeyDown, this._onKeyDown),
theme: this.props.theme,
onChange: this._onChange,
onInputClicked: this._onInputClicked,
onFocus: this._onFocus,
onBlur: this._onBlur,
onCompositionChange: this.onCompositionChange,
width: inputElement.props.width,
textOverflow: inputElement.props.textOverflow
}));
}
}, {
key: '_processOptions',
value: function _processOptions(options) {
var _this3 = this;
return !this.props.highlight ? options : options.map(function (option) {
return _extends({}, option, {
value: option.value === DIVIDER_OPTION_VALUE ? option.value : React.createElement(
Highlighter,
{
match: _this3.state.inputValue,
dataHook: 'highlighter-' + option.id
},
option.value
)
});
});
}
}, {
key: '_renderDropdownLayout',
value: function _renderDropdownLayout() {
var _this4 = this;
var inputOnlyProps = omit(['tabIndex'], Input.propTypes);
var dropdownProps = Object.assign(omit(Object.keys(inputOnlyProps).concat(['dataHook']), this.props), this.dropdownAdditionalProps());
var customStyle = { marginLeft: this.props.dropdownOffsetLeft };
if (this.props.dropdownWidth) {
customStyle.width = this.props.dropdownWidth;
}
var isDropdownLayoutVisible = this.state.showOptions && (this.props.showOptionsIfEmptyInput || this.state.inputValue.length > 0);
return React.createElement(
'div',
{
className: this.dropdownClasses(),
style: customStyle,
'data-hook': 'dropdown-layout-wrapper'
},
React.createElement(DropdownLayout, _extends({
ref: function ref(dropdownLayout) {
return _this4.dropdownLayout = dropdownLayout;
}
}, dropdownProps, {
options: this._processOptions(dropdownProps.options),
theme: this.props.theme,
visible: isDropdownLayoutVisible,
onClose: this.hideOptions,
onSelect: this._onSelect,
isComposing: this.state.isComposing
}))
);
}
}, {
key: 'render',
value: function render() {
var dropDirectionUp = this.props.dropDirectionUp;
return React.createElement(
'div',
null,
dropDirectionUp ? this._renderDropdownLayout() : null,
React.createElement(
'div',
{ 'data-input-parent': true, className: this.inputClasses() },
this.renderInput()
),
!dropDirectionUp ? this._renderDropdownLayout() : null
);
}
}, {
key: 'hideOptions',
value: function hideOptions() {
if (this.state.showOptions) {
this.setState({ showOptions: false });
}
}
}, {
key: 'showOptions',
value: function showOptions() {
this.setState({ showOptions: true, lastOptionsShow: Date.now() });
}
}, {
key: 'closeOnSelect',
value: function closeOnSelect() {
return this.props.closeOnSelect;
}
}, {
key: 'shouldOpenDropdown',
/**
* Determine if the provided key should cause the dropdown to be opened.
*
* @param {KeyboardEvent.key}
* @returns {boolean}
*/
value: function shouldOpenDropdown(key) {
var openKeys = this.isReadOnly ? ['Enter', 'Spacebar', ' ', 'ArrowDown'] : ['ArrowDown'];
return openKeys.includes(key);
}
/**
* Determine if the provided key should delegate the keydown event to the
* DropdownLayout.
*
* @param {KeyboardEvent.key}
* @returns {boolean}
*/
}, {
key: 'shouldDelegateKeyDown',
value: function shouldDelegateKeyDown(key) {
return this.isReadOnly || !['Spacebar', ' '].includes(key);
}
/**
* Determine if the provided key should cause manual submit.
*
* @param {KeyboardEvent.key}
* @returns {boolean}
*/
}, {
key: 'shouldPerformManualSubmit',
value: function shouldPerformManualSubmit(key) {
return this.getManualSubmitKeys().includes(key);
}
}, {
key: '_onManuallyInput',
value: function _onManuallyInput() {
var inputValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
if (this.state.isComposing) {
return;
}
inputValue = inputValue.trim();
if (this.closeOnSelect()) {
this.hideOptions();
}
var suggestedOption = this.props.options.find(function (element) {
return element.value === inputValue;
});
if (this.props.onManuallyInput) {
this.props.onManuallyInput(inputValue, suggestedOption);
}
}
}, {
key: '_onSelect',
value: function _onSelect(option, isSelectedOption) {
this.showOptions();
var onSelect = this.props.onSelect;
if (this.closeOnSelect()) {
this.setState({ showOptions: false });
}
if (isSelectedOption) {
this.setState({ showOptions: false });
}
if (onSelect) {
onSelect(this.props.highlight ? this.props.options.find(function (opt) {
return opt.id === option.id;
}) : option);
}
}
}, {
key: '_onChange',
value: function _onChange(event) {
this.setState({ inputValue: event.target.value });
if (this.props.onChange) {
this.props.onChange(event);
}
// If the input value is not empty, should show the options
if (event.target.value.trim()) {
this.showOptions();
}
}
}, {
key: '_onInputClicked',
value: function _onInputClicked(event) {
if (this.state.showOptions) {
if (Date.now() - this.state.lastOptionsShow > 2000) {
this.hideOptions();
}
} else {
this.showOptions();
}
if (this.props.onInputClicked) {
this.props.onInputClicked(event);
}
}
}, {
key: '_onFocus',
value: function _onFocus(e) {
if (this.props.disabled) {
return;
}
this._focused = true;
this.setState({ isEditing: false });
if (this.props.onFocus) {
this.props.onFocus(e);
}
}
}, {
key: '_onBlur',
value: function _onBlur(e) {
this._focused = false;
if (this.props.onBlur) {
this.props.onBlur(e);
}
}
}, {
key: '_onKeyDown',
value: function _onKeyDown(event) {
if (this.props.disabled) {
return;
}
var key = event.key;
if (key !== 'ArrowDown' && key !== 'ArrowUp') {
this.setState({ isEditing: true });
}
if (this.shouldOpenDropdown(key)) {
this.showOptions();
event.preventDefault();
}
if (this.shouldDelegateKeyDown(key)) {
// Delegate event and get result
var eventWasHandled = this.dropdownLayout._onKeyDown(event);
if (eventWasHandled || this.isReadOnly) {
return;
}
// For editing mode, we want to *submit* only for specific keys.
if (this.shouldPerformManualSubmit(key)) {
this._onManuallyInput(this.state.inputValue);
}
}
}
}, {
key: 'focus',
value: function focus() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.input.focus(options);
}
}, {
key: 'blur',
value: function blur() {
this.input.blur();
}
}, {
key: 'select',
value: function select() {
this.input.select();
}
}, {
key: 'isReadOnly',
get: function get() {
var _ref = this.inputAdditionalProps() || {},
readOnly = _ref.readOnly;
return readOnly;
}
}]);
return InputWithOptions;
}(WixComponent);
InputWithOptions.defaultProps = _extends({}, Input.defaultProps, DropdownLayout.defaultProps, {
onSelect: function onSelect() {},
options: [],
closeOnSelect: true,
inputElement: React.createElement(Input, null),
valueParser: DEFAULT_VALUE_PARSER,
dropdownWidth: null,
dropdownOffsetLeft: '0',
showOptionsIfEmptyInput: true,
autocomplete: 'off'
});
InputWithOptions.propTypes = _extends({}, Input.propTypes, DropdownLayout.propTypes, {
autocomplete: PropTypes.string,
inputElement: PropTypes.element,
closeOnSelect: PropTypes.bool,
onManuallyInput: PropTypes.func,
/** Function that receives an option, and should return the value to be displayed. By default returns `option.value`. */
valueParser: PropTypes.func,
dropdownWidth: PropTypes.string,
dropdownOffsetLeft: PropTypes.string,
/** Controls whether to show options if input is empty */
showOptionsIfEmptyInput: PropTypes.bool,
highlight: PropTypes.bool
});
InputWithOptions.displayName = 'InputWithOptions';
export default InputWithOptions;