PickerPanel.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { createVNode as _createVNode } from "vue";
  4. import TimePanel from './panels/TimePanel';
  5. import DatetimePanel from './panels/DatetimePanel';
  6. import DatePanel from './panels/DatePanel';
  7. import WeekPanel from './panels/WeekPanel';
  8. import MonthPanel from './panels/MonthPanel';
  9. import QuarterPanel from './panels/QuarterPanel';
  10. import YearPanel from './panels/YearPanel';
  11. import DecadePanel from './panels/DecadePanel';
  12. import { isEqual } from './utils/dateUtil';
  13. import { useInjectPanel, useProvidePanel } from './PanelContext';
  14. import { PickerModeMap } from './utils/uiUtil';
  15. import { useInjectRange } from './RangeContext';
  16. import getExtraFooter from './utils/getExtraFooter';
  17. import getRanges from './utils/getRanges';
  18. import { getLowerBoundTime, setDateTime, setTime } from './utils/timeUtil';
  19. import { computed, createVNode, defineComponent, ref, toRef, watch, watchEffect } from 'vue';
  20. import useMergedState from '../_util/hooks/useMergedState';
  21. import { warning } from '../vc-util/warning';
  22. import KeyCode from '../_util/KeyCode';
  23. import classNames from '../_util/classNames';
  24. function PickerPanel() {
  25. return defineComponent({
  26. name: 'PickerPanel',
  27. inheritAttrs: false,
  28. props: {
  29. prefixCls: String,
  30. locale: Object,
  31. generateConfig: Object,
  32. value: Object,
  33. defaultValue: Object,
  34. pickerValue: Object,
  35. defaultPickerValue: Object,
  36. disabledDate: Function,
  37. mode: String,
  38. picker: {
  39. type: String,
  40. default: 'date'
  41. },
  42. tabindex: {
  43. type: [Number, String],
  44. default: 0
  45. },
  46. showNow: {
  47. type: Boolean,
  48. default: undefined
  49. },
  50. showTime: [Boolean, Object],
  51. showToday: Boolean,
  52. renderExtraFooter: Function,
  53. dateRender: Function,
  54. hideHeader: {
  55. type: Boolean,
  56. default: undefined
  57. },
  58. onSelect: Function,
  59. onChange: Function,
  60. onPanelChange: Function,
  61. onMousedown: Function,
  62. onPickerValueChange: Function,
  63. onOk: Function,
  64. components: Object,
  65. direction: String,
  66. hourStep: {
  67. type: Number,
  68. default: 1
  69. },
  70. minuteStep: {
  71. type: Number,
  72. default: 1
  73. },
  74. secondStep: {
  75. type: Number,
  76. default: 1
  77. }
  78. },
  79. setup(props, _ref) {
  80. let {
  81. attrs
  82. } = _ref;
  83. const needConfirmButton = computed(() => props.picker === 'date' && !!props.showTime || props.picker === 'time');
  84. const isHourStepValid = computed(() => 24 % props.hourStep === 0);
  85. const isMinuteStepValid = computed(() => 60 % props.minuteStep === 0);
  86. const isSecondStepValid = computed(() => 60 % props.secondStep === 0);
  87. if (process.env.NODE_ENV !== 'production') {
  88. watchEffect(() => {
  89. const {
  90. generateConfig,
  91. value,
  92. hourStep = 1,
  93. minuteStep = 1,
  94. secondStep = 1
  95. } = props;
  96. warning(!value || generateConfig.isValidate(value), 'Invalidate date pass to `value`.');
  97. warning(!value || generateConfig.isValidate(value), 'Invalidate date pass to `defaultValue`.');
  98. warning(isHourStepValid.value, `\`hourStep\` ${hourStep} is invalid. It should be a factor of 24.`);
  99. warning(isMinuteStepValid.value, `\`minuteStep\` ${minuteStep} is invalid. It should be a factor of 60.`);
  100. warning(isSecondStepValid.value, `\`secondStep\` ${secondStep} is invalid. It should be a factor of 60.`);
  101. });
  102. }
  103. const panelContext = useInjectPanel();
  104. const {
  105. operationRef,
  106. onSelect: onContextSelect,
  107. hideRanges,
  108. defaultOpenValue
  109. } = panelContext;
  110. const {
  111. inRange,
  112. panelPosition,
  113. rangedValue,
  114. hoverRangedValue
  115. } = useInjectRange();
  116. const panelRef = ref({});
  117. // Value
  118. const [mergedValue, setInnerValue] = useMergedState(null, {
  119. value: toRef(props, 'value'),
  120. defaultValue: props.defaultValue,
  121. postState: val => {
  122. if (!val && (defaultOpenValue === null || defaultOpenValue === void 0 ? void 0 : defaultOpenValue.value) && props.picker === 'time') {
  123. return defaultOpenValue.value;
  124. }
  125. return val;
  126. }
  127. });
  128. // View date control
  129. const [viewDate, setInnerViewDate] = useMergedState(null, {
  130. value: toRef(props, 'pickerValue'),
  131. defaultValue: props.defaultPickerValue || mergedValue.value,
  132. postState: date => {
  133. const {
  134. generateConfig,
  135. showTime,
  136. defaultValue
  137. } = props;
  138. const now = generateConfig.getNow();
  139. if (!date) return now;
  140. // When value is null and set showTime
  141. if (!mergedValue.value && props.showTime) {
  142. if (typeof showTime === 'object') {
  143. return setDateTime(generateConfig, Array.isArray(date) ? date[0] : date, showTime.defaultValue || now);
  144. }
  145. if (defaultValue) {
  146. return setDateTime(generateConfig, Array.isArray(date) ? date[0] : date, defaultValue);
  147. }
  148. return setDateTime(generateConfig, Array.isArray(date) ? date[0] : date, now);
  149. }
  150. return date;
  151. }
  152. });
  153. const setViewDate = date => {
  154. setInnerViewDate(date);
  155. if (props.onPickerValueChange) {
  156. props.onPickerValueChange(date);
  157. }
  158. };
  159. // Panel control
  160. const getInternalNextMode = nextMode => {
  161. const getNextMode = PickerModeMap[props.picker];
  162. if (getNextMode) {
  163. return getNextMode(nextMode);
  164. }
  165. return nextMode;
  166. };
  167. // Save panel is changed from which panel
  168. const [mergedMode, setInnerMode] = useMergedState(() => {
  169. if (props.picker === 'time') {
  170. return 'time';
  171. }
  172. return getInternalNextMode('date');
  173. }, {
  174. value: toRef(props, 'mode')
  175. });
  176. watch(() => props.picker, () => {
  177. setInnerMode(props.picker);
  178. });
  179. const sourceMode = ref(mergedMode.value);
  180. const setSourceMode = val => {
  181. sourceMode.value = val;
  182. };
  183. const onInternalPanelChange = (newMode, viewValue) => {
  184. const {
  185. onPanelChange,
  186. generateConfig
  187. } = props;
  188. const nextMode = getInternalNextMode(newMode || mergedMode.value);
  189. setSourceMode(mergedMode.value);
  190. setInnerMode(nextMode);
  191. if (onPanelChange && (mergedMode.value !== nextMode || isEqual(generateConfig, viewDate.value, viewDate.value))) {
  192. onPanelChange(viewValue, nextMode);
  193. }
  194. };
  195. const triggerSelect = function (date, type) {
  196. let forceTriggerSelect = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  197. const {
  198. picker,
  199. generateConfig,
  200. onSelect,
  201. onChange,
  202. disabledDate
  203. } = props;
  204. if (mergedMode.value === picker || forceTriggerSelect) {
  205. setInnerValue(date);
  206. if (onSelect) {
  207. onSelect(date);
  208. }
  209. if (onContextSelect) {
  210. onContextSelect(date, type);
  211. }
  212. if (onChange && !isEqual(generateConfig, date, mergedValue.value) && !(disabledDate === null || disabledDate === void 0 ? void 0 : disabledDate(date))) {
  213. onChange(date);
  214. }
  215. }
  216. };
  217. // ========================= Interactive ==========================
  218. const onInternalKeydown = e => {
  219. if (panelRef.value && panelRef.value.onKeydown) {
  220. if ([KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP, KeyCode.DOWN, KeyCode.PAGE_UP, KeyCode.PAGE_DOWN, KeyCode.ENTER].includes(e.which)) {
  221. e.preventDefault();
  222. }
  223. return panelRef.value.onKeydown(e);
  224. }
  225. /* istanbul ignore next */
  226. /* eslint-disable no-lone-blocks */
  227. {
  228. warning(false, 'Panel not correct handle keyDown event. Please help to fire issue about this.');
  229. return false;
  230. }
  231. /* eslint-enable no-lone-blocks */
  232. };
  233. const onInternalBlur = e => {
  234. if (panelRef.value && panelRef.value.onBlur) {
  235. panelRef.value.onBlur(e);
  236. }
  237. };
  238. const onNow = () => {
  239. const {
  240. generateConfig,
  241. hourStep,
  242. minuteStep,
  243. secondStep
  244. } = props;
  245. const now = generateConfig.getNow();
  246. const lowerBoundTime = getLowerBoundTime(generateConfig.getHour(now), generateConfig.getMinute(now), generateConfig.getSecond(now), isHourStepValid.value ? hourStep : 1, isMinuteStepValid.value ? minuteStep : 1, isSecondStepValid.value ? secondStep : 1);
  247. const adjustedNow = setTime(generateConfig, now, lowerBoundTime[0],
  248. // hour
  249. lowerBoundTime[1],
  250. // minute
  251. lowerBoundTime[2]);
  252. triggerSelect(adjustedNow, 'submit');
  253. };
  254. const classString = computed(() => {
  255. const {
  256. prefixCls,
  257. direction
  258. } = props;
  259. return classNames(`${prefixCls}-panel`, {
  260. [`${prefixCls}-panel-has-range`]: rangedValue && rangedValue.value && rangedValue.value[0] && rangedValue.value[1],
  261. [`${prefixCls}-panel-has-range-hover`]: hoverRangedValue && hoverRangedValue.value && hoverRangedValue.value[0] && hoverRangedValue.value[1],
  262. [`${prefixCls}-panel-rtl`]: direction === 'rtl'
  263. });
  264. });
  265. useProvidePanel(_extends(_extends({}, panelContext), {
  266. mode: mergedMode,
  267. hideHeader: computed(() => {
  268. var _a;
  269. return props.hideHeader !== undefined ? props.hideHeader : (_a = panelContext.hideHeader) === null || _a === void 0 ? void 0 : _a.value;
  270. }),
  271. hidePrevBtn: computed(() => inRange.value && panelPosition.value === 'right'),
  272. hideNextBtn: computed(() => inRange.value && panelPosition.value === 'left')
  273. }));
  274. watch(() => props.value, () => {
  275. if (props.value) {
  276. setInnerViewDate(props.value);
  277. }
  278. });
  279. return () => {
  280. const {
  281. prefixCls = 'ant-picker',
  282. locale,
  283. generateConfig,
  284. disabledDate,
  285. picker = 'date',
  286. tabindex = 0,
  287. showNow,
  288. showTime,
  289. showToday,
  290. renderExtraFooter,
  291. onMousedown,
  292. onOk,
  293. components
  294. } = props;
  295. if (operationRef && panelPosition.value !== 'right') {
  296. operationRef.value = {
  297. onKeydown: onInternalKeydown,
  298. onClose: () => {
  299. if (panelRef.value && panelRef.value.onClose) {
  300. panelRef.value.onClose();
  301. }
  302. }
  303. };
  304. }
  305. // ============================ Panels ============================
  306. let panelNode;
  307. const pickerProps = _extends(_extends(_extends({}, attrs), props), {
  308. operationRef: panelRef,
  309. prefixCls,
  310. viewDate: viewDate.value,
  311. value: mergedValue.value,
  312. onViewDateChange: setViewDate,
  313. sourceMode: sourceMode.value,
  314. onPanelChange: onInternalPanelChange,
  315. disabledDate
  316. });
  317. delete pickerProps.onChange;
  318. delete pickerProps.onSelect;
  319. switch (mergedMode.value) {
  320. case 'decade':
  321. panelNode = _createVNode(DecadePanel, _objectSpread(_objectSpread({}, pickerProps), {}, {
  322. "onSelect": (date, type) => {
  323. setViewDate(date);
  324. triggerSelect(date, type);
  325. }
  326. }), null);
  327. break;
  328. case 'year':
  329. panelNode = _createVNode(YearPanel, _objectSpread(_objectSpread({}, pickerProps), {}, {
  330. "onSelect": (date, type) => {
  331. setViewDate(date);
  332. triggerSelect(date, type);
  333. }
  334. }), null);
  335. break;
  336. case 'month':
  337. panelNode = _createVNode(MonthPanel, _objectSpread(_objectSpread({}, pickerProps), {}, {
  338. "onSelect": (date, type) => {
  339. setViewDate(date);
  340. triggerSelect(date, type);
  341. }
  342. }), null);
  343. break;
  344. case 'quarter':
  345. panelNode = _createVNode(QuarterPanel, _objectSpread(_objectSpread({}, pickerProps), {}, {
  346. "onSelect": (date, type) => {
  347. setViewDate(date);
  348. triggerSelect(date, type);
  349. }
  350. }), null);
  351. break;
  352. case 'week':
  353. panelNode = _createVNode(WeekPanel, _objectSpread(_objectSpread({}, pickerProps), {}, {
  354. "onSelect": (date, type) => {
  355. setViewDate(date);
  356. triggerSelect(date, type);
  357. }
  358. }), null);
  359. break;
  360. case 'time':
  361. delete pickerProps.showTime;
  362. panelNode = _createVNode(TimePanel, _objectSpread(_objectSpread(_objectSpread({}, pickerProps), typeof showTime === 'object' ? showTime : null), {}, {
  363. "onSelect": (date, type) => {
  364. setViewDate(date);
  365. triggerSelect(date, type);
  366. }
  367. }), null);
  368. break;
  369. default:
  370. if (showTime) {
  371. panelNode = _createVNode(DatetimePanel, _objectSpread(_objectSpread({}, pickerProps), {}, {
  372. "onSelect": (date, type) => {
  373. setViewDate(date);
  374. triggerSelect(date, type);
  375. }
  376. }), null);
  377. } else {
  378. panelNode = _createVNode(DatePanel, _objectSpread(_objectSpread({}, pickerProps), {}, {
  379. "onSelect": (date, type) => {
  380. setViewDate(date);
  381. triggerSelect(date, type);
  382. }
  383. }), null);
  384. }
  385. }
  386. // ============================ Footer ============================
  387. let extraFooter;
  388. let rangesNode;
  389. if (!(hideRanges === null || hideRanges === void 0 ? void 0 : hideRanges.value)) {
  390. extraFooter = getExtraFooter(prefixCls, mergedMode.value, renderExtraFooter);
  391. rangesNode = getRanges({
  392. prefixCls,
  393. components,
  394. needConfirmButton: needConfirmButton.value,
  395. okDisabled: !mergedValue.value || disabledDate && disabledDate(mergedValue.value),
  396. locale,
  397. showNow,
  398. onNow: needConfirmButton.value && onNow,
  399. onOk: () => {
  400. if (mergedValue.value) {
  401. triggerSelect(mergedValue.value, 'submit', true);
  402. if (onOk) {
  403. onOk(mergedValue.value);
  404. }
  405. }
  406. }
  407. });
  408. }
  409. let todayNode;
  410. if (showToday && mergedMode.value === 'date' && picker === 'date' && !showTime) {
  411. const now = generateConfig.getNow();
  412. const todayCls = `${prefixCls}-today-btn`;
  413. const disabled = disabledDate && disabledDate(now);
  414. todayNode = _createVNode("a", {
  415. "class": classNames(todayCls, disabled && `${todayCls}-disabled`),
  416. "aria-disabled": disabled,
  417. "onClick": () => {
  418. if (!disabled) {
  419. triggerSelect(now, 'mouse', true);
  420. }
  421. }
  422. }, [locale.today]);
  423. }
  424. return _createVNode("div", {
  425. "tabindex": tabindex,
  426. "class": classNames(classString.value, attrs.class),
  427. "style": attrs.style,
  428. "onKeydown": onInternalKeydown,
  429. "onBlur": onInternalBlur,
  430. "onMousedown": onMousedown
  431. }, [panelNode, extraFooter || rangesNode || todayNode ? _createVNode("div", {
  432. "class": `${prefixCls}-footer`
  433. }, [extraFooter, rangesNode, todayNode]) : null]);
  434. };
  435. }
  436. });
  437. }
  438. const InterPickerPanel = PickerPanel();
  439. export default (props => createVNode(InterPickerPanel, props));