index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. exports.__esModule = true;
  4. exports.default = void 0;
  5. var _raf = require("../utils/dom/raf");
  6. var _date = require("../utils/validate/date");
  7. var _scroll = require("../utils/dom/scroll");
  8. var _utils = require("./utils");
  9. var _popup = _interopRequireDefault(require("../popup"));
  10. var _button = _interopRequireDefault(require("../button"));
  11. var _toast = _interopRequireDefault(require("../toast"));
  12. var _Month = _interopRequireDefault(require("./components/Month"));
  13. var _Header = _interopRequireDefault(require("./components/Header"));
  14. // Utils
  15. // Components
  16. var _default2 = (0, _utils.createComponent)({
  17. props: {
  18. title: String,
  19. color: String,
  20. value: Boolean,
  21. readonly: Boolean,
  22. formatter: Function,
  23. rowHeight: [Number, String],
  24. confirmText: String,
  25. rangePrompt: String,
  26. defaultDate: [Date, Array],
  27. getContainer: [String, Function],
  28. allowSameDay: Boolean,
  29. confirmDisabledText: String,
  30. type: {
  31. type: String,
  32. default: 'single'
  33. },
  34. round: {
  35. type: Boolean,
  36. default: true
  37. },
  38. position: {
  39. type: String,
  40. default: 'bottom'
  41. },
  42. poppable: {
  43. type: Boolean,
  44. default: true
  45. },
  46. maxRange: {
  47. type: [Number, String],
  48. default: null
  49. },
  50. lazyRender: {
  51. type: Boolean,
  52. default: true
  53. },
  54. showMark: {
  55. type: Boolean,
  56. default: true
  57. },
  58. showTitle: {
  59. type: Boolean,
  60. default: true
  61. },
  62. showConfirm: {
  63. type: Boolean,
  64. default: true
  65. },
  66. showSubtitle: {
  67. type: Boolean,
  68. default: true
  69. },
  70. closeOnPopstate: {
  71. type: Boolean,
  72. default: true
  73. },
  74. closeOnClickOverlay: {
  75. type: Boolean,
  76. default: true
  77. },
  78. safeAreaInsetBottom: {
  79. type: Boolean,
  80. default: true
  81. },
  82. minDate: {
  83. type: Date,
  84. validator: _date.isDate,
  85. default: function _default() {
  86. return new Date();
  87. }
  88. },
  89. maxDate: {
  90. type: Date,
  91. validator: _date.isDate,
  92. default: function _default() {
  93. var now = new Date();
  94. return new Date(now.getFullYear(), now.getMonth() + 6, now.getDate());
  95. }
  96. },
  97. firstDayOfWeek: {
  98. type: [Number, String],
  99. default: 0,
  100. validator: function validator(val) {
  101. return val >= 0 && val <= 6;
  102. }
  103. }
  104. },
  105. data: function data() {
  106. return {
  107. subtitle: '',
  108. currentDate: this.getInitialDate()
  109. };
  110. },
  111. computed: {
  112. months: function months() {
  113. var months = [];
  114. var cursor = new Date(this.minDate);
  115. cursor.setDate(1);
  116. do {
  117. months.push(new Date(cursor));
  118. cursor.setMonth(cursor.getMonth() + 1);
  119. } while ((0, _utils.compareMonth)(cursor, this.maxDate) !== 1);
  120. return months;
  121. },
  122. buttonDisabled: function buttonDisabled() {
  123. var type = this.type,
  124. currentDate = this.currentDate;
  125. if (currentDate) {
  126. if (type === 'range') {
  127. return !currentDate[0] || !currentDate[1];
  128. }
  129. if (type === 'multiple') {
  130. return !currentDate.length;
  131. }
  132. }
  133. return !currentDate;
  134. },
  135. dayOffset: function dayOffset() {
  136. return this.firstDayOfWeek ? this.firstDayOfWeek % 7 : 0;
  137. }
  138. },
  139. watch: {
  140. value: 'init',
  141. type: function type() {
  142. this.reset();
  143. },
  144. defaultDate: function defaultDate(val) {
  145. this.currentDate = val;
  146. this.scrollIntoView();
  147. }
  148. },
  149. mounted: function mounted() {
  150. this.init();
  151. },
  152. /* istanbul ignore next */
  153. activated: function activated() {
  154. this.init();
  155. },
  156. methods: {
  157. // @exposed-api
  158. reset: function reset(date) {
  159. if (date === void 0) {
  160. date = this.getInitialDate();
  161. }
  162. this.currentDate = date;
  163. this.scrollIntoView();
  164. },
  165. init: function init() {
  166. var _this = this;
  167. if (this.poppable && !this.value) {
  168. return;
  169. }
  170. this.$nextTick(function () {
  171. // add Math.floor to avoid decimal height issues
  172. // https://github.com/youzan/vant/issues/5640
  173. _this.bodyHeight = Math.floor(_this.$refs.body.getBoundingClientRect().height);
  174. _this.onScroll();
  175. _this.scrollIntoView();
  176. });
  177. },
  178. // @exposed-api
  179. scrollToDate: function scrollToDate(targetDate) {
  180. var _this2 = this;
  181. (0, _raf.raf)(function () {
  182. var displayed = _this2.value || !_this2.poppable;
  183. /* istanbul ignore if */
  184. if (!targetDate || !displayed) {
  185. return;
  186. }
  187. _this2.months.some(function (month, index) {
  188. if ((0, _utils.compareMonth)(month, targetDate) === 0) {
  189. var _this2$$refs = _this2.$refs,
  190. body = _this2$$refs.body,
  191. months = _this2$$refs.months;
  192. months[index].scrollIntoView(body);
  193. return true;
  194. }
  195. return false;
  196. });
  197. _this2.onScroll();
  198. });
  199. },
  200. // scroll to current month
  201. scrollIntoView: function scrollIntoView() {
  202. var currentDate = this.currentDate;
  203. if (currentDate) {
  204. var targetDate = this.type === 'single' ? currentDate : currentDate[0];
  205. this.scrollToDate(targetDate);
  206. }
  207. },
  208. getInitialDate: function getInitialDate() {
  209. var type = this.type,
  210. minDate = this.minDate,
  211. maxDate = this.maxDate,
  212. defaultDate = this.defaultDate;
  213. if (defaultDate === null) {
  214. return defaultDate;
  215. }
  216. var defaultVal = new Date();
  217. if ((0, _utils.compareDay)(defaultVal, minDate) === -1) {
  218. defaultVal = minDate;
  219. } else if ((0, _utils.compareDay)(defaultVal, maxDate) === 1) {
  220. defaultVal = maxDate;
  221. }
  222. if (type === 'range') {
  223. var _ref = defaultDate || [],
  224. startDay = _ref[0],
  225. endDay = _ref[1];
  226. return [startDay || defaultVal, endDay || (0, _utils.getNextDay)(defaultVal)];
  227. }
  228. if (type === 'multiple') {
  229. return defaultDate || [defaultVal];
  230. }
  231. return defaultDate || defaultVal;
  232. },
  233. // calculate the position of the elements
  234. // and find the elements that needs to be rendered
  235. onScroll: function onScroll() {
  236. var _this$$refs = this.$refs,
  237. body = _this$$refs.body,
  238. months = _this$$refs.months;
  239. var top = (0, _scroll.getScrollTop)(body);
  240. var bottom = top + this.bodyHeight;
  241. var heights = months.map(function (item) {
  242. return item.getHeight();
  243. });
  244. var heightSum = heights.reduce(function (a, b) {
  245. return a + b;
  246. }, 0); // iOS scroll bounce may exceed the range
  247. if (bottom > heightSum && top > 0) {
  248. return;
  249. }
  250. var height = 0;
  251. var currentMonth;
  252. var visibleRange = [-1, -1];
  253. for (var i = 0; i < months.length; i++) {
  254. var visible = height <= bottom && height + heights[i] >= top;
  255. if (visible) {
  256. visibleRange[1] = i;
  257. if (!currentMonth) {
  258. currentMonth = months[i];
  259. visibleRange[0] = i;
  260. }
  261. if (!months[i].showed) {
  262. months[i].showed = true;
  263. this.$emit('month-show', {
  264. date: months[i].date,
  265. title: months[i].title
  266. });
  267. }
  268. }
  269. height += heights[i];
  270. }
  271. months.forEach(function (month, index) {
  272. month.visible = index >= visibleRange[0] - 1 && index <= visibleRange[1] + 1;
  273. });
  274. /* istanbul ignore else */
  275. if (currentMonth) {
  276. this.subtitle = currentMonth.title;
  277. }
  278. },
  279. onClickDay: function onClickDay(item) {
  280. if (this.readonly) {
  281. return;
  282. }
  283. var date = item.date;
  284. var type = this.type,
  285. currentDate = this.currentDate;
  286. if (type === 'range') {
  287. if (!currentDate) {
  288. this.select([date, null]);
  289. return;
  290. }
  291. var startDay = currentDate[0],
  292. endDay = currentDate[1];
  293. if (startDay && !endDay) {
  294. var compareToStart = (0, _utils.compareDay)(date, startDay);
  295. if (compareToStart === 1) {
  296. this.select([startDay, date], true);
  297. } else if (compareToStart === -1) {
  298. this.select([date, null]);
  299. } else if (this.allowSameDay) {
  300. this.select([date, date], true);
  301. }
  302. } else {
  303. this.select([date, null]);
  304. }
  305. } else if (type === 'multiple') {
  306. if (!currentDate) {
  307. this.select([date]);
  308. return;
  309. }
  310. var selectedIndex;
  311. var selected = this.currentDate.some(function (dateItem, index) {
  312. var equal = (0, _utils.compareDay)(dateItem, date) === 0;
  313. if (equal) {
  314. selectedIndex = index;
  315. }
  316. return equal;
  317. });
  318. if (selected) {
  319. var _currentDate$splice = currentDate.splice(selectedIndex, 1),
  320. unselectedDate = _currentDate$splice[0];
  321. this.$emit('unselect', (0, _utils.copyDate)(unselectedDate));
  322. } else if (this.maxRange && currentDate.length >= this.maxRange) {
  323. (0, _toast.default)(this.rangePrompt || (0, _utils.t)('rangePrompt', this.maxRange));
  324. } else {
  325. this.select([].concat(currentDate, [date]));
  326. }
  327. } else {
  328. this.select(date, true);
  329. }
  330. },
  331. togglePopup: function togglePopup(val) {
  332. this.$emit('input', val);
  333. },
  334. select: function select(date, complete) {
  335. var _this3 = this;
  336. var emit = function emit(date) {
  337. _this3.currentDate = date;
  338. _this3.$emit('select', (0, _utils.copyDates)(_this3.currentDate));
  339. };
  340. if (complete && this.type === 'range') {
  341. var valid = this.checkRange(date);
  342. if (!valid) {
  343. // auto selected to max range if showConfirm
  344. if (this.showConfirm) {
  345. emit([date[0], (0, _utils.getDayByOffset)(date[0], this.maxRange - 1)]);
  346. } else {
  347. emit(date);
  348. }
  349. return;
  350. }
  351. }
  352. emit(date);
  353. if (complete && !this.showConfirm) {
  354. this.onConfirm();
  355. }
  356. },
  357. checkRange: function checkRange(date) {
  358. var maxRange = this.maxRange,
  359. rangePrompt = this.rangePrompt;
  360. if (maxRange && (0, _utils.calcDateNum)(date) > maxRange) {
  361. (0, _toast.default)(rangePrompt || (0, _utils.t)('rangePrompt', maxRange));
  362. return false;
  363. }
  364. return true;
  365. },
  366. onConfirm: function onConfirm() {
  367. this.$emit('confirm', (0, _utils.copyDates)(this.currentDate));
  368. },
  369. genMonth: function genMonth(date, index) {
  370. var h = this.$createElement;
  371. var showMonthTitle = index !== 0 || !this.showSubtitle;
  372. return h(_Month.default, {
  373. "ref": "months",
  374. "refInFor": true,
  375. "attrs": {
  376. "date": date,
  377. "type": this.type,
  378. "color": this.color,
  379. "minDate": this.minDate,
  380. "maxDate": this.maxDate,
  381. "showMark": this.showMark,
  382. "formatter": this.formatter,
  383. "rowHeight": this.rowHeight,
  384. "lazyRender": this.lazyRender,
  385. "currentDate": this.currentDate,
  386. "showSubtitle": this.showSubtitle,
  387. "allowSameDay": this.allowSameDay,
  388. "showMonthTitle": showMonthTitle,
  389. "firstDayOfWeek": this.dayOffset
  390. },
  391. "on": {
  392. "click": this.onClickDay
  393. }
  394. });
  395. },
  396. genFooterContent: function genFooterContent() {
  397. var h = this.$createElement;
  398. var slot = this.slots('footer');
  399. if (slot) {
  400. return slot;
  401. }
  402. if (this.showConfirm) {
  403. var text = this.buttonDisabled ? this.confirmDisabledText : this.confirmText;
  404. return h(_button.default, {
  405. "attrs": {
  406. "round": true,
  407. "block": true,
  408. "type": "danger",
  409. "color": this.color,
  410. "disabled": this.buttonDisabled,
  411. "nativeType": "button"
  412. },
  413. "class": (0, _utils.bem)('confirm'),
  414. "on": {
  415. "click": this.onConfirm
  416. }
  417. }, [text || (0, _utils.t)('confirm')]);
  418. }
  419. },
  420. genFooter: function genFooter() {
  421. var h = this.$createElement;
  422. return h("div", {
  423. "class": (0, _utils.bem)('footer', {
  424. unfit: !this.safeAreaInsetBottom
  425. })
  426. }, [this.genFooterContent()]);
  427. },
  428. genCalendar: function genCalendar() {
  429. var _this4 = this;
  430. var h = this.$createElement;
  431. return h("div", {
  432. "class": (0, _utils.bem)()
  433. }, [h(_Header.default, {
  434. "attrs": {
  435. "title": this.title,
  436. "showTitle": this.showTitle,
  437. "subtitle": this.subtitle,
  438. "showSubtitle": this.showSubtitle,
  439. "firstDayOfWeek": this.dayOffset
  440. },
  441. "scopedSlots": {
  442. title: function title() {
  443. return _this4.slots('title');
  444. }
  445. }
  446. }), h("div", {
  447. "ref": "body",
  448. "class": (0, _utils.bem)('body'),
  449. "on": {
  450. "scroll": this.onScroll
  451. }
  452. }, [this.months.map(this.genMonth)]), this.genFooter()]);
  453. }
  454. },
  455. render: function render() {
  456. var _this5 = this;
  457. var h = arguments[0];
  458. if (this.poppable) {
  459. var _attrs;
  460. var createListener = function createListener(name) {
  461. return function () {
  462. return _this5.$emit(name);
  463. };
  464. };
  465. return h(_popup.default, {
  466. "attrs": (_attrs = {
  467. "round": true,
  468. "value": this.value
  469. }, _attrs["round"] = this.round, _attrs["position"] = this.position, _attrs["closeable"] = this.showTitle || this.showSubtitle, _attrs["getContainer"] = this.getContainer, _attrs["closeOnPopstate"] = this.closeOnPopstate, _attrs["closeOnClickOverlay"] = this.closeOnClickOverlay, _attrs),
  470. "class": (0, _utils.bem)('popup'),
  471. "on": {
  472. "input": this.togglePopup,
  473. "open": createListener('open'),
  474. "opened": createListener('opened'),
  475. "close": createListener('close'),
  476. "closed": createListener('closed')
  477. }
  478. }, [this.genCalendar()]);
  479. }
  480. return this.genCalendar();
  481. }
  482. });
  483. exports.default = _default2;