RangePicker.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { resolveDirective as _resolveDirective, Fragment as _Fragment, createVNode as _createVNode } from "vue";
  4. import PickerTrigger from './PickerTrigger';
  5. import PickerPanel from './PickerPanel';
  6. import usePickerInput from './hooks/usePickerInput';
  7. import PresetPanel from './PresetPanel';
  8. import getDataOrAriaProps, { toArray, getValue, updateValues } from './utils/miscUtil';
  9. import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil';
  10. import { useProvidePanel } from './PanelContext';
  11. import { isEqual, getClosingViewDate, isSameDate, isSameWeek, isSameQuarter, formatValue, parseValue } from './utils/dateUtil';
  12. import useValueTexts from './hooks/useValueTexts';
  13. import useTextValueMapping from './hooks/useTextValueMapping';
  14. import usePresets from './hooks/usePresets';
  15. import { RangeContextProvider } from './RangeContext';
  16. import useRangeDisabled from './hooks/useRangeDisabled';
  17. import getExtraFooter from './utils/getExtraFooter';
  18. import getRanges from './utils/getRanges';
  19. import useRangeViewDates from './hooks/useRangeViewDates';
  20. import useHoverValue from './hooks/useHoverValue';
  21. import { computed, defineComponent, ref, toRef, watch, watchEffect } from 'vue';
  22. import useMergedState from '../_util/hooks/useMergedState';
  23. import { warning } from '../vc-util/warning';
  24. import useState from '../_util/hooks/useState';
  25. import classNames from '../_util/classNames';
  26. import { legacyPropsWarning } from './utils/warnUtil';
  27. import { useElementSize } from '../_util/hooks/_vueuse/useElementSize';
  28. function reorderValues(values, generateConfig) {
  29. if (values && values[0] && values[1] && generateConfig.isAfter(values[0], values[1])) {
  30. return [values[1], values[0]];
  31. }
  32. return values;
  33. }
  34. function canValueTrigger(value, index, disabled, allowEmpty) {
  35. if (value) {
  36. return true;
  37. }
  38. if (allowEmpty && allowEmpty[index]) {
  39. return true;
  40. }
  41. if (disabled[(index + 1) % 2]) {
  42. return true;
  43. }
  44. return false;
  45. }
  46. function RangerPicker() {
  47. return defineComponent({
  48. name: 'RangerPicker',
  49. inheritAttrs: false,
  50. props: ['prefixCls', 'id', 'popupStyle', 'dropdownClassName', 'transitionName', 'dropdownAlign', 'getPopupContainer', 'generateConfig', 'locale', 'placeholder', 'autofocus', 'disabled', 'format', 'picker', 'showTime', 'showNow', 'showHour', 'showMinute', 'showSecond', 'use12Hours', 'separator', 'value', 'defaultValue', 'defaultPickerValue', 'open', 'defaultOpen', 'disabledDate', 'disabledTime', 'dateRender', 'panelRender', 'ranges', 'allowEmpty', 'allowClear', 'suffixIcon', 'clearIcon', 'pickerRef', 'inputReadOnly', 'mode', 'renderExtraFooter', 'onChange', 'onOpenChange', 'onPanelChange', 'onCalendarChange', 'onFocus', 'onBlur', 'onMousedown', 'onMouseup', 'onMouseenter', 'onMouseleave', 'onClick', 'onOk', 'onKeydown', 'components', 'order', 'direction', 'activePickerIndex', 'autocomplete', 'minuteStep', 'hourStep', 'secondStep', 'hideDisabledOptions', 'disabledMinutes', 'presets', 'prevIcon', 'nextIcon', 'superPrevIcon', 'superNextIcon'],
  51. setup(props, _ref) {
  52. let {
  53. attrs,
  54. expose
  55. } = _ref;
  56. const needConfirmButton = computed(() => props.picker === 'date' && !!props.showTime || props.picker === 'time');
  57. const presets = computed(() => props.presets);
  58. const ranges = computed(() => props.ranges);
  59. const presetList = usePresets(presets, ranges);
  60. // We record oqqpened status here in case repeat open with picker
  61. const openRecordsRef = ref({});
  62. const containerRef = ref(null);
  63. const panelDivRef = ref(null);
  64. const startInputDivRef = ref(null);
  65. const endInputDivRef = ref(null);
  66. const separatorRef = ref(null);
  67. const startInputRef = ref(null);
  68. const endInputRef = ref(null);
  69. const arrowRef = ref(null);
  70. // ============================ Warning ============================
  71. if (process.env.NODE_ENV !== 'production') {
  72. legacyPropsWarning(props);
  73. }
  74. // ============================= Misc ==============================
  75. const formatList = computed(() => toArray(getDefaultFormat(props.format, props.picker, props.showTime, props.use12Hours)));
  76. // Active picker
  77. const [mergedActivePickerIndex, setMergedActivePickerIndex] = useMergedState(0, {
  78. value: toRef(props, 'activePickerIndex')
  79. });
  80. // Operation ref
  81. const operationRef = ref(null);
  82. const mergedDisabled = computed(() => {
  83. const {
  84. disabled
  85. } = props;
  86. if (Array.isArray(disabled)) {
  87. return disabled;
  88. }
  89. return [disabled || false, disabled || false];
  90. });
  91. // ============================= Value =============================
  92. const [mergedValue, setInnerValue] = useMergedState(null, {
  93. value: toRef(props, 'value'),
  94. defaultValue: props.defaultValue,
  95. postState: values => props.picker === 'time' && !props.order ? values : reorderValues(values, props.generateConfig)
  96. });
  97. // =========================== View Date ===========================
  98. // Config view panel
  99. const [startViewDate, endViewDate, setViewDate] = useRangeViewDates({
  100. values: mergedValue,
  101. picker: toRef(props, 'picker'),
  102. defaultDates: props.defaultPickerValue,
  103. generateConfig: toRef(props, 'generateConfig')
  104. });
  105. // ========================= Select Values =========================
  106. const [selectedValue, setSelectedValue] = useMergedState(mergedValue.value, {
  107. postState: values => {
  108. let postValues = values;
  109. if (mergedDisabled.value[0] && mergedDisabled.value[1]) {
  110. return postValues;
  111. }
  112. // Fill disabled unit
  113. for (let i = 0; i < 2; i += 1) {
  114. if (mergedDisabled.value[i] && !getValue(postValues, i) && !getValue(props.allowEmpty, i)) {
  115. postValues = updateValues(postValues, props.generateConfig.getNow(), i);
  116. }
  117. }
  118. return postValues;
  119. }
  120. });
  121. // ============================= Modes =============================
  122. const [mergedModes, setInnerModes] = useMergedState([props.picker, props.picker], {
  123. value: toRef(props, 'mode')
  124. });
  125. watch(() => props.picker, () => {
  126. setInnerModes([props.picker, props.picker]);
  127. });
  128. const triggerModesChange = (modes, values) => {
  129. var _a;
  130. setInnerModes(modes);
  131. (_a = props.onPanelChange) === null || _a === void 0 ? void 0 : _a.call(props, values, modes);
  132. };
  133. // ========================= Disable Date ==========================
  134. const [disabledStartDate, disabledEndDate] = useRangeDisabled({
  135. picker: toRef(props, 'picker'),
  136. selectedValue,
  137. locale: toRef(props, 'locale'),
  138. disabled: mergedDisabled,
  139. disabledDate: toRef(props, 'disabledDate'),
  140. generateConfig: toRef(props, 'generateConfig')
  141. }, openRecordsRef);
  142. // ============================= Open ==============================
  143. const [mergedOpen, triggerInnerOpen] = useMergedState(false, {
  144. value: toRef(props, 'open'),
  145. defaultValue: props.defaultOpen,
  146. postState: postOpen => mergedDisabled.value[mergedActivePickerIndex.value] ? false : postOpen,
  147. onChange: newOpen => {
  148. var _a;
  149. (_a = props.onOpenChange) === null || _a === void 0 ? void 0 : _a.call(props, newOpen);
  150. if (!newOpen && operationRef.value && operationRef.value.onClose) {
  151. operationRef.value.onClose();
  152. }
  153. }
  154. });
  155. const startOpen = computed(() => mergedOpen.value && mergedActivePickerIndex.value === 0);
  156. const endOpen = computed(() => mergedOpen.value && mergedActivePickerIndex.value === 1);
  157. const panelLeft = ref(0);
  158. const arrowLeft = ref(0);
  159. // ============================= Popup =============================
  160. // Popup min width
  161. const popupMinWidth = ref(0);
  162. const {
  163. width: containerWidth
  164. } = useElementSize(containerRef);
  165. watch([mergedOpen, containerWidth], () => {
  166. if (!mergedOpen.value && containerRef.value) {
  167. popupMinWidth.value = containerWidth.value;
  168. }
  169. });
  170. const {
  171. width: panelDivWidth
  172. } = useElementSize(panelDivRef);
  173. const {
  174. width: arrowWidth
  175. } = useElementSize(arrowRef);
  176. const {
  177. width: startInputDivWidth
  178. } = useElementSize(startInputDivRef);
  179. const {
  180. width: separatorWidth
  181. } = useElementSize(separatorRef);
  182. watch([mergedActivePickerIndex, mergedOpen, panelDivWidth, arrowWidth, startInputDivWidth, separatorWidth, () => props.direction], () => {
  183. arrowLeft.value = 0;
  184. if (mergedActivePickerIndex.value) {
  185. if (startInputDivRef.value && separatorRef.value) {
  186. arrowLeft.value = startInputDivWidth.value + separatorWidth.value;
  187. if (panelDivWidth.value && arrowWidth.value && arrowLeft.value > panelDivWidth.value - arrowWidth.value - (props.direction === 'rtl' || arrowRef.value.offsetLeft > arrowLeft.value ? 0 : arrowRef.value.offsetLeft)) {
  188. panelLeft.value = arrowLeft.value;
  189. }
  190. }
  191. } else if (mergedActivePickerIndex.value === 0) {
  192. panelLeft.value = 0;
  193. }
  194. }, {
  195. immediate: true
  196. });
  197. // ============================ Trigger ============================
  198. const triggerRef = ref();
  199. function triggerOpen(newOpen, index) {
  200. if (newOpen) {
  201. clearTimeout(triggerRef.value);
  202. openRecordsRef.value[index] = true;
  203. setMergedActivePickerIndex(index);
  204. triggerInnerOpen(newOpen);
  205. // Open to reset view date
  206. if (!mergedOpen.value) {
  207. setViewDate(null, index);
  208. }
  209. } else if (mergedActivePickerIndex.value === index) {
  210. triggerInnerOpen(newOpen);
  211. // Clean up async
  212. // This makes ref not quick refresh in case user open another input with blur trigger
  213. const openRecords = openRecordsRef.value;
  214. triggerRef.value = setTimeout(() => {
  215. if (openRecords === openRecordsRef.value) {
  216. openRecordsRef.value = {};
  217. }
  218. });
  219. }
  220. }
  221. function triggerOpenAndFocus(index) {
  222. triggerOpen(true, index);
  223. // Use setTimeout to make sure panel DOM exists
  224. setTimeout(() => {
  225. const inputRef = [startInputRef, endInputRef][index];
  226. if (inputRef.value) {
  227. inputRef.value.focus();
  228. }
  229. }, 0);
  230. }
  231. function triggerChange(newValue, sourceIndex) {
  232. let values = newValue;
  233. let startValue = getValue(values, 0);
  234. let endValue = getValue(values, 1);
  235. const {
  236. generateConfig,
  237. locale,
  238. picker,
  239. order,
  240. onCalendarChange,
  241. allowEmpty,
  242. onChange,
  243. showTime
  244. } = props;
  245. // >>>>> Format start & end values
  246. if (startValue && endValue && generateConfig.isAfter(startValue, endValue)) {
  247. if (
  248. // WeekPicker only compare week
  249. picker === 'week' && !isSameWeek(generateConfig, locale.locale, startValue, endValue) ||
  250. // QuotaPicker only compare week
  251. picker === 'quarter' && !isSameQuarter(generateConfig, startValue, endValue) ||
  252. // Other non-TimePicker compare date
  253. picker !== 'week' && picker !== 'quarter' && picker !== 'time' && !(showTime ? isEqual(generateConfig, startValue, endValue) : isSameDate(generateConfig, startValue, endValue))) {
  254. // Clean up end date when start date is after end date
  255. if (sourceIndex === 0) {
  256. values = [startValue, null];
  257. endValue = null;
  258. } else {
  259. startValue = null;
  260. values = [null, endValue];
  261. }
  262. // Clean up cache since invalidate
  263. openRecordsRef.value = {
  264. [sourceIndex]: true
  265. };
  266. } else if (picker !== 'time' || order !== false) {
  267. // Reorder when in same date
  268. values = reorderValues(values, generateConfig);
  269. }
  270. }
  271. setSelectedValue(values);
  272. const startStr = values && values[0] ? formatValue(values[0], {
  273. generateConfig,
  274. locale,
  275. format: formatList.value[0]
  276. }) : '';
  277. const endStr = values && values[1] ? formatValue(values[1], {
  278. generateConfig,
  279. locale,
  280. format: formatList.value[0]
  281. }) : '';
  282. if (onCalendarChange) {
  283. const info = {
  284. range: sourceIndex === 0 ? 'start' : 'end'
  285. };
  286. onCalendarChange(values, [startStr, endStr], info);
  287. }
  288. // >>>>> Trigger `onChange` event
  289. const canStartValueTrigger = canValueTrigger(startValue, 0, mergedDisabled.value, allowEmpty);
  290. const canEndValueTrigger = canValueTrigger(endValue, 1, mergedDisabled.value, allowEmpty);
  291. const canTrigger = values === null || canStartValueTrigger && canEndValueTrigger;
  292. if (canTrigger) {
  293. // Trigger onChange only when value is validate
  294. setInnerValue(values);
  295. if (onChange && (!isEqual(generateConfig, getValue(mergedValue.value, 0), startValue) || !isEqual(generateConfig, getValue(mergedValue.value, 1), endValue))) {
  296. onChange(values, [startStr, endStr]);
  297. }
  298. }
  299. // >>>>> Open picker when
  300. // Always open another picker if possible
  301. let nextOpenIndex = null;
  302. if (sourceIndex === 0 && !mergedDisabled.value[1]) {
  303. nextOpenIndex = 1;
  304. } else if (sourceIndex === 1 && !mergedDisabled.value[0]) {
  305. nextOpenIndex = 0;
  306. }
  307. if (nextOpenIndex !== null && nextOpenIndex !== mergedActivePickerIndex.value && (!openRecordsRef.value[nextOpenIndex] || !getValue(values, nextOpenIndex)) && getValue(values, sourceIndex)) {
  308. // Delay to focus to avoid input blur trigger expired selectedValues
  309. triggerOpenAndFocus(nextOpenIndex);
  310. } else {
  311. triggerOpen(false, sourceIndex);
  312. }
  313. }
  314. const forwardKeydown = e => {
  315. if (mergedOpen && operationRef.value && operationRef.value.onKeydown) {
  316. // Let popup panel handle keyboard
  317. return operationRef.value.onKeydown(e);
  318. }
  319. /* istanbul ignore next */
  320. /* eslint-disable no-lone-blocks */
  321. {
  322. warning(false, 'Picker not correct forward Keydown operation. Please help to fire issue about this.');
  323. return false;
  324. }
  325. };
  326. // ============================= Text ==============================
  327. const sharedTextHooksProps = {
  328. formatList,
  329. generateConfig: toRef(props, 'generateConfig'),
  330. locale: toRef(props, 'locale')
  331. };
  332. const [startValueTexts, firstStartValueText] = useValueTexts(computed(() => getValue(selectedValue.value, 0)), sharedTextHooksProps);
  333. const [endValueTexts, firstEndValueText] = useValueTexts(computed(() => getValue(selectedValue.value, 1)), sharedTextHooksProps);
  334. const onTextChange = (newText, index) => {
  335. const inputDate = parseValue(newText, {
  336. locale: props.locale,
  337. formatList: formatList.value,
  338. generateConfig: props.generateConfig
  339. });
  340. const disabledFunc = index === 0 ? disabledStartDate : disabledEndDate;
  341. if (inputDate && !disabledFunc(inputDate)) {
  342. setSelectedValue(updateValues(selectedValue.value, inputDate, index));
  343. setViewDate(inputDate, index);
  344. }
  345. };
  346. const [startText, triggerStartTextChange, resetStartText] = useTextValueMapping({
  347. valueTexts: startValueTexts,
  348. onTextChange: newText => onTextChange(newText, 0)
  349. });
  350. const [endText, triggerEndTextChange, resetEndText] = useTextValueMapping({
  351. valueTexts: endValueTexts,
  352. onTextChange: newText => onTextChange(newText, 1)
  353. });
  354. const [rangeHoverValue, setRangeHoverValue] = useState(null);
  355. // ========================== Hover Range ==========================
  356. const [hoverRangedValue, setHoverRangedValue] = useState(null);
  357. const [startHoverValue, onStartEnter, onStartLeave] = useHoverValue(startText, sharedTextHooksProps);
  358. const [endHoverValue, onEndEnter, onEndLeave] = useHoverValue(endText, sharedTextHooksProps);
  359. const onDateMouseenter = date => {
  360. setHoverRangedValue(updateValues(selectedValue.value, date, mergedActivePickerIndex.value));
  361. if (mergedActivePickerIndex.value === 0) {
  362. onStartEnter(date);
  363. } else {
  364. onEndEnter(date);
  365. }
  366. };
  367. const onDateMouseleave = () => {
  368. setHoverRangedValue(updateValues(selectedValue.value, null, mergedActivePickerIndex.value));
  369. if (mergedActivePickerIndex.value === 0) {
  370. onStartLeave();
  371. } else {
  372. onEndLeave();
  373. }
  374. };
  375. // ============================= Input =============================
  376. const getSharedInputHookProps = (index, resetText) => ({
  377. forwardKeydown,
  378. onBlur: e => {
  379. var _a;
  380. (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
  381. },
  382. isClickOutside: target => !elementsContains([panelDivRef.value, startInputDivRef.value, endInputDivRef.value, containerRef.value], target),
  383. onFocus: e => {
  384. var _a;
  385. setMergedActivePickerIndex(index);
  386. (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
  387. },
  388. triggerOpen: newOpen => {
  389. triggerOpen(newOpen, index);
  390. },
  391. onSubmit: () => {
  392. if (
  393. // When user typing disabledDate with keyboard and enter, this value will be empty
  394. !selectedValue.value ||
  395. // Normal disabled check
  396. props.disabledDate && props.disabledDate(selectedValue.value[index])) {
  397. return false;
  398. }
  399. triggerChange(selectedValue.value, index);
  400. resetText();
  401. },
  402. onCancel: () => {
  403. triggerOpen(false, index);
  404. setSelectedValue(mergedValue.value);
  405. resetText();
  406. }
  407. });
  408. const [startInputProps, {
  409. focused: startFocused,
  410. typing: startTyping
  411. }] = usePickerInput(_extends(_extends({}, getSharedInputHookProps(0, resetStartText)), {
  412. blurToCancel: needConfirmButton,
  413. open: startOpen,
  414. value: startText,
  415. onKeydown: (e, preventDefault) => {
  416. var _a;
  417. (_a = props.onKeydown) === null || _a === void 0 ? void 0 : _a.call(props, e, preventDefault);
  418. }
  419. }));
  420. const [endInputProps, {
  421. focused: endFocused,
  422. typing: endTyping
  423. }] = usePickerInput(_extends(_extends({}, getSharedInputHookProps(1, resetEndText)), {
  424. blurToCancel: needConfirmButton,
  425. open: endOpen,
  426. value: endText,
  427. onKeydown: (e, preventDefault) => {
  428. var _a;
  429. (_a = props.onKeydown) === null || _a === void 0 ? void 0 : _a.call(props, e, preventDefault);
  430. }
  431. }));
  432. // ========================== Click Picker ==========================
  433. const onPickerClick = e => {
  434. var _a;
  435. // When click inside the picker & outside the picker's input elements
  436. // the panel should still be opened
  437. (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, e);
  438. if (!mergedOpen.value && !startInputRef.value.contains(e.target) && !endInputRef.value.contains(e.target)) {
  439. if (!mergedDisabled.value[0]) {
  440. triggerOpenAndFocus(0);
  441. } else if (!mergedDisabled.value[1]) {
  442. triggerOpenAndFocus(1);
  443. }
  444. }
  445. };
  446. const onPickerMousedown = e => {
  447. var _a;
  448. // shouldn't affect input elements if picker is active
  449. (_a = props.onMousedown) === null || _a === void 0 ? void 0 : _a.call(props, e);
  450. if (mergedOpen.value && (startFocused.value || endFocused.value) && !startInputRef.value.contains(e.target) && !endInputRef.value.contains(e.target)) {
  451. e.preventDefault();
  452. }
  453. };
  454. // ============================= Sync ==============================
  455. // Close should sync back with text value
  456. const startStr = computed(() => {
  457. var _a;
  458. return ((_a = mergedValue.value) === null || _a === void 0 ? void 0 : _a[0]) ? formatValue(mergedValue.value[0], {
  459. locale: props.locale,
  460. format: 'YYYYMMDDHHmmss',
  461. generateConfig: props.generateConfig
  462. }) : '';
  463. });
  464. const endStr = computed(() => {
  465. var _a;
  466. return ((_a = mergedValue.value) === null || _a === void 0 ? void 0 : _a[1]) ? formatValue(mergedValue.value[1], {
  467. locale: props.locale,
  468. format: 'YYYYMMDDHHmmss',
  469. generateConfig: props.generateConfig
  470. }) : '';
  471. });
  472. watch([mergedOpen, startValueTexts, endValueTexts], () => {
  473. if (!mergedOpen.value) {
  474. setSelectedValue(mergedValue.value);
  475. if (!startValueTexts.value.length || startValueTexts.value[0] === '') {
  476. triggerStartTextChange('');
  477. } else if (firstStartValueText.value !== startText.value) {
  478. resetStartText();
  479. }
  480. if (!endValueTexts.value.length || endValueTexts.value[0] === '') {
  481. triggerEndTextChange('');
  482. } else if (firstEndValueText.value !== endText.value) {
  483. resetEndText();
  484. }
  485. }
  486. });
  487. // Sync innerValue with control mode
  488. watch([startStr, endStr], () => {
  489. setSelectedValue(mergedValue.value);
  490. });
  491. // ============================ Warning ============================
  492. if (process.env.NODE_ENV !== 'production') {
  493. watchEffect(() => {
  494. const {
  495. value,
  496. disabled
  497. } = props;
  498. if (value && Array.isArray(disabled) && (getValue(disabled, 0) && !getValue(value, 0) || getValue(disabled, 1) && !getValue(value, 1))) {
  499. warning(false, '`disabled` should not set with empty `value`. You should set `allowEmpty` or `value` instead.');
  500. }
  501. });
  502. }
  503. expose({
  504. focus: () => {
  505. if (startInputRef.value) {
  506. startInputRef.value.focus();
  507. }
  508. },
  509. blur: () => {
  510. if (startInputRef.value) {
  511. startInputRef.value.blur();
  512. }
  513. if (endInputRef.value) {
  514. endInputRef.value.blur();
  515. }
  516. }
  517. });
  518. // ============================= Panel =============================
  519. const panelHoverRangedValue = computed(() => {
  520. if (mergedOpen.value && hoverRangedValue.value && hoverRangedValue.value[0] && hoverRangedValue.value[1] && props.generateConfig.isAfter(hoverRangedValue.value[1], hoverRangedValue.value[0])) {
  521. return hoverRangedValue.value;
  522. } else {
  523. return null;
  524. }
  525. });
  526. function renderPanel() {
  527. let panelPosition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  528. let panelProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  529. const {
  530. generateConfig,
  531. showTime,
  532. dateRender,
  533. direction,
  534. disabledTime,
  535. prefixCls,
  536. locale
  537. } = props;
  538. let panelShowTime = showTime;
  539. if (showTime && typeof showTime === 'object' && showTime.defaultValue) {
  540. const timeDefaultValues = showTime.defaultValue;
  541. panelShowTime = _extends(_extends({}, showTime), {
  542. defaultValue: getValue(timeDefaultValues, mergedActivePickerIndex.value) || undefined
  543. });
  544. }
  545. let panelDateRender = null;
  546. if (dateRender) {
  547. panelDateRender = _ref2 => {
  548. let {
  549. current: date,
  550. today
  551. } = _ref2;
  552. return dateRender({
  553. current: date,
  554. today,
  555. info: {
  556. range: mergedActivePickerIndex.value ? 'end' : 'start'
  557. }
  558. });
  559. };
  560. }
  561. return _createVNode(RangeContextProvider, {
  562. "value": {
  563. inRange: true,
  564. panelPosition,
  565. rangedValue: rangeHoverValue.value || selectedValue.value,
  566. hoverRangedValue: panelHoverRangedValue.value
  567. }
  568. }, {
  569. default: () => [_createVNode(PickerPanel, _objectSpread(_objectSpread(_objectSpread({}, props), panelProps), {}, {
  570. "dateRender": panelDateRender,
  571. "showTime": panelShowTime,
  572. "mode": mergedModes.value[mergedActivePickerIndex.value],
  573. "generateConfig": generateConfig,
  574. "style": undefined,
  575. "direction": direction,
  576. "disabledDate": mergedActivePickerIndex.value === 0 ? disabledStartDate : disabledEndDate,
  577. "disabledTime": date => {
  578. if (disabledTime) {
  579. return disabledTime(date, mergedActivePickerIndex.value === 0 ? 'start' : 'end');
  580. }
  581. return false;
  582. },
  583. "class": classNames({
  584. [`${prefixCls}-panel-focused`]: mergedActivePickerIndex.value === 0 ? !startTyping.value : !endTyping.value
  585. }),
  586. "value": getValue(selectedValue.value, mergedActivePickerIndex.value),
  587. "locale": locale,
  588. "tabIndex": -1,
  589. "onPanelChange": (date, newMode) => {
  590. // clear hover value when panel change
  591. if (mergedActivePickerIndex.value === 0) {
  592. onStartLeave(true);
  593. }
  594. if (mergedActivePickerIndex.value === 1) {
  595. onEndLeave(true);
  596. }
  597. triggerModesChange(updateValues(mergedModes.value, newMode, mergedActivePickerIndex.value), updateValues(selectedValue.value, date, mergedActivePickerIndex.value));
  598. let viewDate = date;
  599. if (panelPosition === 'right' && mergedModes.value[mergedActivePickerIndex.value] === newMode) {
  600. viewDate = getClosingViewDate(viewDate, newMode, generateConfig, -1);
  601. }
  602. setViewDate(viewDate, mergedActivePickerIndex.value);
  603. },
  604. "onOk": null,
  605. "onSelect": undefined,
  606. "onChange": undefined,
  607. "defaultValue": mergedActivePickerIndex.value === 0 ? getValue(selectedValue.value, 1) : getValue(selectedValue.value, 0)
  608. }), null)]
  609. });
  610. }
  611. const onContextSelect = (date, type) => {
  612. const values = updateValues(selectedValue.value, date, mergedActivePickerIndex.value);
  613. if (type === 'submit' || type !== 'key' && !needConfirmButton.value) {
  614. // triggerChange will also update selected values
  615. triggerChange(values, mergedActivePickerIndex.value);
  616. // clear hover value style
  617. if (mergedActivePickerIndex.value === 0) {
  618. onStartLeave();
  619. } else {
  620. onEndLeave();
  621. }
  622. } else {
  623. setSelectedValue(values);
  624. }
  625. };
  626. useProvidePanel({
  627. operationRef,
  628. hideHeader: computed(() => props.picker === 'time'),
  629. onDateMouseenter,
  630. onDateMouseleave,
  631. hideRanges: computed(() => true),
  632. onSelect: onContextSelect,
  633. open: mergedOpen
  634. });
  635. return () => {
  636. const {
  637. prefixCls = 'rc-picker',
  638. id,
  639. popupStyle,
  640. dropdownClassName,
  641. transitionName,
  642. dropdownAlign,
  643. getPopupContainer,
  644. generateConfig,
  645. locale,
  646. placeholder,
  647. autofocus,
  648. picker = 'date',
  649. showTime,
  650. separator = '~',
  651. disabledDate,
  652. panelRender,
  653. allowClear,
  654. suffixIcon,
  655. clearIcon,
  656. inputReadOnly,
  657. renderExtraFooter,
  658. onMouseenter,
  659. onMouseleave,
  660. onMouseup,
  661. onOk,
  662. components,
  663. direction,
  664. autocomplete = 'off'
  665. } = props;
  666. const arrowPositionStyle = direction === 'rtl' ? {
  667. right: `${arrowLeft.value}px`
  668. } : {
  669. left: `${arrowLeft.value}px`
  670. };
  671. function renderPanels() {
  672. let panels;
  673. const extraNode = getExtraFooter(prefixCls, mergedModes.value[mergedActivePickerIndex.value], renderExtraFooter);
  674. const rangesNode = getRanges({
  675. prefixCls,
  676. components,
  677. needConfirmButton: needConfirmButton.value,
  678. okDisabled: !getValue(selectedValue.value, mergedActivePickerIndex.value) || disabledDate && disabledDate(selectedValue.value[mergedActivePickerIndex.value]),
  679. locale,
  680. onOk: () => {
  681. if (getValue(selectedValue.value, mergedActivePickerIndex.value)) {
  682. // triggerChangeOld(selectedValue.value);
  683. triggerChange(selectedValue.value, mergedActivePickerIndex.value);
  684. if (onOk) {
  685. onOk(selectedValue.value);
  686. }
  687. }
  688. }
  689. });
  690. if (picker !== 'time' && !showTime) {
  691. const viewDate = mergedActivePickerIndex.value === 0 ? startViewDate.value : endViewDate.value;
  692. const nextViewDate = getClosingViewDate(viewDate, picker, generateConfig);
  693. const currentMode = mergedModes.value[mergedActivePickerIndex.value];
  694. const showDoublePanel = currentMode === picker;
  695. const leftPanel = renderPanel(showDoublePanel ? 'left' : false, {
  696. pickerValue: viewDate,
  697. onPickerValueChange: newViewDate => {
  698. setViewDate(newViewDate, mergedActivePickerIndex.value);
  699. }
  700. });
  701. const rightPanel = renderPanel('right', {
  702. pickerValue: nextViewDate,
  703. onPickerValueChange: newViewDate => {
  704. setViewDate(getClosingViewDate(newViewDate, picker, generateConfig, -1), mergedActivePickerIndex.value);
  705. }
  706. });
  707. if (direction === 'rtl') {
  708. panels = _createVNode(_Fragment, null, [rightPanel, showDoublePanel && leftPanel]);
  709. } else {
  710. panels = _createVNode(_Fragment, null, [leftPanel, showDoublePanel && rightPanel]);
  711. }
  712. } else {
  713. panels = renderPanel();
  714. }
  715. let mergedNodes = _createVNode("div", {
  716. "class": `${prefixCls}-panel-layout`
  717. }, [_createVNode(PresetPanel, {
  718. "prefixCls": prefixCls,
  719. "presets": presetList.value,
  720. "onClick": nextValue => {
  721. triggerChange(nextValue, null);
  722. triggerOpen(false, mergedActivePickerIndex.value);
  723. },
  724. "onHover": hoverValue => {
  725. setRangeHoverValue(hoverValue);
  726. }
  727. }, null), _createVNode("div", null, [_createVNode("div", {
  728. "class": `${prefixCls}-panels`
  729. }, [panels]), (extraNode || rangesNode) && _createVNode("div", {
  730. "class": `${prefixCls}-footer`
  731. }, [extraNode, rangesNode])])]);
  732. if (panelRender) {
  733. mergedNodes = panelRender(mergedNodes);
  734. }
  735. return _createVNode("div", {
  736. "class": `${prefixCls}-panel-container`,
  737. "style": {
  738. marginLeft: `${panelLeft.value}px`
  739. },
  740. "ref": panelDivRef,
  741. "onMousedown": e => {
  742. e.preventDefault();
  743. }
  744. }, [mergedNodes]);
  745. }
  746. const rangePanel = _createVNode("div", {
  747. "class": classNames(`${prefixCls}-range-wrapper`, `${prefixCls}-${picker}-range-wrapper`),
  748. "style": {
  749. minWidth: `${popupMinWidth.value}px`
  750. }
  751. }, [_createVNode("div", {
  752. "ref": arrowRef,
  753. "class": `${prefixCls}-range-arrow`,
  754. "style": arrowPositionStyle
  755. }, null), renderPanels()]);
  756. // ============================= Icons =============================
  757. let suffixNode;
  758. if (suffixIcon) {
  759. suffixNode = _createVNode("span", {
  760. "class": `${prefixCls}-suffix`
  761. }, [suffixIcon]);
  762. }
  763. let clearNode;
  764. if (allowClear && (getValue(mergedValue.value, 0) && !mergedDisabled.value[0] || getValue(mergedValue.value, 1) && !mergedDisabled.value[1])) {
  765. clearNode = _createVNode("span", {
  766. "onMousedown": e => {
  767. e.preventDefault();
  768. e.stopPropagation();
  769. },
  770. "onMouseup": e => {
  771. e.preventDefault();
  772. e.stopPropagation();
  773. let values = mergedValue.value;
  774. if (!mergedDisabled.value[0]) {
  775. values = updateValues(values, null, 0);
  776. }
  777. if (!mergedDisabled.value[1]) {
  778. values = updateValues(values, null, 1);
  779. }
  780. triggerChange(values, null);
  781. triggerOpen(false, mergedActivePickerIndex.value);
  782. },
  783. "class": `${prefixCls}-clear`
  784. }, [clearIcon || _createVNode("span", {
  785. "class": `${prefixCls}-clear-btn`
  786. }, null)]);
  787. }
  788. const inputSharedProps = {
  789. size: getInputSize(picker, formatList.value[0], generateConfig)
  790. };
  791. let activeBarLeft = 0;
  792. let activeBarWidth = 0;
  793. if (startInputDivRef.value && endInputDivRef.value && separatorRef.value) {
  794. if (mergedActivePickerIndex.value === 0) {
  795. activeBarWidth = startInputDivRef.value.offsetWidth;
  796. } else {
  797. activeBarLeft = arrowLeft.value;
  798. activeBarWidth = endInputDivRef.value.offsetWidth;
  799. }
  800. }
  801. const activeBarPositionStyle = direction === 'rtl' ? {
  802. right: `${activeBarLeft}px`
  803. } : {
  804. left: `${activeBarLeft}px`
  805. };
  806. // ============================ Return =============================
  807. return _createVNode("div", _objectSpread({
  808. "ref": containerRef,
  809. "class": classNames(prefixCls, `${prefixCls}-range`, attrs.class, {
  810. [`${prefixCls}-disabled`]: mergedDisabled.value[0] && mergedDisabled.value[1],
  811. [`${prefixCls}-focused`]: mergedActivePickerIndex.value === 0 ? startFocused.value : endFocused.value,
  812. [`${prefixCls}-rtl`]: direction === 'rtl'
  813. }),
  814. "style": attrs.style,
  815. "onClick": onPickerClick,
  816. "onMouseenter": onMouseenter,
  817. "onMouseleave": onMouseleave,
  818. "onMousedown": onPickerMousedown,
  819. "onMouseup": onMouseup
  820. }, getDataOrAriaProps(props)), [_createVNode("div", {
  821. "class": classNames(`${prefixCls}-input`, {
  822. [`${prefixCls}-input-active`]: mergedActivePickerIndex.value === 0,
  823. [`${prefixCls}-input-placeholder`]: !!startHoverValue.value
  824. }),
  825. "ref": startInputDivRef
  826. }, [_createVNode("input", _objectSpread(_objectSpread(_objectSpread({
  827. "id": id,
  828. "disabled": mergedDisabled.value[0],
  829. "readonly": inputReadOnly || typeof formatList.value[0] === 'function' || !startTyping.value,
  830. "value": startHoverValue.value || startText.value,
  831. "onInput": e => {
  832. triggerStartTextChange(e.target.value);
  833. },
  834. "autofocus": autofocus,
  835. "placeholder": getValue(placeholder, 0) || '',
  836. "ref": startInputRef
  837. }, startInputProps.value), inputSharedProps), {}, {
  838. "autocomplete": autocomplete
  839. }), null)]), _createVNode("div", {
  840. "class": `${prefixCls}-range-separator`,
  841. "ref": separatorRef
  842. }, [separator]), _createVNode("div", {
  843. "class": classNames(`${prefixCls}-input`, {
  844. [`${prefixCls}-input-active`]: mergedActivePickerIndex.value === 1,
  845. [`${prefixCls}-input-placeholder`]: !!endHoverValue.value
  846. }),
  847. "ref": endInputDivRef
  848. }, [_createVNode("input", _objectSpread(_objectSpread(_objectSpread({
  849. "disabled": mergedDisabled.value[1],
  850. "readonly": inputReadOnly || typeof formatList.value[0] === 'function' || !endTyping.value,
  851. "value": endHoverValue.value || endText.value,
  852. "onInput": e => {
  853. triggerEndTextChange(e.target.value);
  854. },
  855. "placeholder": getValue(placeholder, 1) || '',
  856. "ref": endInputRef
  857. }, endInputProps.value), inputSharedProps), {}, {
  858. "autocomplete": autocomplete
  859. }), null)]), _createVNode("div", {
  860. "class": `${prefixCls}-active-bar`,
  861. "style": _extends(_extends({}, activeBarPositionStyle), {
  862. width: `${activeBarWidth}px`,
  863. position: 'absolute'
  864. })
  865. }, null), suffixNode, clearNode, _createVNode(PickerTrigger, {
  866. "visible": mergedOpen.value,
  867. "popupStyle": popupStyle,
  868. "prefixCls": prefixCls,
  869. "dropdownClassName": dropdownClassName,
  870. "dropdownAlign": dropdownAlign,
  871. "getPopupContainer": getPopupContainer,
  872. "transitionName": transitionName,
  873. "range": true,
  874. "direction": direction
  875. }, {
  876. default: () => [_createVNode("div", {
  877. "style": {
  878. pointerEvents: 'none',
  879. position: 'absolute',
  880. top: 0,
  881. bottom: 0,
  882. left: 0,
  883. right: 0
  884. }
  885. }, null)],
  886. popupElement: () => rangePanel
  887. })]);
  888. };
  889. }
  890. });
  891. }
  892. const InterRangerPicker = RangerPicker();
  893. export default InterRangerPicker;