No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

707 líneas
21 KiB

  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. var _extends2 = require('babel-runtime/helpers/extends');
  6. var _extends3 = _interopRequireDefault(_extends2);
  7. var _typeof2 = require('babel-runtime/helpers/typeof');
  8. var _typeof3 = _interopRequireDefault(_typeof2);
  9. var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');
  10. var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2);
  11. var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
  12. var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
  13. var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
  14. var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
  15. var _createClass2 = require('babel-runtime/helpers/createClass');
  16. var _createClass3 = _interopRequireDefault(_createClass2);
  17. var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
  18. var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
  19. var _inherits2 = require('babel-runtime/helpers/inherits');
  20. var _inherits3 = _interopRequireDefault(_inherits2);
  21. var _simpleAssign = require('simple-assign');
  22. var _simpleAssign2 = _interopRequireDefault(_simpleAssign);
  23. var _react = require('react');
  24. var _react2 = _interopRequireDefault(_react);
  25. var _propTypes = require('prop-types');
  26. var _propTypes2 = _interopRequireDefault(_propTypes);
  27. var _reactDom = require('react-dom');
  28. var _reactDom2 = _interopRequireDefault(_reactDom);
  29. var _keycode = require('keycode');
  30. var _keycode2 = _interopRequireDefault(_keycode);
  31. var _TextField = require('../TextField');
  32. var _TextField2 = _interopRequireDefault(_TextField);
  33. var _Menu = require('../Menu');
  34. var _Menu2 = _interopRequireDefault(_Menu);
  35. var _MenuItem = require('../MenuItem');
  36. var _MenuItem2 = _interopRequireDefault(_MenuItem);
  37. var _Divider = require('../Divider');
  38. var _Divider2 = _interopRequireDefault(_Divider);
  39. var _Popover = require('../Popover/Popover');
  40. var _Popover2 = _interopRequireDefault(_Popover);
  41. var _propTypes3 = require('../utils/propTypes');
  42. var _propTypes4 = _interopRequireDefault(_propTypes3);
  43. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  44. function getStyles(props, context, state) {
  45. var anchorEl = state.anchorEl;
  46. var fullWidth = props.fullWidth;
  47. var styles = {
  48. root: {
  49. display: 'inline-block',
  50. position: 'relative',
  51. width: fullWidth ? '100%' : 256
  52. },
  53. menu: {
  54. width: '100%'
  55. },
  56. list: {
  57. display: 'block',
  58. width: fullWidth ? '100%' : 256
  59. },
  60. innerDiv: {
  61. overflow: 'hidden'
  62. }
  63. };
  64. if (anchorEl && fullWidth) {
  65. styles.popover = {
  66. width: anchorEl.clientWidth
  67. };
  68. }
  69. return styles;
  70. }
  71. var AutoComplete = function (_Component) {
  72. (0, _inherits3.default)(AutoComplete, _Component);
  73. function AutoComplete() {
  74. var _ref;
  75. var _temp, _this, _ret;
  76. (0, _classCallCheck3.default)(this, AutoComplete);
  77. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  78. args[_key] = arguments[_key];
  79. }
  80. return _ret = (_temp = (_this = (0, _possibleConstructorReturn3.default)(this, (_ref = AutoComplete.__proto__ || (0, _getPrototypeOf2.default)(AutoComplete)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
  81. anchorEl: null,
  82. focusTextField: true,
  83. open: false,
  84. searchText: undefined
  85. }, _this.handleRequestClose = function () {
  86. // Only take into account the Popover clickAway when we are
  87. // not focusing the TextField.
  88. if (!_this.state.focusTextField) {
  89. _this.close();
  90. }
  91. }, _this.handleMouseDown = function (event) {
  92. // Keep the TextField focused
  93. event.preventDefault();
  94. }, _this.handleItemClick = function (event, child) {
  95. var dataSource = _this.props.dataSource;
  96. var index = parseInt(child.key, 10);
  97. var chosenRequest = dataSource[index];
  98. var searchText = _this.chosenRequestText(chosenRequest);
  99. var updateInput = function updateInput() {
  100. return _this.props.onUpdateInput(searchText, _this.props.dataSource, {
  101. source: 'click'
  102. });
  103. };
  104. _this.timerClickCloseId = function () {
  105. return setTimeout(function () {
  106. _this.timerClickCloseId = null;
  107. _this.close();
  108. _this.props.onNewRequest(chosenRequest, index);
  109. }, _this.props.menuCloseDelay);
  110. };
  111. if (typeof _this.props.searchText !== 'undefined') {
  112. updateInput();
  113. _this.timerClickCloseId();
  114. } else {
  115. _this.setState({
  116. searchText: searchText
  117. }, function () {
  118. updateInput();
  119. _this.timerClickCloseId();
  120. });
  121. }
  122. }, _this.chosenRequestText = function (chosenRequest) {
  123. if (typeof chosenRequest === 'string') {
  124. return chosenRequest;
  125. } else {
  126. return chosenRequest[_this.props.dataSourceConfig.text];
  127. }
  128. }, _this.handleEscKeyDown = function () {
  129. _this.close();
  130. }, _this.handleKeyDown = function (event) {
  131. if (_this.props.onKeyDown) _this.props.onKeyDown(event);
  132. switch ((0, _keycode2.default)(event)) {
  133. case 'enter':
  134. _this.close();
  135. var searchText = _this.state.searchText;
  136. if (searchText !== '') {
  137. _this.props.onNewRequest(searchText, -1);
  138. }
  139. break;
  140. case 'esc':
  141. _this.close();
  142. break;
  143. case 'down':
  144. event.preventDefault();
  145. _this.setState({
  146. open: true,
  147. focusTextField: false,
  148. anchorEl: _reactDom2.default.findDOMNode(_this.refs.searchTextField)
  149. });
  150. break;
  151. default:
  152. break;
  153. }
  154. }, _this.handleChange = function (event) {
  155. var searchText = event.target.value;
  156. // Make sure that we have a new searchText.
  157. // Fix an issue with a Cordova Webview
  158. if (searchText === _this.state.searchText) {
  159. return;
  160. }
  161. var state = {
  162. open: true,
  163. anchorEl: _reactDom2.default.findDOMNode(_this.refs.searchTextField)
  164. };
  165. if (_this.props.searchText === undefined) {
  166. state.searchText = searchText;
  167. }
  168. _this.setState(state);
  169. _this.props.onUpdateInput(searchText, _this.props.dataSource, {
  170. source: 'change'
  171. });
  172. }, _this.handleBlur = function (event) {
  173. if (_this.state.focusTextField && _this.timerClickCloseId === null) {
  174. _this.timerBlurClose = setTimeout(function () {
  175. _this.close();
  176. }, 0);
  177. }
  178. if (_this.props.onBlur) {
  179. _this.props.onBlur(event);
  180. }
  181. }, _this.handleFocus = function (event) {
  182. if (!_this.state.open && _this.props.openOnFocus) {
  183. _this.setState({
  184. open: true,
  185. anchorEl: _reactDom2.default.findDOMNode(_this.refs.searchTextField)
  186. });
  187. }
  188. _this.setState({
  189. focusTextField: true
  190. });
  191. if (_this.props.onFocus) {
  192. _this.props.onFocus(event);
  193. }
  194. }, _temp), (0, _possibleConstructorReturn3.default)(_this, _ret);
  195. }
  196. (0, _createClass3.default)(AutoComplete, [{
  197. key: 'componentWillMount',
  198. value: function componentWillMount() {
  199. this.requestsList = [];
  200. this.setState({
  201. open: this.props.open,
  202. searchText: this.props.searchText || ''
  203. });
  204. this.timerClickCloseId = null;
  205. }
  206. }, {
  207. key: 'componentWillReceiveProps',
  208. value: function componentWillReceiveProps(nextProps) {
  209. if (this.props.searchText !== nextProps.searchText) {
  210. this.setState({
  211. searchText: nextProps.searchText
  212. });
  213. }
  214. if (this.props.open !== nextProps.open) {
  215. this.setState({
  216. open: nextProps.open,
  217. anchorEl: _reactDom2.default.findDOMNode(this.refs.searchTextField)
  218. });
  219. }
  220. }
  221. }, {
  222. key: 'componentWillUnmount',
  223. value: function componentWillUnmount() {
  224. clearTimeout(this.timerClickCloseId);
  225. clearTimeout(this.timerBlurClose);
  226. }
  227. }, {
  228. key: 'close',
  229. value: function close() {
  230. this.setState({
  231. open: false,
  232. anchorEl: null
  233. });
  234. if (this.props.onClose) {
  235. this.props.onClose();
  236. }
  237. }
  238. }, {
  239. key: 'blur',
  240. value: function blur() {
  241. this.refs.searchTextField.blur();
  242. }
  243. }, {
  244. key: 'focus',
  245. value: function focus() {
  246. this.refs.searchTextField.focus();
  247. }
  248. }, {
  249. key: 'render',
  250. value: function render() {
  251. var _this2 = this;
  252. var _props = this.props,
  253. anchorOrigin = _props.anchorOrigin,
  254. animated = _props.animated,
  255. animation = _props.animation,
  256. dataSource = _props.dataSource,
  257. dataSourceConfig = _props.dataSourceConfig,
  258. disableFocusRipple = _props.disableFocusRipple,
  259. errorStyle = _props.errorStyle,
  260. floatingLabelText = _props.floatingLabelText,
  261. filter = _props.filter,
  262. fullWidth = _props.fullWidth,
  263. style = _props.style,
  264. hintText = _props.hintText,
  265. maxSearchResults = _props.maxSearchResults,
  266. menuCloseDelay = _props.menuCloseDelay,
  267. textFieldStyle = _props.textFieldStyle,
  268. menuStyle = _props.menuStyle,
  269. menuProps = _props.menuProps,
  270. listStyle = _props.listStyle,
  271. targetOrigin = _props.targetOrigin,
  272. onBlur = _props.onBlur,
  273. onClose = _props.onClose,
  274. onFocus = _props.onFocus,
  275. onKeyDown = _props.onKeyDown,
  276. onNewRequest = _props.onNewRequest,
  277. onUpdateInput = _props.onUpdateInput,
  278. openOnFocus = _props.openOnFocus,
  279. popoverProps = _props.popoverProps,
  280. searchTextProp = _props.searchText,
  281. other = (0, _objectWithoutProperties3.default)(_props, ['anchorOrigin', 'animated', 'animation', 'dataSource', 'dataSourceConfig', 'disableFocusRipple', 'errorStyle', 'floatingLabelText', 'filter', 'fullWidth', 'style', 'hintText', 'maxSearchResults', 'menuCloseDelay', 'textFieldStyle', 'menuStyle', 'menuProps', 'listStyle', 'targetOrigin', 'onBlur', 'onClose', 'onFocus', 'onKeyDown', 'onNewRequest', 'onUpdateInput', 'openOnFocus', 'popoverProps', 'searchText']);
  282. var _ref2 = popoverProps || {},
  283. popoverStyle = _ref2.style,
  284. popoverOther = (0, _objectWithoutProperties3.default)(_ref2, ['style']);
  285. var _state = this.state,
  286. open = _state.open,
  287. anchorEl = _state.anchorEl,
  288. searchText = _state.searchText,
  289. focusTextField = _state.focusTextField;
  290. var prepareStyles = this.context.muiTheme.prepareStyles;
  291. var styles = getStyles(this.props, this.context, this.state);
  292. var requestsList = [];
  293. dataSource.every(function (item, index) {
  294. switch (typeof item === 'undefined' ? 'undefined' : (0, _typeof3.default)(item)) {
  295. case 'string':
  296. if (filter(searchText, item, item)) {
  297. requestsList.push({
  298. text: item,
  299. value: _react2.default.createElement(_MenuItem2.default, {
  300. innerDivStyle: styles.innerDiv,
  301. value: item,
  302. primaryText: item,
  303. disableFocusRipple: disableFocusRipple,
  304. key: index
  305. })
  306. });
  307. }
  308. break;
  309. case 'object':
  310. if (item && typeof item[_this2.props.dataSourceConfig.text] === 'string') {
  311. var itemText = item[_this2.props.dataSourceConfig.text];
  312. if (!_this2.props.filter(searchText, itemText, item)) break;
  313. var itemValue = item[_this2.props.dataSourceConfig.value];
  314. if (itemValue && itemValue.type && (itemValue.type.muiName === _MenuItem2.default.muiName || itemValue.type.muiName === _Divider2.default.muiName)) {
  315. requestsList.push({
  316. text: itemText,
  317. value: _react2.default.cloneElement(itemValue, {
  318. key: index,
  319. disableFocusRipple: disableFocusRipple
  320. })
  321. });
  322. } else {
  323. requestsList.push({
  324. text: itemText,
  325. value: _react2.default.createElement(_MenuItem2.default, {
  326. innerDivStyle: styles.innerDiv,
  327. primaryText: itemText,
  328. disableFocusRipple: disableFocusRipple,
  329. key: index
  330. })
  331. });
  332. }
  333. }
  334. break;
  335. default:
  336. // Do nothing
  337. }
  338. return !(maxSearchResults && maxSearchResults > 0 && requestsList.length === maxSearchResults);
  339. });
  340. this.requestsList = requestsList;
  341. var menu = open && requestsList.length > 0 && _react2.default.createElement(
  342. _Menu2.default,
  343. (0, _extends3.default)({
  344. ref: 'menu',
  345. autoWidth: false,
  346. disableAutoFocus: focusTextField,
  347. onEscKeyDown: this.handleEscKeyDown,
  348. initiallyKeyboardFocused: true,
  349. onItemClick: this.handleItemClick,
  350. onMouseDown: this.handleMouseDown,
  351. style: (0, _simpleAssign2.default)(styles.menu, menuStyle),
  352. listStyle: (0, _simpleAssign2.default)(styles.list, listStyle)
  353. }, menuProps),
  354. requestsList.map(function (i) {
  355. return i.value;
  356. })
  357. );
  358. return _react2.default.createElement(
  359. 'div',
  360. { style: prepareStyles((0, _simpleAssign2.default)(styles.root, style)) },
  361. _react2.default.createElement(_TextField2.default, (0, _extends3.default)({
  362. ref: 'searchTextField',
  363. autoComplete: 'off',
  364. onBlur: this.handleBlur,
  365. onFocus: this.handleFocus,
  366. onKeyDown: this.handleKeyDown,
  367. floatingLabelText: floatingLabelText,
  368. hintText: hintText,
  369. fullWidth: fullWidth,
  370. multiLine: false,
  371. errorStyle: errorStyle,
  372. style: textFieldStyle
  373. }, other, {
  374. // value and onChange are idiomatic properties often leaked.
  375. // We prevent their overrides in order to reduce potential bugs.
  376. value: searchText,
  377. onChange: this.handleChange
  378. })),
  379. _react2.default.createElement(
  380. _Popover2.default,
  381. (0, _extends3.default)({
  382. style: (0, _simpleAssign2.default)({}, styles.popover, popoverStyle),
  383. canAutoPosition: false,
  384. anchorOrigin: anchorOrigin,
  385. targetOrigin: targetOrigin,
  386. open: open,
  387. anchorEl: anchorEl,
  388. useLayerForClickAway: false,
  389. onRequestClose: this.handleRequestClose,
  390. animated: animated,
  391. animation: animation
  392. }, popoverOther),
  393. menu
  394. )
  395. );
  396. }
  397. }]);
  398. return AutoComplete;
  399. }(_react.Component);
  400. AutoComplete.defaultProps = {
  401. anchorOrigin: {
  402. vertical: 'bottom',
  403. horizontal: 'left'
  404. },
  405. animated: true,
  406. dataSourceConfig: {
  407. text: 'text',
  408. value: 'value'
  409. },
  410. disableFocusRipple: true,
  411. filter: function filter(searchText, key) {
  412. return searchText !== '' && key.indexOf(searchText) !== -1;
  413. },
  414. fullWidth: false,
  415. open: false,
  416. openOnFocus: false,
  417. onUpdateInput: function onUpdateInput() {},
  418. onNewRequest: function onNewRequest() {},
  419. menuCloseDelay: 300,
  420. targetOrigin: {
  421. vertical: 'top',
  422. horizontal: 'left'
  423. }
  424. };
  425. AutoComplete.contextTypes = {
  426. muiTheme: _propTypes2.default.object.isRequired
  427. };
  428. AutoComplete.propTypes = process.env.NODE_ENV !== "production" ? {
  429. /**
  430. * Location of the anchor for the auto complete.
  431. */
  432. anchorOrigin: _propTypes4.default.origin,
  433. /**
  434. * If true, the auto complete is animated as it is toggled.
  435. */
  436. animated: _propTypes2.default.bool,
  437. /**
  438. * Override the default animation component used.
  439. */
  440. animation: _propTypes2.default.func,
  441. /**
  442. * Array of strings or nodes used to populate the list.
  443. */
  444. dataSource: _propTypes2.default.array.isRequired,
  445. /**
  446. * Config for objects list dataSource.
  447. *
  448. * @typedef {Object} dataSourceConfig
  449. *
  450. * @property {string} text `dataSource` element key used to find a string to be matched for search
  451. * and shown as a `TextField` input value after choosing the result.
  452. * @property {string} value `dataSource` element key used to find a string to be shown in search results.
  453. */
  454. dataSourceConfig: _propTypes2.default.object,
  455. /**
  456. * Disables focus ripple when true.
  457. */
  458. disableFocusRipple: _propTypes2.default.bool,
  459. /**
  460. * Override style prop for error.
  461. */
  462. errorStyle: _propTypes2.default.object,
  463. /**
  464. * The error content to display.
  465. */
  466. errorText: _propTypes2.default.node,
  467. /**
  468. * Callback function used to filter the auto complete.
  469. *
  470. * @param {string} searchText The text to search for within `dataSource`.
  471. * @param {string} key `dataSource` element, or `text` property on that element if it's not a string.
  472. * @returns {boolean} `true` indicates the auto complete list will include `key` when the input is `searchText`.
  473. */
  474. filter: _propTypes2.default.func,
  475. /**
  476. * The content to use for adding floating label element.
  477. */
  478. floatingLabelText: _propTypes2.default.node,
  479. /**
  480. * If true, the field receives the property `width: 100%`.
  481. */
  482. fullWidth: _propTypes2.default.bool,
  483. /**
  484. * The hint content to display.
  485. */
  486. hintText: _propTypes2.default.node,
  487. /**
  488. * Override style for list.
  489. */
  490. listStyle: _propTypes2.default.object,
  491. /**
  492. * The max number of search results to be shown.
  493. * By default it shows all the items which matches filter.
  494. */
  495. maxSearchResults: _propTypes2.default.number,
  496. /**
  497. * Delay for closing time of the menu.
  498. */
  499. menuCloseDelay: _propTypes2.default.number,
  500. /**
  501. * Props to be passed to menu.
  502. */
  503. menuProps: _propTypes2.default.object,
  504. /**
  505. * Override style for menu.
  506. */
  507. menuStyle: _propTypes2.default.object,
  508. /** @ignore */
  509. onBlur: _propTypes2.default.func,
  510. /**
  511. * Callback function fired when the menu is closed.
  512. */
  513. onClose: _propTypes2.default.func,
  514. /** @ignore */
  515. onFocus: _propTypes2.default.func,
  516. /** @ignore */
  517. onKeyDown: _propTypes2.default.func,
  518. /**
  519. * Callback function that is fired when a list item is selected, or enter is pressed in the `TextField`.
  520. *
  521. * @param {string} chosenRequest Either the `TextField` input value, if enter is pressed in the `TextField`,
  522. * or the dataSource object corresponding to the list item that was selected.
  523. * @param {number} index The index in `dataSource` of the list item selected, or `-1` if enter is pressed in the
  524. * `TextField`.
  525. */
  526. onNewRequest: _propTypes2.default.func,
  527. /**
  528. * Callback function that is fired when the user updates the `TextField`.
  529. *
  530. * @param {string} searchText The auto-complete's `searchText` value.
  531. * @param {array} dataSource The auto-complete's `dataSource` array.
  532. * @param {object} params Additional information linked the update.
  533. */
  534. onUpdateInput: _propTypes2.default.func,
  535. /**
  536. * Auto complete menu is open if true.
  537. */
  538. open: _propTypes2.default.bool,
  539. /**
  540. * If true, the list item is showed when a focus event triggers.
  541. */
  542. openOnFocus: _propTypes2.default.bool,
  543. /**
  544. * Props to be passed to popover.
  545. */
  546. popoverProps: _propTypes2.default.object,
  547. /**
  548. * Text being input to auto complete.
  549. */
  550. searchText: _propTypes2.default.string,
  551. /**
  552. * Override the inline-styles of the root element.
  553. */
  554. style: _propTypes2.default.object,
  555. /**
  556. * Origin for location of target.
  557. */
  558. targetOrigin: _propTypes4.default.origin,
  559. /**
  560. * Override the inline-styles of AutoComplete's TextField element.
  561. */
  562. textFieldStyle: _propTypes2.default.object
  563. } : {};
  564. AutoComplete.levenshteinDistance = function (searchText, key) {
  565. var current = [];
  566. var prev = void 0;
  567. var value = void 0;
  568. for (var i = 0; i <= key.length; i++) {
  569. for (var j = 0; j <= searchText.length; j++) {
  570. if (i && j) {
  571. if (searchText.charAt(j - 1) === key.charAt(i - 1)) value = prev;else value = Math.min(current[j], current[j - 1], prev) + 1;
  572. } else {
  573. value = i + j;
  574. }
  575. prev = current[j];
  576. current[j] = value;
  577. }
  578. }
  579. return current.pop();
  580. };
  581. AutoComplete.noFilter = function () {
  582. return true;
  583. };
  584. AutoComplete.defaultFilter = AutoComplete.caseSensitiveFilter = function (searchText, key) {
  585. return searchText !== '' && key.indexOf(searchText) !== -1;
  586. };
  587. AutoComplete.caseInsensitiveFilter = function (searchText, key) {
  588. return key.toLowerCase().indexOf(searchText.toLowerCase()) !== -1;
  589. };
  590. AutoComplete.levenshteinDistanceFilter = function (distanceLessThan) {
  591. if (distanceLessThan === undefined) {
  592. return AutoComplete.levenshteinDistance;
  593. } else if (typeof distanceLessThan !== 'number') {
  594. throw 'Error: AutoComplete.levenshteinDistanceFilter is a filter generator, not a filter!';
  595. }
  596. return function (s, k) {
  597. return AutoComplete.levenshteinDistance(s, k) < distanceLessThan;
  598. };
  599. };
  600. AutoComplete.fuzzyFilter = function (searchText, key) {
  601. var compareString = key.toLowerCase();
  602. searchText = searchText.toLowerCase();
  603. var searchTextIndex = 0;
  604. for (var index = 0; index < key.length; index++) {
  605. if (compareString[index] === searchText[searchTextIndex]) {
  606. searchTextIndex += 1;
  607. }
  608. }
  609. return searchTextIndex === searchText.length;
  610. };
  611. AutoComplete.Item = _MenuItem2.default;
  612. AutoComplete.Divider = _Divider2.default;
  613. exports.default = AutoComplete;