SinglePicker.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  3. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  4. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  5. import { useEvent, useMergedState } from 'rc-util';
  6. import useLayoutEffect from "rc-util/es/hooks/useLayoutEffect";
  7. import omit from "rc-util/es/omit";
  8. import pickAttrs from "rc-util/es/pickAttrs";
  9. import * as React from 'react';
  10. import useToggleDates from "../hooks/useToggleDates";
  11. import PickerTrigger from "../PickerTrigger";
  12. import { pickTriggerProps } from "../PickerTrigger/util";
  13. import { toArray } from "../utils/miscUtil";
  14. import PickerContext from "./context";
  15. import useCellRender from "./hooks/useCellRender";
  16. import useFieldsInvalidate from "./hooks/useFieldsInvalidate";
  17. import useFilledProps from "./hooks/useFilledProps";
  18. import useOpen from "./hooks/useOpen";
  19. import usePickerRef from "./hooks/usePickerRef";
  20. import usePresets from "./hooks/usePresets";
  21. import useRangeActive from "./hooks/useRangeActive";
  22. import useRangePickerValue from "./hooks/useRangePickerValue";
  23. import useRangeValue, { useInnerValue } from "./hooks/useRangeValue";
  24. import useShowNow from "./hooks/useShowNow";
  25. import Popup from "./Popup";
  26. import SingleSelector from "./Selector/SingleSelector";
  27. // TODO: isInvalidateDate with showTime.disabledTime should not provide `range` prop
  28. /** Internal usage. For cross function get same aligned props */
  29. function Picker(props, ref) {
  30. // ========================= Prop =========================
  31. var _useFilledProps = useFilledProps(props),
  32. _useFilledProps2 = _slicedToArray(_useFilledProps, 6),
  33. filledProps = _useFilledProps2[0],
  34. internalPicker = _useFilledProps2[1],
  35. complexPicker = _useFilledProps2[2],
  36. formatList = _useFilledProps2[3],
  37. maskFormat = _useFilledProps2[4],
  38. isInvalidateDate = _useFilledProps2[5];
  39. var _ref = filledProps,
  40. prefixCls = _ref.prefixCls,
  41. styles = _ref.styles,
  42. classNames = _ref.classNames,
  43. order = _ref.order,
  44. defaultValue = _ref.defaultValue,
  45. value = _ref.value,
  46. needConfirm = _ref.needConfirm,
  47. onChange = _ref.onChange,
  48. onKeyDown = _ref.onKeyDown,
  49. disabled = _ref.disabled,
  50. disabledDate = _ref.disabledDate,
  51. minDate = _ref.minDate,
  52. maxDate = _ref.maxDate,
  53. defaultOpen = _ref.defaultOpen,
  54. open = _ref.open,
  55. onOpenChange = _ref.onOpenChange,
  56. locale = _ref.locale,
  57. generateConfig = _ref.generateConfig,
  58. picker = _ref.picker,
  59. showNow = _ref.showNow,
  60. showToday = _ref.showToday,
  61. showTime = _ref.showTime,
  62. mode = _ref.mode,
  63. onPanelChange = _ref.onPanelChange,
  64. onCalendarChange = _ref.onCalendarChange,
  65. onOk = _ref.onOk,
  66. multiple = _ref.multiple,
  67. defaultPickerValue = _ref.defaultPickerValue,
  68. pickerValue = _ref.pickerValue,
  69. onPickerValueChange = _ref.onPickerValueChange,
  70. inputReadOnly = _ref.inputReadOnly,
  71. suffixIcon = _ref.suffixIcon,
  72. removeIcon = _ref.removeIcon,
  73. onFocus = _ref.onFocus,
  74. onBlur = _ref.onBlur,
  75. presets = _ref.presets,
  76. components = _ref.components,
  77. cellRender = _ref.cellRender,
  78. dateRender = _ref.dateRender,
  79. monthCellRender = _ref.monthCellRender,
  80. onClick = _ref.onClick;
  81. // ========================= Refs =========================
  82. var selectorRef = usePickerRef(ref);
  83. // ========================= Util =========================
  84. function pickerParam(values) {
  85. if (values === null) {
  86. return null;
  87. }
  88. return multiple ? values : values[0];
  89. }
  90. var toggleDates = useToggleDates(generateConfig, locale, internalPicker);
  91. // ========================= Open =========================
  92. var _useOpen = useOpen(open, defaultOpen, [disabled], onOpenChange),
  93. _useOpen2 = _slicedToArray(_useOpen, 2),
  94. mergedOpen = _useOpen2[0],
  95. triggerOpen = _useOpen2[1];
  96. // ======================= Calendar =======================
  97. var onInternalCalendarChange = function onInternalCalendarChange(dates, dateStrings, info) {
  98. if (onCalendarChange) {
  99. var filteredInfo = _objectSpread({}, info);
  100. delete filteredInfo.range;
  101. onCalendarChange(pickerParam(dates), pickerParam(dateStrings), filteredInfo);
  102. }
  103. };
  104. var onInternalOk = function onInternalOk(dates) {
  105. onOk === null || onOk === void 0 || onOk(pickerParam(dates));
  106. };
  107. // ======================== Values ========================
  108. var _useInnerValue = useInnerValue(generateConfig, locale, formatList, false, order, defaultValue, value, onInternalCalendarChange, onInternalOk),
  109. _useInnerValue2 = _slicedToArray(_useInnerValue, 5),
  110. mergedValue = _useInnerValue2[0],
  111. setInnerValue = _useInnerValue2[1],
  112. getCalendarValue = _useInnerValue2[2],
  113. triggerCalendarChange = _useInnerValue2[3],
  114. triggerOk = _useInnerValue2[4];
  115. var calendarValue = getCalendarValue();
  116. // ======================== Active ========================
  117. // In SinglePicker, we will always get `activeIndex` is 0.
  118. var _useRangeActive = useRangeActive([disabled]),
  119. _useRangeActive2 = _slicedToArray(_useRangeActive, 4),
  120. focused = _useRangeActive2[0],
  121. triggerFocus = _useRangeActive2[1],
  122. lastOperation = _useRangeActive2[2],
  123. activeIndex = _useRangeActive2[3];
  124. var onSharedFocus = function onSharedFocus(event) {
  125. triggerFocus(true);
  126. onFocus === null || onFocus === void 0 || onFocus(event, {});
  127. };
  128. var onSharedBlur = function onSharedBlur(event) {
  129. triggerFocus(false);
  130. onBlur === null || onBlur === void 0 || onBlur(event, {});
  131. };
  132. // ========================= Mode =========================
  133. var _useMergedState = useMergedState(picker, {
  134. value: mode
  135. }),
  136. _useMergedState2 = _slicedToArray(_useMergedState, 2),
  137. mergedMode = _useMergedState2[0],
  138. setMode = _useMergedState2[1];
  139. /** Extends from `mergedMode` to patch `datetime` mode */
  140. var internalMode = mergedMode === 'date' && showTime ? 'datetime' : mergedMode;
  141. // ======================= Show Now =======================
  142. var mergedShowNow = useShowNow(picker, mergedMode, showNow, showToday);
  143. // ======================== Value =========================
  144. var onInternalChange = onChange && function (dates, dateStrings) {
  145. onChange(pickerParam(dates), pickerParam(dateStrings));
  146. };
  147. var _useRangeValue = useRangeValue(_objectSpread(_objectSpread({}, filledProps), {}, {
  148. onChange: onInternalChange
  149. }), mergedValue, setInnerValue, getCalendarValue, triggerCalendarChange, [],
  150. //disabled,
  151. formatList, focused, mergedOpen, isInvalidateDate),
  152. _useRangeValue2 = _slicedToArray(_useRangeValue, 2),
  153. /** Trigger `onChange` directly without check `disabledDate` */
  154. triggerSubmitChange = _useRangeValue2[1];
  155. // ======================= Validate =======================
  156. var _useFieldsInvalidate = useFieldsInvalidate(calendarValue, isInvalidateDate),
  157. _useFieldsInvalidate2 = _slicedToArray(_useFieldsInvalidate, 2),
  158. submitInvalidates = _useFieldsInvalidate2[0],
  159. onSelectorInvalid = _useFieldsInvalidate2[1];
  160. var submitInvalidate = React.useMemo(function () {
  161. return submitInvalidates.some(function (invalidated) {
  162. return invalidated;
  163. });
  164. }, [submitInvalidates]);
  165. // ===================== Picker Value =====================
  166. // Proxy to single pickerValue
  167. var onInternalPickerValueChange = function onInternalPickerValueChange(dates, info) {
  168. if (onPickerValueChange) {
  169. var cleanInfo = _objectSpread(_objectSpread({}, info), {}, {
  170. mode: info.mode[0]
  171. });
  172. delete cleanInfo.range;
  173. onPickerValueChange(dates[0], cleanInfo);
  174. }
  175. };
  176. var _useRangePickerValue = useRangePickerValue(generateConfig, locale, calendarValue, [mergedMode], mergedOpen, activeIndex, internalPicker, false,
  177. // multiplePanel,
  178. defaultPickerValue, pickerValue, toArray(showTime === null || showTime === void 0 ? void 0 : showTime.defaultOpenValue), onInternalPickerValueChange, minDate, maxDate),
  179. _useRangePickerValue2 = _slicedToArray(_useRangePickerValue, 2),
  180. currentPickerValue = _useRangePickerValue2[0],
  181. setCurrentPickerValue = _useRangePickerValue2[1];
  182. // >>> Mode need wait for `pickerValue`
  183. var triggerModeChange = useEvent(function (nextPickerValue, nextMode, triggerEvent) {
  184. setMode(nextMode);
  185. // Compatible with `onPanelChange`
  186. if (onPanelChange && triggerEvent !== false) {
  187. var lastPickerValue = nextPickerValue || calendarValue[calendarValue.length - 1];
  188. onPanelChange(lastPickerValue, nextMode);
  189. }
  190. });
  191. // ======================== Submit ========================
  192. /**
  193. * Different with RangePicker, confirm should check `multiple` logic.
  194. * This will never provide `date` instead.
  195. */
  196. var triggerConfirm = function triggerConfirm() {
  197. triggerSubmitChange(getCalendarValue());
  198. triggerOpen(false, {
  199. force: true
  200. });
  201. };
  202. // ======================== Click =========================
  203. var onSelectorClick = function onSelectorClick(event) {
  204. if (!disabled && !selectorRef.current.nativeElement.contains(document.activeElement)) {
  205. // Click to focus the enabled input
  206. selectorRef.current.focus();
  207. }
  208. triggerOpen(true);
  209. onClick === null || onClick === void 0 || onClick(event);
  210. };
  211. var onSelectorClear = function onSelectorClear() {
  212. triggerSubmitChange(null);
  213. triggerOpen(false, {
  214. force: true
  215. });
  216. };
  217. // ======================== Hover =========================
  218. var _React$useState = React.useState(null),
  219. _React$useState2 = _slicedToArray(_React$useState, 2),
  220. hoverSource = _React$useState2[0],
  221. setHoverSource = _React$useState2[1];
  222. var _React$useState3 = React.useState(null),
  223. _React$useState4 = _slicedToArray(_React$useState3, 2),
  224. internalHoverValue = _React$useState4[0],
  225. setInternalHoverValue = _React$useState4[1];
  226. var hoverValues = React.useMemo(function () {
  227. var values = [internalHoverValue].concat(_toConsumableArray(calendarValue)).filter(function (date) {
  228. return date;
  229. });
  230. return multiple ? values : values.slice(0, 1);
  231. }, [calendarValue, internalHoverValue, multiple]);
  232. // Selector values is different with RangePicker
  233. // which can not use `hoverValue` directly
  234. var selectorValues = React.useMemo(function () {
  235. if (!multiple && internalHoverValue) {
  236. return [internalHoverValue];
  237. }
  238. return calendarValue.filter(function (date) {
  239. return date;
  240. });
  241. }, [calendarValue, internalHoverValue, multiple]);
  242. // Clean up `internalHoverValues` when closed
  243. React.useEffect(function () {
  244. if (!mergedOpen) {
  245. setInternalHoverValue(null);
  246. }
  247. }, [mergedOpen]);
  248. // ========================================================
  249. // == Panels ==
  250. // ========================================================
  251. // ======================= Presets ========================
  252. var presetList = usePresets(presets);
  253. var onPresetHover = function onPresetHover(nextValue) {
  254. setInternalHoverValue(nextValue);
  255. setHoverSource('preset');
  256. };
  257. // TODO: handle this
  258. var onPresetSubmit = function onPresetSubmit(nextValue) {
  259. var nextCalendarValues = multiple ? toggleDates(getCalendarValue(), nextValue) : [nextValue];
  260. var passed = triggerSubmitChange(nextCalendarValues);
  261. if (passed && !multiple) {
  262. triggerOpen(false, {
  263. force: true
  264. });
  265. }
  266. };
  267. var onNow = function onNow(now) {
  268. onPresetSubmit(now);
  269. };
  270. // ======================== Panel =========================
  271. var onPanelHover = function onPanelHover(date) {
  272. setInternalHoverValue(date);
  273. setHoverSource('cell');
  274. };
  275. // >>> Focus
  276. var onPanelFocus = function onPanelFocus(event) {
  277. triggerOpen(true);
  278. onSharedFocus(event);
  279. };
  280. // >>> Calendar
  281. var onPanelSelect = function onPanelSelect(date) {
  282. lastOperation('panel');
  283. // Not change values if multiple and current panel is to match with picker
  284. if (multiple && internalMode !== picker) {
  285. return;
  286. }
  287. var nextValues = multiple ? toggleDates(getCalendarValue(), date) : [date];
  288. // Only trigger calendar event but not update internal `calendarValue` state
  289. triggerCalendarChange(nextValues);
  290. // >>> Trigger next active if !needConfirm
  291. // Fully logic check `useRangeValue` hook
  292. if (!needConfirm && !complexPicker && internalPicker === internalMode) {
  293. triggerConfirm();
  294. }
  295. };
  296. // >>> Close
  297. var onPopupClose = function onPopupClose() {
  298. // Close popup
  299. triggerOpen(false);
  300. };
  301. // >>> cellRender
  302. var onInternalCellRender = useCellRender(cellRender, dateRender, monthCellRender);
  303. // >>> invalid
  304. var panelProps = React.useMemo(function () {
  305. var domProps = pickAttrs(filledProps, false);
  306. var restProps = omit(filledProps, [].concat(_toConsumableArray(Object.keys(domProps)), ['onChange', 'onCalendarChange', 'style', 'className', 'onPanelChange']));
  307. return _objectSpread(_objectSpread({}, restProps), {}, {
  308. multiple: filledProps.multiple
  309. });
  310. }, [filledProps]);
  311. // >>> Render
  312. var panel = /*#__PURE__*/React.createElement(Popup, _extends({}, panelProps, {
  313. showNow: mergedShowNow,
  314. showTime: showTime
  315. // Disabled
  316. ,
  317. disabledDate: disabledDate
  318. // Focus
  319. ,
  320. onFocus: onPanelFocus,
  321. onBlur: onSharedBlur
  322. // Mode
  323. ,
  324. picker: picker,
  325. mode: mergedMode,
  326. internalMode: internalMode,
  327. onPanelChange: triggerModeChange
  328. // Value
  329. ,
  330. format: maskFormat,
  331. value: calendarValue,
  332. isInvalid: isInvalidateDate,
  333. onChange: null,
  334. onSelect: onPanelSelect
  335. // PickerValue
  336. ,
  337. pickerValue: currentPickerValue,
  338. defaultOpenValue: showTime === null || showTime === void 0 ? void 0 : showTime.defaultOpenValue,
  339. onPickerValueChange: setCurrentPickerValue
  340. // Hover
  341. ,
  342. hoverValue: hoverValues,
  343. onHover: onPanelHover
  344. // Submit
  345. ,
  346. needConfirm: needConfirm,
  347. onSubmit: triggerConfirm,
  348. onOk: triggerOk
  349. // Preset
  350. ,
  351. presets: presetList,
  352. onPresetHover: onPresetHover,
  353. onPresetSubmit: onPresetSubmit,
  354. onNow: onNow
  355. // Render
  356. ,
  357. cellRender: onInternalCellRender
  358. }));
  359. // ========================================================
  360. // == Selector ==
  361. // ========================================================
  362. // ======================== Change ========================
  363. var onSelectorChange = function onSelectorChange(date) {
  364. triggerCalendarChange(date);
  365. };
  366. var onSelectorInputChange = function onSelectorInputChange() {
  367. lastOperation('input');
  368. };
  369. // ======================= Selector =======================
  370. var onSelectorFocus = function onSelectorFocus(event) {
  371. lastOperation('input');
  372. triggerOpen(true, {
  373. inherit: true
  374. });
  375. // setActiveIndex(index);
  376. onSharedFocus(event);
  377. };
  378. var onSelectorBlur = function onSelectorBlur(event) {
  379. triggerOpen(false);
  380. onSharedBlur(event);
  381. };
  382. var onSelectorKeyDown = function onSelectorKeyDown(event, preventDefault) {
  383. if (event.key === 'Tab') {
  384. triggerConfirm();
  385. }
  386. onKeyDown === null || onKeyDown === void 0 || onKeyDown(event, preventDefault);
  387. };
  388. // ======================= Context ========================
  389. var context = React.useMemo(function () {
  390. return {
  391. prefixCls: prefixCls,
  392. locale: locale,
  393. generateConfig: generateConfig,
  394. button: components.button,
  395. input: components.input
  396. };
  397. }, [prefixCls, locale, generateConfig, components.button, components.input]);
  398. // ======================== Effect ========================
  399. // >>> Mode
  400. // Reset for every active
  401. useLayoutEffect(function () {
  402. if (mergedOpen && activeIndex !== undefined) {
  403. // Legacy compatible. This effect update should not trigger `onPanelChange`
  404. triggerModeChange(null, picker, false);
  405. }
  406. }, [mergedOpen, activeIndex, picker]);
  407. // >>> For complex picker, we need check if need to focus next one
  408. useLayoutEffect(function () {
  409. var lastOp = lastOperation();
  410. // Trade as confirm on field leave
  411. if (!mergedOpen && lastOp === 'input') {
  412. triggerOpen(false);
  413. triggerConfirm();
  414. }
  415. // Submit with complex picker
  416. if (!mergedOpen && complexPicker && !needConfirm && lastOp === 'panel') {
  417. triggerConfirm();
  418. }
  419. }, [mergedOpen]);
  420. // ======================== Render ========================
  421. return /*#__PURE__*/React.createElement(PickerContext.Provider, {
  422. value: context
  423. }, /*#__PURE__*/React.createElement(PickerTrigger, _extends({}, pickTriggerProps(filledProps), {
  424. popupElement: panel,
  425. popupStyle: styles.popup,
  426. popupClassName: classNames.popup
  427. // Visible
  428. ,
  429. visible: mergedOpen,
  430. onClose: onPopupClose
  431. }), /*#__PURE__*/React.createElement(SingleSelector
  432. // Shared
  433. , _extends({}, filledProps, {
  434. // Ref
  435. ref: selectorRef
  436. // Icon
  437. ,
  438. suffixIcon: suffixIcon,
  439. removeIcon: removeIcon
  440. // Active
  441. ,
  442. activeHelp: !!internalHoverValue,
  443. allHelp: !!internalHoverValue && hoverSource === 'preset',
  444. focused: focused,
  445. onFocus: onSelectorFocus,
  446. onBlur: onSelectorBlur,
  447. onKeyDown: onSelectorKeyDown,
  448. onSubmit: triggerConfirm
  449. // Change
  450. ,
  451. value: selectorValues,
  452. maskFormat: maskFormat,
  453. onChange: onSelectorChange,
  454. onInputChange: onSelectorInputChange,
  455. internalPicker: internalPicker
  456. // Format
  457. ,
  458. format: formatList,
  459. inputReadOnly: inputReadOnly
  460. // Disabled
  461. ,
  462. disabled: disabled
  463. // Open
  464. ,
  465. open: mergedOpen,
  466. onOpenChange: triggerOpen
  467. // Click
  468. ,
  469. onClick: onSelectorClick,
  470. onClear: onSelectorClear
  471. // Invalid
  472. ,
  473. invalid: submitInvalidate,
  474. onInvalid: function onInvalid(invalid) {
  475. // Only `single` mode support type date.
  476. // `multiple` mode can not typing.
  477. onSelectorInvalid(invalid, 0);
  478. }
  479. }))));
  480. }
  481. var RefPicker = /*#__PURE__*/React.forwardRef(Picker);
  482. if (process.env.NODE_ENV !== 'production') {
  483. RefPicker.displayName = 'RefPicker';
  484. }
  485. export default RefPicker;