index.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  3. import { createVNode as _createVNode } from "vue";
  4. /**
  5. * Cursor rule:
  6. * 1. Only `showSearch` enabled
  7. * 2. Only `open` is `true`
  8. * 3. When typing, set `open` to `true` which hit rule of 2
  9. *
  10. * Accessibility:
  11. * - https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
  12. */
  13. import KeyCode from '../../_util/KeyCode';
  14. import MultipleSelector from './MultipleSelector';
  15. import SingleSelector from './SingleSelector';
  16. import { isValidateOpenKey } from '../utils/keyUtil';
  17. import useLock from '../hooks/useLock';
  18. import { defineComponent } from 'vue';
  19. import createRef from '../../_util/createRef';
  20. import PropTypes from '../../_util/vue-types';
  21. var Selector = defineComponent({
  22. name: 'Selector',
  23. inheritAttrs: false,
  24. props: {
  25. id: String,
  26. prefixCls: String,
  27. showSearch: {
  28. type: Boolean,
  29. default: undefined
  30. },
  31. open: {
  32. type: Boolean,
  33. default: undefined
  34. },
  35. /** Display in the Selector value, it's not same as `value` prop */
  36. values: PropTypes.array,
  37. multiple: {
  38. type: Boolean,
  39. default: undefined
  40. },
  41. mode: String,
  42. searchValue: String,
  43. activeValue: String,
  44. inputElement: PropTypes.any,
  45. autofocus: {
  46. type: Boolean,
  47. default: undefined
  48. },
  49. activeDescendantId: String,
  50. tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  51. disabled: {
  52. type: Boolean,
  53. default: undefined
  54. },
  55. placeholder: PropTypes.any,
  56. removeIcon: PropTypes.any,
  57. // Tags
  58. maxTagCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  59. maxTagTextLength: Number,
  60. maxTagPlaceholder: PropTypes.any,
  61. tagRender: Function,
  62. optionLabelRender: Function,
  63. /** Check if `tokenSeparators` contains `\n` or `\r\n` */
  64. tokenWithEnter: {
  65. type: Boolean,
  66. default: undefined
  67. },
  68. // Motion
  69. choiceTransitionName: String,
  70. onToggleOpen: {
  71. type: Function
  72. },
  73. /** `onSearch` returns go next step boolean to check if need do toggle open */
  74. onSearch: Function,
  75. onSearchSubmit: Function,
  76. onRemove: Function,
  77. onInputKeyDown: {
  78. type: Function
  79. },
  80. /**
  81. * @private get real dom for trigger align.
  82. * This may be removed after React provides replacement of `findDOMNode`
  83. */
  84. domRef: Function
  85. },
  86. setup: function setup(props, _ref) {
  87. var expose = _ref.expose;
  88. var inputRef = createRef();
  89. var compositionStatus = false;
  90. // ====================== Input ======================
  91. var _useLock = useLock(0),
  92. _useLock2 = _slicedToArray(_useLock, 2),
  93. getInputMouseDown = _useLock2[0],
  94. setInputMouseDown = _useLock2[1];
  95. var onInternalInputKeyDown = function onInternalInputKeyDown(event) {
  96. var which = event.which;
  97. if (which === KeyCode.UP || which === KeyCode.DOWN) {
  98. event.preventDefault();
  99. }
  100. if (props.onInputKeyDown) {
  101. props.onInputKeyDown(event);
  102. }
  103. if (which === KeyCode.ENTER && props.mode === 'tags' && !compositionStatus && !props.open) {
  104. // When menu isn't open, OptionList won't trigger a value change
  105. // So when enter is pressed, the tag's input value should be emitted here to let selector know
  106. props.onSearchSubmit(event.target.value);
  107. }
  108. if (isValidateOpenKey(which)) {
  109. props.onToggleOpen(true);
  110. }
  111. };
  112. /**
  113. * We can not use `findDOMNode` sine it will get warning,
  114. * have to use timer to check if is input element.
  115. */
  116. var onInternalInputMouseDown = function onInternalInputMouseDown() {
  117. setInputMouseDown(true);
  118. };
  119. // When paste come, ignore next onChange
  120. var pastedText = null;
  121. var triggerOnSearch = function triggerOnSearch(value) {
  122. if (props.onSearch(value, true, compositionStatus) !== false) {
  123. props.onToggleOpen(true);
  124. }
  125. };
  126. var onInputCompositionStart = function onInputCompositionStart() {
  127. compositionStatus = true;
  128. };
  129. var onInputCompositionEnd = function onInputCompositionEnd(e) {
  130. compositionStatus = false;
  131. // Trigger search again to support `tokenSeparators` with typewriting
  132. if (props.mode !== 'combobox') {
  133. triggerOnSearch(e.target.value);
  134. }
  135. };
  136. var onInputChange = function onInputChange(event) {
  137. var value = event.target.value;
  138. // Pasted text should replace back to origin content
  139. if (props.tokenWithEnter && pastedText && /[\r\n]/.test(pastedText)) {
  140. // CRLF will be treated as a single space for input element
  141. var replacedText = pastedText.replace(/[\r\n]+$/, '').replace(/\r\n/g, ' ').replace(/[\r\n]/g, ' ');
  142. value = value.replace(replacedText, pastedText);
  143. }
  144. pastedText = null;
  145. triggerOnSearch(value);
  146. };
  147. var onInputPaste = function onInputPaste(e) {
  148. var clipboardData = e.clipboardData;
  149. var value = clipboardData.getData('text');
  150. pastedText = value;
  151. };
  152. var onClick = function onClick(_ref2) {
  153. var target = _ref2.target;
  154. if (target !== inputRef.current) {
  155. // Should focus input if click the selector
  156. var isIE = document.body.style.msTouchAction !== undefined;
  157. if (isIE) {
  158. setTimeout(function () {
  159. inputRef.current.focus();
  160. });
  161. } else {
  162. inputRef.current.focus();
  163. }
  164. }
  165. };
  166. var onMousedown = function onMousedown(event) {
  167. var inputMouseDown = getInputMouseDown();
  168. if (event.target !== inputRef.current && !inputMouseDown) {
  169. event.preventDefault();
  170. }
  171. if (props.mode !== 'combobox' && (!props.showSearch || !inputMouseDown) || !props.open) {
  172. if (props.open) {
  173. props.onSearch('', true, false);
  174. }
  175. props.onToggleOpen();
  176. }
  177. };
  178. expose({
  179. focus: function focus() {
  180. inputRef.current.focus();
  181. },
  182. blur: function blur() {
  183. inputRef.current.blur();
  184. }
  185. });
  186. return function () {
  187. var prefixCls = props.prefixCls,
  188. domRef = props.domRef,
  189. mode = props.mode;
  190. var sharedProps = {
  191. inputRef: inputRef,
  192. onInputKeyDown: onInternalInputKeyDown,
  193. onInputMouseDown: onInternalInputMouseDown,
  194. onInputChange: onInputChange,
  195. onInputPaste: onInputPaste,
  196. onInputCompositionStart: onInputCompositionStart,
  197. onInputCompositionEnd: onInputCompositionEnd
  198. };
  199. var selectNode = mode === 'multiple' || mode === 'tags' ? _createVNode(MultipleSelector, _objectSpread(_objectSpread({}, props), sharedProps), null) : _createVNode(SingleSelector, _objectSpread(_objectSpread({}, props), sharedProps), null);
  200. return _createVNode("div", {
  201. "ref": domRef,
  202. "class": "".concat(prefixCls, "-selector"),
  203. "onClick": onClick,
  204. "onMousedown": onMousedown
  205. }, [selectNode]);
  206. };
  207. }
  208. });
  209. export default Selector;