Picker.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  2. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  3. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  4. import { resolveDirective as _resolveDirective, createVNode as _createVNode } from "vue";
  5. /**
  6. * Removed:
  7. * - getCalendarContainer: use `getPopupContainer` instead
  8. * - onOk
  9. *
  10. * New Feature:
  11. * - picker
  12. * - allowEmpty
  13. * - selectable
  14. *
  15. * Tips: Should add faq about `datetime` mode with `defaultValue`
  16. */
  17. import PickerPanel from './PickerPanel';
  18. import PickerTrigger from './PickerTrigger';
  19. import { formatValue, isEqual, parseValue } from './utils/dateUtil';
  20. import getDataOrAriaProps, { toArray } from './utils/miscUtil';
  21. import { useProvidePanel } from './PanelContext';
  22. import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil';
  23. import usePickerInput from './hooks/usePickerInput';
  24. import useTextValueMapping from './hooks/useTextValueMapping';
  25. import useValueTexts from './hooks/useValueTexts';
  26. import useHoverValue from './hooks/useHoverValue';
  27. import { computed, defineComponent, ref, toRef, watch } from 'vue';
  28. import useMergedState from '../_util/hooks/useMergedState';
  29. import { warning } from '../vc-util/warning';
  30. import classNames from '../_util/classNames';
  31. import { useProviderTrigger } from '../vc-trigger/context';
  32. import { legacyPropsWarning } from './utils/warnUtil';
  33. function Picker() {
  34. return defineComponent({
  35. name: 'Picker',
  36. inheritAttrs: false,
  37. props: ['prefixCls', 'id', 'tabindex', 'dropdownClassName', 'dropdownAlign', 'popupStyle', 'transitionName', 'generateConfig', 'locale', 'inputReadOnly', 'allowClear', 'autofocus', 'showTime', 'showNow', 'showHour', 'showMinute', 'showSecond', 'picker', 'format', 'use12Hours', 'value', 'defaultValue', 'open', 'defaultOpen', 'defaultOpenValue', 'suffixIcon', 'clearIcon', 'disabled', 'disabledDate', 'placeholder', 'getPopupContainer', 'panelRender', 'inputRender', 'onChange', 'onOpenChange', 'onFocus', 'onBlur', 'onMousedown', 'onMouseup', 'onMouseenter', 'onMouseleave', 'onContextmenu', 'onClick', 'onKeydown', 'onSelect', 'direction', 'autocomplete', 'showToday', 'renderExtraFooter', 'dateRender', 'minuteStep', 'hourStep', 'secondStep', 'hideDisabledOptions'],
  38. // slots: [
  39. // 'suffixIcon',
  40. // 'clearIcon',
  41. // 'prevIcon',
  42. // 'nextIcon',
  43. // 'superPrevIcon',
  44. // 'superNextIcon',
  45. // 'panelRender',
  46. // ],
  47. setup: function setup(props, _ref) {
  48. var attrs = _ref.attrs,
  49. expose = _ref.expose;
  50. var inputRef = ref(null);
  51. var picker = computed(function () {
  52. var _props$picker;
  53. return (_props$picker = props.picker) !== null && _props$picker !== void 0 ? _props$picker : 'date';
  54. });
  55. var needConfirmButton = computed(function () {
  56. return picker.value === 'date' && !!props.showTime || picker.value === 'time';
  57. });
  58. // ============================ Warning ============================
  59. if (process.env.NODE_ENV !== 'production') {
  60. legacyPropsWarning(props);
  61. }
  62. // ============================= State =============================
  63. var formatList = computed(function () {
  64. return toArray(getDefaultFormat(props.format, picker.value, props.showTime, props.use12Hours));
  65. });
  66. // Panel ref
  67. var panelDivRef = ref(null);
  68. var inputDivRef = ref(null);
  69. var containerRef = ref(null);
  70. // Real value
  71. var _useMergedState = useMergedState(null, {
  72. value: toRef(props, 'value'),
  73. defaultValue: props.defaultValue
  74. }),
  75. _useMergedState2 = _slicedToArray(_useMergedState, 2),
  76. mergedValue = _useMergedState2[0],
  77. setInnerValue = _useMergedState2[1];
  78. var selectedValue = ref(mergedValue.value);
  79. var setSelectedValue = function setSelectedValue(val) {
  80. selectedValue.value = val;
  81. };
  82. // Operation ref
  83. var operationRef = ref(null);
  84. // Open
  85. var _useMergedState3 = useMergedState(false, {
  86. value: toRef(props, 'open'),
  87. defaultValue: props.defaultOpen,
  88. postState: function postState(postOpen) {
  89. return props.disabled ? false : postOpen;
  90. },
  91. onChange: function onChange(newOpen) {
  92. if (props.onOpenChange) {
  93. props.onOpenChange(newOpen);
  94. }
  95. if (!newOpen && operationRef.value && operationRef.value.onClose) {
  96. operationRef.value.onClose();
  97. }
  98. }
  99. }),
  100. _useMergedState4 = _slicedToArray(_useMergedState3, 2),
  101. mergedOpen = _useMergedState4[0],
  102. triggerInnerOpen = _useMergedState4[1];
  103. // ============================= Text ==============================
  104. var _useValueTexts = useValueTexts(selectedValue, {
  105. formatList: formatList,
  106. generateConfig: toRef(props, 'generateConfig'),
  107. locale: toRef(props, 'locale')
  108. }),
  109. _useValueTexts2 = _slicedToArray(_useValueTexts, 2),
  110. valueTexts = _useValueTexts2[0],
  111. firstValueText = _useValueTexts2[1];
  112. var _useTextValueMapping = useTextValueMapping({
  113. valueTexts: valueTexts,
  114. onTextChange: function onTextChange(newText) {
  115. var inputDate = parseValue(newText, {
  116. locale: props.locale,
  117. formatList: formatList.value,
  118. generateConfig: props.generateConfig
  119. });
  120. if (inputDate && (!props.disabledDate || !props.disabledDate(inputDate))) {
  121. setSelectedValue(inputDate);
  122. }
  123. }
  124. }),
  125. _useTextValueMapping2 = _slicedToArray(_useTextValueMapping, 3),
  126. text = _useTextValueMapping2[0],
  127. triggerTextChange = _useTextValueMapping2[1],
  128. resetText = _useTextValueMapping2[2];
  129. // ============================ Trigger ============================
  130. var triggerChange = function triggerChange(newValue) {
  131. var onChange = props.onChange,
  132. generateConfig = props.generateConfig,
  133. locale = props.locale;
  134. setSelectedValue(newValue);
  135. setInnerValue(newValue);
  136. if (onChange && !isEqual(generateConfig, mergedValue.value, newValue)) {
  137. onChange(newValue, newValue ? formatValue(newValue, {
  138. generateConfig: generateConfig,
  139. locale: locale,
  140. format: formatList.value[0]
  141. }) : '');
  142. }
  143. };
  144. var triggerOpen = function triggerOpen(newOpen) {
  145. if (props.disabled && newOpen) {
  146. return;
  147. }
  148. triggerInnerOpen(newOpen);
  149. };
  150. var forwardKeydown = function forwardKeydown(e) {
  151. if (mergedOpen.value && operationRef.value && operationRef.value.onKeydown) {
  152. // Let popup panel handle keyboard
  153. return operationRef.value.onKeydown(e);
  154. }
  155. /* istanbul ignore next */
  156. /* eslint-disable no-lone-blocks */
  157. {
  158. warning(false, 'Picker not correct forward Keydown operation. Please help to fire issue about this.');
  159. return false;
  160. }
  161. };
  162. var onInternalMouseup = function onInternalMouseup() {
  163. if (props.onMouseup) {
  164. props.onMouseup.apply(props, arguments);
  165. }
  166. if (inputRef.value) {
  167. inputRef.value.focus();
  168. triggerOpen(true);
  169. }
  170. };
  171. // ============================= Input =============================
  172. var _usePickerInput = usePickerInput({
  173. blurToCancel: needConfirmButton,
  174. open: mergedOpen,
  175. value: text,
  176. triggerOpen: triggerOpen,
  177. forwardKeydown: forwardKeydown,
  178. isClickOutside: function isClickOutside(target) {
  179. return !elementsContains([panelDivRef.value, inputDivRef.value, containerRef.value], target);
  180. },
  181. onSubmit: function onSubmit() {
  182. if (
  183. // When user typing disabledDate with keyboard and enter, this value will be empty
  184. !selectedValue.value ||
  185. // Normal disabled check
  186. props.disabledDate && props.disabledDate(selectedValue.value)) {
  187. return false;
  188. }
  189. triggerChange(selectedValue.value);
  190. triggerOpen(false);
  191. resetText();
  192. return true;
  193. },
  194. onCancel: function onCancel() {
  195. triggerOpen(false);
  196. setSelectedValue(mergedValue.value);
  197. resetText();
  198. },
  199. onKeydown: function onKeydown(e, preventDefault) {
  200. var _props$onKeydown;
  201. (_props$onKeydown = props.onKeydown) === null || _props$onKeydown === void 0 ? void 0 : _props$onKeydown.call(props, e, preventDefault);
  202. },
  203. onFocus: function onFocus(e) {
  204. var _props$onFocus;
  205. (_props$onFocus = props.onFocus) === null || _props$onFocus === void 0 ? void 0 : _props$onFocus.call(props, e);
  206. },
  207. onBlur: function onBlur(e) {
  208. var _props$onBlur;
  209. (_props$onBlur = props.onBlur) === null || _props$onBlur === void 0 ? void 0 : _props$onBlur.call(props, e);
  210. }
  211. }),
  212. _usePickerInput2 = _slicedToArray(_usePickerInput, 2),
  213. inputProps = _usePickerInput2[0],
  214. _usePickerInput2$ = _usePickerInput2[1],
  215. focused = _usePickerInput2$.focused,
  216. typing = _usePickerInput2$.typing;
  217. // ============================= Sync ==============================
  218. // Close should sync back with text value
  219. watch([mergedOpen, valueTexts], function () {
  220. if (!mergedOpen.value) {
  221. setSelectedValue(mergedValue.value);
  222. if (!valueTexts.value.length || valueTexts.value[0] === '') {
  223. triggerTextChange('');
  224. } else if (firstValueText.value !== text.value) {
  225. resetText();
  226. }
  227. }
  228. });
  229. // Change picker should sync back with text value
  230. watch(picker, function () {
  231. if (!mergedOpen.value) {
  232. resetText();
  233. }
  234. });
  235. // Sync innerValue with control mode
  236. watch(mergedValue, function () {
  237. // Sync select value
  238. setSelectedValue(mergedValue.value);
  239. });
  240. var _useHoverValue = useHoverValue(text, {
  241. formatList: formatList,
  242. generateConfig: toRef(props, 'generateConfig'),
  243. locale: toRef(props, 'locale')
  244. }),
  245. _useHoverValue2 = _slicedToArray(_useHoverValue, 3),
  246. hoverValue = _useHoverValue2[0],
  247. onEnter = _useHoverValue2[1],
  248. onLeave = _useHoverValue2[2];
  249. var onContextSelect = function onContextSelect(date, type) {
  250. if (type === 'submit' || type !== 'key' && !needConfirmButton.value) {
  251. // triggerChange will also update selected values
  252. triggerChange(date);
  253. triggerOpen(false);
  254. }
  255. };
  256. useProvidePanel({
  257. operationRef: operationRef,
  258. hideHeader: computed(function () {
  259. return picker.value === 'time';
  260. }),
  261. panelRef: panelDivRef,
  262. onSelect: onContextSelect,
  263. open: mergedOpen,
  264. defaultOpenValue: toRef(props, 'defaultOpenValue'),
  265. onDateMouseenter: onEnter,
  266. onDateMouseleave: onLeave
  267. });
  268. expose({
  269. focus: function focus() {
  270. if (inputRef.value) {
  271. inputRef.value.focus();
  272. }
  273. },
  274. blur: function blur() {
  275. if (inputRef.value) {
  276. inputRef.value.blur();
  277. }
  278. }
  279. });
  280. var getPortal = useProviderTrigger();
  281. return function () {
  282. var _classNames2;
  283. var _props$prefixCls = props.prefixCls,
  284. prefixCls = _props$prefixCls === void 0 ? 'rc-picker' : _props$prefixCls,
  285. id = props.id,
  286. tabindex = props.tabindex,
  287. dropdownClassName = props.dropdownClassName,
  288. dropdownAlign = props.dropdownAlign,
  289. popupStyle = props.popupStyle,
  290. transitionName = props.transitionName,
  291. generateConfig = props.generateConfig,
  292. locale = props.locale,
  293. inputReadOnly = props.inputReadOnly,
  294. allowClear = props.allowClear,
  295. autofocus = props.autofocus,
  296. _props$picker2 = props.picker,
  297. picker = _props$picker2 === void 0 ? 'date' : _props$picker2,
  298. defaultOpenValue = props.defaultOpenValue,
  299. suffixIcon = props.suffixIcon,
  300. clearIcon = props.clearIcon,
  301. disabled = props.disabled,
  302. placeholder = props.placeholder,
  303. getPopupContainer = props.getPopupContainer,
  304. panelRender = props.panelRender,
  305. onMousedown = props.onMousedown,
  306. onMouseenter = props.onMouseenter,
  307. onMouseleave = props.onMouseleave,
  308. onContextmenu = props.onContextmenu,
  309. onClick = props.onClick,
  310. _onSelect = props.onSelect,
  311. direction = props.direction,
  312. _props$autocomplete = props.autocomplete,
  313. autocomplete = _props$autocomplete === void 0 ? 'off' : _props$autocomplete;
  314. // ============================= Panel =============================
  315. var panelProps = _objectSpread(_objectSpread(_objectSpread({}, props), attrs), {}, {
  316. class: classNames(_defineProperty({}, "".concat(prefixCls, "-panel-focused"), !typing.value)),
  317. style: undefined,
  318. pickerValue: undefined,
  319. onPickerValueChange: undefined,
  320. onChange: null
  321. });
  322. var panelNode = _createVNode(PickerPanel, _objectSpread(_objectSpread({}, panelProps), {}, {
  323. "generateConfig": generateConfig,
  324. "value": selectedValue.value,
  325. "locale": locale,
  326. "tabindex": -1,
  327. "onSelect": function onSelect(date) {
  328. _onSelect === null || _onSelect === void 0 ? void 0 : _onSelect(date);
  329. setSelectedValue(date);
  330. },
  331. "direction": direction,
  332. "onPanelChange": function onPanelChange(viewDate, mode) {
  333. var onPanelChange = props.onPanelChange;
  334. onLeave(true);
  335. onPanelChange === null || onPanelChange === void 0 ? void 0 : onPanelChange(viewDate, mode);
  336. }
  337. }), null);
  338. if (panelRender) {
  339. panelNode = panelRender(panelNode);
  340. }
  341. var panel = _createVNode("div", {
  342. "class": "".concat(prefixCls, "-panel-container"),
  343. "onMousedown": function onMousedown(e) {
  344. e.preventDefault();
  345. }
  346. }, [panelNode]);
  347. var suffixNode;
  348. if (suffixIcon) {
  349. suffixNode = _createVNode("span", {
  350. "class": "".concat(prefixCls, "-suffix")
  351. }, [suffixIcon]);
  352. }
  353. var clearNode;
  354. if (allowClear && mergedValue.value && !disabled) {
  355. clearNode = _createVNode("span", {
  356. "onMousedown": function onMousedown(e) {
  357. e.preventDefault();
  358. e.stopPropagation();
  359. },
  360. "onMouseup": function onMouseup(e) {
  361. e.preventDefault();
  362. e.stopPropagation();
  363. triggerChange(null);
  364. triggerOpen(false);
  365. },
  366. "class": "".concat(prefixCls, "-clear"),
  367. "role": "button"
  368. }, [clearIcon || _createVNode("span", {
  369. "class": "".concat(prefixCls, "-clear-btn")
  370. }, null)]);
  371. }
  372. var mergedInputProps = _objectSpread(_objectSpread(_objectSpread({
  373. id: id,
  374. tabindex: tabindex,
  375. disabled: disabled,
  376. readonly: inputReadOnly || typeof formatList.value[0] === 'function' || !typing.value,
  377. value: hoverValue.value || text.value,
  378. onInput: function onInput(e) {
  379. triggerTextChange(e.target.value);
  380. },
  381. autofocus: autofocus,
  382. placeholder: placeholder,
  383. ref: inputRef,
  384. title: text.value
  385. }, inputProps.value), {}, {
  386. size: getInputSize(picker, formatList.value[0], generateConfig)
  387. }, getDataOrAriaProps(props)), {}, {
  388. autocomplete: autocomplete
  389. });
  390. var inputNode = props.inputRender ? props.inputRender(mergedInputProps) : _createVNode("input", mergedInputProps, null);
  391. // ============================ Warning ============================
  392. if (process.env.NODE_ENV !== 'production') {
  393. warning(!defaultOpenValue, '`defaultOpenValue` may confuse user for the current value status. Please use `defaultValue` instead.');
  394. }
  395. // ============================ Return =============================
  396. var popupPlacement = direction === 'rtl' ? 'bottomRight' : 'bottomLeft';
  397. return _createVNode(PickerTrigger, {
  398. "visible": mergedOpen.value,
  399. "popupStyle": popupStyle,
  400. "prefixCls": prefixCls,
  401. "dropdownClassName": dropdownClassName,
  402. "dropdownAlign": dropdownAlign,
  403. "getPopupContainer": getPopupContainer,
  404. "transitionName": transitionName,
  405. "popupPlacement": popupPlacement,
  406. "direction": direction
  407. }, {
  408. default: function _default() {
  409. return [_createVNode("div", {
  410. "ref": containerRef,
  411. "class": classNames(prefixCls, attrs.class, (_classNames2 = {}, _defineProperty(_classNames2, "".concat(prefixCls, "-disabled"), disabled), _defineProperty(_classNames2, "".concat(prefixCls, "-focused"), focused.value), _defineProperty(_classNames2, "".concat(prefixCls, "-rtl"), direction === 'rtl'), _classNames2)),
  412. "style": attrs.style,
  413. "onMousedown": onMousedown,
  414. "onMouseup": onInternalMouseup,
  415. "onMouseenter": onMouseenter,
  416. "onMouseleave": onMouseleave,
  417. "onContextmenu": onContextmenu,
  418. "onClick": onClick
  419. }, [_createVNode("div", {
  420. "class": classNames("".concat(prefixCls, "-input"), _defineProperty({}, "".concat(prefixCls, "-input-placeholder"), !!hoverValue.value)),
  421. "ref": inputDivRef
  422. }, [inputNode, suffixNode, clearNode]), getPortal()])];
  423. },
  424. popupElement: function popupElement() {
  425. return panel;
  426. }
  427. });
  428. };
  429. }
  430. });
  431. }
  432. export default Picker();