react-bootstrap-typeahead-portal
Version:
React typeahead with Bootstrap styling
148 lines (125 loc) • 5.51 kB
JavaScript
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
var _excluded = ["className"];
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
import invariant from 'invariant';
import React, { cloneElement, useEffect, useRef } from 'react';
import { useTypeaheadContext } from '../core/Context';
import { isSelectable } from '../utils';
import { RETURN, RIGHT, TAB } from '../constants';
// IE doesn't seem to get the composite computed value (eg: 'padding',
// 'borderStyle', etc.), so generate these from the individual values.
function interpolateStyle(styles, attr) {
var subattr = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
// Title-case the sub-attribute.
if (subattr) {
/* eslint-disable-next-line no-param-reassign */
subattr = subattr.replace(subattr[0], subattr[0].toUpperCase());
}
return ['Top', 'Right', 'Bottom', 'Left'].map(function (dir) {
return styles[attr + dir + subattr];
}).join(' ');
}
function copyStyles(inputNode, hintNode) {
if (!inputNode || !hintNode) {
return;
}
var inputStyle = window.getComputedStyle(inputNode);
/* eslint-disable no-param-reassign */
hintNode.style.borderStyle = interpolateStyle(inputStyle, 'border', 'style');
hintNode.style.borderWidth = interpolateStyle(inputStyle, 'border', 'width');
hintNode.style.fontSize = inputStyle.fontSize;
hintNode.style.height = inputStyle.height;
hintNode.style.lineHeight = inputStyle.lineHeight;
hintNode.style.margin = interpolateStyle(inputStyle, 'margin');
hintNode.style.padding = interpolateStyle(inputStyle, 'padding');
/* eslint-enable no-param-reassign */
}
export function defaultShouldSelect(e, state) {
var shouldSelectHint = false;
var currentTarget = e.currentTarget,
keyCode = e.keyCode;
if (keyCode === RIGHT) {
// For selectable input types ("text", "search"), only select the hint if
// it's at the end of the input value. For non-selectable types ("email",
// "number"), always select the hint.
shouldSelectHint = isSelectable(currentTarget) ? currentTarget.selectionStart === currentTarget.value.length : true;
}
if (keyCode === TAB) {
// Prevent input from blurring on TAB.
e.preventDefault();
shouldSelectHint = true;
}
if (keyCode === RETURN) {
shouldSelectHint = !!state.selectHintOnEnter;
}
return typeof state.shouldSelect === 'function' ? state.shouldSelect(shouldSelectHint, e) : shouldSelectHint;
}
export var useHint = function useHint(_ref) {
var children = _ref.children,
shouldSelect = _ref.shouldSelect;
!(React.Children.count(children) === 1) ? process.env.NODE_ENV !== "production" ? invariant(false, '`useHint` expects one child.') : invariant(false) : void 0;
var _useTypeaheadContext = useTypeaheadContext(),
hintText = _useTypeaheadContext.hintText,
initialItem = _useTypeaheadContext.initialItem,
inputNode = _useTypeaheadContext.inputNode,
onAdd = _useTypeaheadContext.onAdd,
selectHintOnEnter = _useTypeaheadContext.selectHintOnEnter;
var hintRef = useRef(null);
var onKeyDown = function onKeyDown(e) {
if (hintText && initialItem && defaultShouldSelect(e, {
selectHintOnEnter: selectHintOnEnter,
shouldSelect: shouldSelect
})) {
onAdd(initialItem);
}
children.props.onKeyDown && children.props.onKeyDown(e);
};
useEffect(function () {
copyStyles(inputNode, hintRef.current);
});
return {
child: /*#__PURE__*/cloneElement(children, _objectSpread(_objectSpread({}, children.props), {}, {
onKeyDown: onKeyDown
})),
hintRef: hintRef,
hintText: hintText
};
};
var Hint = function Hint(_ref2) {
var className = _ref2.className,
props = _objectWithoutProperties(_ref2, _excluded);
var _useHint = useHint(props),
child = _useHint.child,
hintRef = _useHint.hintRef,
hintText = _useHint.hintText;
return /*#__PURE__*/React.createElement("div", {
className: className,
style: {
display: 'flex',
flex: 1,
height: '100%',
position: 'relative'
}
}, child, /*#__PURE__*/React.createElement("input", {
"aria-hidden": true,
className: "rbt-input-hint",
ref: hintRef,
readOnly: true,
style: {
backgroundColor: 'transparent',
borderColor: 'transparent',
boxShadow: 'none',
color: 'rgba(0, 0, 0, 0.54)',
left: 0,
pointerEvents: 'none',
position: 'absolute',
top: 0,
width: '100%'
},
tabIndex: -1,
value: hintText
}));
};
export default Hint;