usePickerInput.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import { onBeforeUnmount, onMounted, watch, shallowRef, computed } from 'vue';
  2. import KeyCode from '../../_util/KeyCode';
  3. import { addGlobalMousedownEvent, getTargetFromEvent } from '../utils/uiUtil';
  4. import raf from '../../_util/raf';
  5. export default function usePickerInput(_ref) {
  6. let {
  7. open,
  8. value,
  9. isClickOutside,
  10. triggerOpen,
  11. forwardKeydown,
  12. onKeydown,
  13. blurToCancel,
  14. onSubmit,
  15. onCancel,
  16. onFocus,
  17. onBlur
  18. } = _ref;
  19. const typing = shallowRef(false);
  20. const focused = shallowRef(false);
  21. /**
  22. * We will prevent blur to handle open event when user click outside,
  23. * since this will repeat trigger `onOpenChange` event.
  24. */
  25. const preventBlurRef = shallowRef(false);
  26. const valueChangedRef = shallowRef(false);
  27. const preventDefaultRef = shallowRef(false);
  28. const inputProps = computed(() => ({
  29. onMousedown: () => {
  30. typing.value = true;
  31. triggerOpen(true);
  32. },
  33. onKeydown: e => {
  34. const preventDefault = () => {
  35. preventDefaultRef.value = true;
  36. };
  37. onKeydown(e, preventDefault);
  38. if (preventDefaultRef.value) return;
  39. switch (e.which) {
  40. case KeyCode.ENTER:
  41. {
  42. if (!open.value) {
  43. triggerOpen(true);
  44. } else if (onSubmit() !== false) {
  45. typing.value = true;
  46. }
  47. e.preventDefault();
  48. return;
  49. }
  50. case KeyCode.TAB:
  51. {
  52. if (typing.value && open.value && !e.shiftKey) {
  53. typing.value = false;
  54. e.preventDefault();
  55. } else if (!typing.value && open.value) {
  56. if (!forwardKeydown(e) && e.shiftKey) {
  57. typing.value = true;
  58. e.preventDefault();
  59. }
  60. }
  61. return;
  62. }
  63. case KeyCode.ESC:
  64. {
  65. typing.value = true;
  66. onCancel();
  67. return;
  68. }
  69. }
  70. if (!open.value && ![KeyCode.SHIFT].includes(e.which)) {
  71. triggerOpen(true);
  72. } else if (!typing.value) {
  73. // Let popup panel handle keyboard
  74. forwardKeydown(e);
  75. }
  76. },
  77. onFocus: e => {
  78. typing.value = true;
  79. focused.value = true;
  80. if (onFocus) {
  81. onFocus(e);
  82. }
  83. },
  84. onBlur: e => {
  85. if (preventBlurRef.value || !isClickOutside(document.activeElement)) {
  86. preventBlurRef.value = false;
  87. return;
  88. }
  89. if (blurToCancel.value) {
  90. setTimeout(() => {
  91. let {
  92. activeElement
  93. } = document;
  94. while (activeElement && activeElement.shadowRoot) {
  95. activeElement = activeElement.shadowRoot.activeElement;
  96. }
  97. if (isClickOutside(activeElement)) {
  98. onCancel();
  99. }
  100. }, 0);
  101. } else if (open.value) {
  102. triggerOpen(false);
  103. if (valueChangedRef.value) {
  104. onSubmit();
  105. }
  106. }
  107. focused.value = false;
  108. if (onBlur) {
  109. onBlur(e);
  110. }
  111. }
  112. }));
  113. // check if value changed
  114. watch(open, () => {
  115. valueChangedRef.value = false;
  116. });
  117. watch(value, () => {
  118. valueChangedRef.value = true;
  119. });
  120. const globalMousedownEvent = shallowRef();
  121. // Global click handler
  122. onMounted(() => {
  123. globalMousedownEvent.value = addGlobalMousedownEvent(e => {
  124. const target = getTargetFromEvent(e);
  125. if (open.value) {
  126. const clickedOutside = isClickOutside(target);
  127. if (!clickedOutside) {
  128. preventBlurRef.value = true;
  129. // Always set back in case `onBlur` prevented by user
  130. raf(() => {
  131. preventBlurRef.value = false;
  132. });
  133. } else if (!focused.value || clickedOutside) {
  134. triggerOpen(false);
  135. }
  136. }
  137. });
  138. });
  139. onBeforeUnmount(() => {
  140. globalMousedownEvent.value && globalMousedownEvent.value();
  141. });
  142. return [inputProps, {
  143. focused,
  144. typing
  145. }];
  146. }