From a5a41096dc96fd7af8fdee03a3c4b6e9524bc2ca Mon Sep 17 00:00:00 2001 From: mengxiong10 <15623530290@163.com> Date: Mon, 16 Nov 2020 11:30:42 +0800 Subject: [PATCH] refactor: remove date-fns && optimize code --- .../__snapshots__/calendar-panel.test.js.snap | 245 ----- .../__snapshots__/date-picker.test.js.snap | 145 ++- .../__snapshots__/table-date.test.js.snap | 958 ++++++++++-------- .../__snapshots__/table-month.test.js.snap | 256 +++-- .../__snapshots__/table-year.test.js.snap | 226 +++-- __test__/calendar-panel.test.js | 114 ++- __test__/calendar-range.test.js | 25 +- __test__/date-picker.test.js | 71 +- __test__/datetime-panel.test.js | 14 +- __test__/datetime-range.test.js | 21 +- __test__/locale.test.js | 17 +- __test__/table-date.test.js | 4 +- __test__/table-month.test.js | 3 +- __test__/table-year.test.js | 2 +- __test__/time-panel.test.js | 10 +- package-lock.json | 143 ++- package.json | 4 +- rollup.config.js | 2 +- src/calendar/calendar-panel.js | 259 +++++ src/calendar/calendar-panel.vue | 379 ------- src/calendar/calendar-range.js | 37 +- src/calendar/icon-button.vue | 22 + src/calendar/table-date.vue | 205 ++-- src/calendar/table-month.vue | 81 +- src/calendar/table-year.vue | 71 +- src/{date-picker.vue => date-picker.js} | 377 +++---- src/datetime/datetime-panel.js | 8 +- src/datetime/datetime-range.js | 6 +- src/icon/icon-calendar.vue | 2 +- src/icon/icon-close.vue | 2 +- src/index.js | 2 +- src/locale.js | 23 - src/mixin/emitter.js | 19 - src/{popup.js => popup.vue} | 45 +- src/time/list-options.vue | 8 +- src/time/time-panel.vue | 10 +- src/util/date.js | 55 + 37 files changed, 1908 insertions(+), 1963 deletions(-) delete mode 100644 __test__/__snapshots__/calendar-panel.test.js.snap create mode 100644 src/calendar/calendar-panel.js delete mode 100644 src/calendar/calendar-panel.vue create mode 100644 src/calendar/icon-button.vue rename src/{date-picker.vue => date-picker.js} (64%) delete mode 100644 src/mixin/emitter.js rename src/{popup.js => popup.vue} (75%) diff --git a/__test__/__snapshots__/calendar-panel.test.js.snap b/__test__/__snapshots__/calendar-panel.test.js.snap deleted file mode 100644 index 2e7b653..0000000 --- a/__test__/__snapshots__/calendar-panel.test.js.snap +++ /dev/null @@ -1,245 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CalendarPanel prop: type=date 1`] = ` -
-
- - - - - - - - - - - - -
- -
- - - - - -
-
-`; - -exports[`CalendarPanel prop: type=month 1`] = ` -
-
- - - - - - - - - - - -
- -
- - - - - -
-
-`; - -exports[`CalendarPanel prop: type=year 1`] = ` -
-
- - - - - - - - - - - 2010 - - - - - - 2019 - - -
- -
- - - - - -
-
-`; diff --git a/__test__/__snapshots__/date-picker.test.js.snap b/__test__/__snapshots__/date-picker.test.js.snap index 55eb737..bbfb117 100644 --- a/__test__/__snapshots__/date-picker.test.js.snap +++ b/__test__/__snapshots__/date-picker.test.js.snap @@ -14,33 +14,23 @@ exports[`DatePicker prop: attrs of input 1`] = ` placeholder="test placeholder" type="number" /> - - - - + - - -
- -
- -
@@ -60,33 +50,23 @@ exports[`DatePicker prop: clearable 1`] = ` placeholder="" type="text" /> - - - - + - - -
- -
- -
@@ -107,37 +87,28 @@ exports[`DatePicker prop: editable 1`] = ` readonly="readonly" type="text" /> - - + - - + - - -
- -
- -
@@ -185,13 +156,13 @@ exports[`DatePicker prop: formatter 1`] = ` class="mx-week-number" > - 29 - + 29 +
@@ -200,7 +171,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -209,7 +180,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -218,7 +189,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -227,7 +198,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -236,7 +207,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -245,7 +216,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -260,13 +231,13 @@ exports[`DatePicker prop: formatter 1`] = ` class="mx-week-number" > - 6 - + 6 +
@@ -275,7 +246,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -284,7 +255,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -293,7 +264,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -302,7 +273,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -311,7 +282,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -320,7 +291,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -335,13 +306,13 @@ exports[`DatePicker prop: formatter 1`] = ` class="mx-week-number" > - 13 - + 13 +
@@ -350,7 +321,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -359,7 +330,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -368,7 +339,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -377,7 +348,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -386,7 +357,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -395,7 +366,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -410,13 +381,13 @@ exports[`DatePicker prop: formatter 1`] = ` class="mx-week-number" > - 20 - + 20 +
@@ -425,7 +396,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -434,7 +405,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -443,7 +414,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -452,7 +423,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -461,7 +432,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -470,7 +441,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -485,13 +456,13 @@ exports[`DatePicker prop: formatter 1`] = ` class="mx-week-number" > - 27 - + 27 +
@@ -500,7 +471,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -509,7 +480,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -518,7 +489,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -527,7 +498,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -536,7 +507,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -545,7 +516,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -560,13 +531,13 @@ exports[`DatePicker prop: formatter 1`] = ` class="mx-week-number" > - 3 - + 3 +
@@ -575,7 +546,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -584,7 +555,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -593,7 +564,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -602,7 +573,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -611,7 +582,7 @@ exports[`DatePicker prop: formatter 1`] = `
@@ -620,7 +591,7 @@ exports[`DatePicker prop: formatter 1`] = `
diff --git a/__test__/__snapshots__/table-date.test.js.snap b/__test__/__snapshots__/table-date.test.js.snap index ff6ac81..8b6ac18 100644 --- a/__test__/__snapshots__/table-date.test.js.snap +++ b/__test__/__snapshots__/table-date.test.js.snap @@ -1,453 +1,521 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`TableDate corrent render 1`] = ` - - - - - - - - - - - - - - +
+ + + + + + + + + + + + +
- - +
- Su - - Mo - - Tu - - We - - Th - - Fr - - Sa -
- + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ Su + + Mo + + Tu + + We + + Th + + Fr + + Sa +
-
- 29 -
-
-
- 30 -
-
-
- 1 -
-
-
- 2 -
-
-
- 3 -
-
-
- 4 -
-
-
- 5 -
-
-
- 6 -
-
-
- 7 -
-
-
- 8 -
-
-
- 9 -
-
-
- 10 -
-
-
- 11 -
-
-
- 12 -
-
-
- 13 -
-
-
- 14 -
-
-
- 15 -
-
-
- 16 -
-
-
- 17 -
-
-
- 18 -
-
-
- 19 -
-
-
- 20 -
-
-
- 21 -
-
-
- 22 -
-
-
- 23 -
-
-
- 24 -
-
-
- 25 -
-
-
- 26 -
-
-
- 27 -
-
-
- 28 -
-
-
- 29 -
-
-
- 30 -
-
-
- 31 -
-
-
- 1 -
-
-
- 2 -
-
-
- 3 -
-
-
- 4 -
-
-
- 5 -
-
-
- 6 -
-
-
- 7 -
-
-
- 8 -
-
-
- 9 -
-
+ + + + + +
+ 29 +
+ + +
+ 30 +
+ + +
+ 1 +
+ + +
+ 2 +
+ + +
+ 3 +
+ + +
+ 4 +
+ + +
+ 5 +
+ + + + + + +
+ 6 +
+ + +
+ 7 +
+ + +
+ 8 +
+ + +
+ 9 +
+ + +
+ 10 +
+ + +
+ 11 +
+ + +
+ 12 +
+ + + + + + +
+ 13 +
+ + +
+ 14 +
+ + +
+ 15 +
+ + +
+ 16 +
+ + +
+ 17 +
+ + +
+ 18 +
+ + +
+ 19 +
+ + + + + + +
+ 20 +
+ + +
+ 21 +
+ + +
+ 22 +
+ + +
+ 23 +
+ + +
+ 24 +
+ + +
+ 25 +
+ + +
+ 26 +
+ + + + + + +
+ 27 +
+ + +
+ 28 +
+ + +
+ 29 +
+ + +
+ 30 +
+ + +
+ 31 +
+ + +
+ 1 +
+ + +
+ 2 +
+ + + + + + +
+ 3 +
+ + +
+ 4 +
+ + +
+ 5 +
+ + +
+ 6 +
+ + +
+ 7 +
+ + +
+ 8 +
+ + +
+ 9 +
+ + + + +
+
`; diff --git a/__test__/__snapshots__/table-month.test.js.snap b/__test__/__snapshots__/table-month.test.js.snap index 5660ddf..3524fc7 100644 --- a/__test__/__snapshots__/table-month.test.js.snap +++ b/__test__/__snapshots__/table-month.test.js.snap @@ -1,119 +1,155 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`TableMonth correct render 1`] = ` - - - - - - - - + + + + + + + + + + + + + + + + + + + +
+ + + + + + + -
- Mar -
-
+ + 2019 + + + + + +
+ -
- Apr -
- - - - - - - - - - - - - - -
-
- May -
-
-
- Jun -
-
-
- Jul -
-
-
- Aug -
-
-
- Sep -
-
-
- Oct -
-
-
- Nov -
-
-
- Dec -
-
+
+
+ Jan +
+
+
+ Feb +
+
+
+ Mar +
+
+
+ Apr +
+
+
+ May +
+
+
+ Jun +
+
+
+ Jul +
+
+
+ Aug +
+
+
+ Sep +
+
+
+ Oct +
+
+
+ Nov +
+
+
+ Dec +
+
+
+
`; diff --git a/__test__/__snapshots__/table-year.test.js.snap b/__test__/__snapshots__/table-year.test.js.snap index 9d08ebf..dda92aa 100644 --- a/__test__/__snapshots__/table-year.test.js.snap +++ b/__test__/__snapshots__/table-year.test.js.snap @@ -1,98 +1,144 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`TableYear decade=2010 1`] = ` - - - - - - - - - - - - - - - - - - - - - -
+ + + + + + 2010 - - -
- 2011 -
-
-
- 2012 -
-
-
- 2013 -
-
-
- 2014 -
-
-
- 2015 -
-
-
- 2016 -
-
-
- 2017 -
-
-
- 2018 -
-
-
+ + + + + 2019 -
-
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+
+ 2010 +
+
+
+ 2011 +
+
+
+ 2012 +
+
+
+ 2013 +
+
+
+ 2014 +
+
+
+ 2015 +
+
+
+ 2016 +
+
+
+ 2017 +
+
+
+ 2018 +
+
+
+ 2019 +
+
+
+
`; diff --git a/__test__/calendar-panel.test.js b/__test__/calendar-panel.test.js index bfa4ba0..b0c33d7 100644 --- a/__test__/calendar-panel.test.js +++ b/__test__/calendar-panel.test.js @@ -1,3 +1,4 @@ +/* eslint-disable no-await-in-loop */ import { mount, shallowMount } from '@vue/test-utils'; import CalendarPanel from '../src/calendar/calendar-panel'; @@ -14,8 +15,8 @@ describe('CalendarPanel', () => { value: new Date(2019, 9, 4), }, }); - const tds = wrapper.findAll('.mx-table-date td'); - tds.at(0).trigger('click'); + const td = wrapper.find('.mx-table-date td'); + td.trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual(new Date(2019, 8, 29)); }); @@ -26,8 +27,8 @@ describe('CalendarPanel', () => { defaultValue: new Date(2019, 9, 1), }, }); - const tds = wrapper.findAll('.mx-table-month td > div'); - tds.at(0).trigger('click'); + const td = wrapper.find('.mx-table-month td > div'); + td.trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual(new Date(2019, 0, 1)); }); @@ -38,8 +39,8 @@ describe('CalendarPanel', () => { defaultValue: new Date(2019, 9, 1), }, }); - const tds = wrapper.findAll('.mx-table-year td > div'); - tds.at(0).trigger('click'); + const td = wrapper.find('.mx-table-year td > div'); + td.trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual(new Date(2010, 0)); }); @@ -50,22 +51,37 @@ describe('CalendarPanel', () => { defaultValue: new Date().setFullYear(11), }, }); - const tds = wrapper.findAll('.mx-table-year td > div'); - tds.at(0).trigger('click'); + const td = wrapper.find('.mx-table-year td > div'); + td.trigger('click'); const expectedDate = new Date(10, 0).setFullYear(10); expect(wrapper.emitted().select[0][0].getTime()).toBe(expectedDate); }); - it('feat: active class', () => { + it('feat: active class', async () => { wrapper = mount(CalendarPanel); const td = wrapper.find('.mx-table-date td:nth-child(6)'); expect(td.classes()).not.toContain('active'); - wrapper.setProps({ value: new Date(2019, 9, 4) }); + await wrapper.setProps({ value: new Date(2019, 9, 4) }); expect(td.classes()).toContain('active'); }); - it('feat: click prev/next month', () => { - wrapper = shallowMount(CalendarPanel); + it('feat: panel change', async () => { + wrapper = mount(CalendarPanel); + const { vm } = wrapper; + await wrapper.find('.mx-btn-current-year').trigger('click'); + expect(vm.panel).toBe('year'); + await wrapper.find('.mx-table-year td > div').trigger('click'); + expect(vm.panel).toBe('month'); + await wrapper.find('.mx-table-month td > div').trigger('click'); + expect(vm.panel).toBe('date'); + await wrapper.find('.mx-btn-current-month').trigger('click'); + expect(vm.panel).toBe('month'); + await wrapper.find('.mx-calendar-header-label > button').trigger('click'); + expect(vm.panel).toBe('year'); + }); + + it('feat: click prev/next month', async () => { + wrapper = mount(CalendarPanel); const nextBtn = wrapper.find('.mx-btn-icon-right'); const lastBtn = wrapper.find('.mx-btn-icon-left'); @@ -74,7 +90,7 @@ describe('CalendarPanel', () => { while (count--) { const oldYear = vm.calendarYear; const oldMonth = vm.calendarMonth; - nextBtn.trigger('click'); + await nextBtn.trigger('click'); const newYear = vm.calendarYear; const newMonth = vm.calendarMonth; if (oldMonth === 11) { @@ -89,7 +105,7 @@ describe('CalendarPanel', () => { while (count--) { const oldYear = vm.calendarYear; const oldMonth = vm.calendarMonth; - lastBtn.trigger('click'); + await lastBtn.trigger('click'); const newYear = vm.calendarYear; const newMonth = vm.calendarMonth; if (oldMonth === 0) { @@ -102,33 +118,42 @@ describe('CalendarPanel', () => { } }); - it('feat: click prev/next year', () => { - wrapper = shallowMount(CalendarPanel, { + ['date', 'month'].forEach(type => { + it(`feat: click prev/next year in ${type} panel`, async () => { + wrapper = mount(CalendarPanel, { + propsData: { + value: new Date(2018, 4, 5), + defaultPanel: type, + }, + }); + const nextBtn = wrapper.find('.mx-btn-icon-double-right'); + const lastBtn = wrapper.find('.mx-btn-icon-double-left'); + const { vm } = wrapper; + const oldYear = vm.calendarYear; + expect(oldYear).toBe(2018); + await nextBtn.trigger('click'); + let newYear = vm.calendarYear; + expect(newYear).toBe(2019); + await lastBtn.trigger('click'); + newYear = vm.calendarYear; + expect(newYear).toBe(oldYear); + }); + }); + + it('feat: click prev/next decade', () => { + wrapper = mount(CalendarPanel, { propsData: { value: new Date(2018, 4, 5), + defaultPanel: 'year', }, }); const nextBtn = wrapper.find('.mx-btn-icon-double-right'); const lastBtn = wrapper.find('.mx-btn-icon-double-left'); - const yearBtn = wrapper.find('.mx-btn-current-year'); - const { vm } = wrapper; - const oldYear = vm.calendarYear; - expect(oldYear).toBe(2018); nextBtn.trigger('click'); - let newYear = vm.calendarYear; - expect(newYear).toBe(2019); - lastBtn.trigger('click'); - newYear = vm.calendarYear; - expect(newYear).toBe(oldYear); - // 年视图测试 - yearBtn.trigger('click'); - expect(vm.panel).toBe('year'); - expect(vm.calendarDecade).toBe(2010); - nextBtn.trigger('click'); - expect(vm.calendarDecade).toBe(2020); + expect(wrapper.vm.calendarYear).toBe(2028); lastBtn.trigger('click'); lastBtn.trigger('click'); - expect(vm.calendarDecade).toBe(2000); + expect(wrapper.vm.calendarYear).toBe(2008); }); const renderType = type => { @@ -139,11 +164,24 @@ describe('CalendarPanel', () => { value: new Date(2019, 9, 1, 10), }, }); - expect(wrapper.element).toMatchSnapshot(); + expect(wrapper.vm.panel).toBe(type); }); }; ['date', 'month', 'year'].forEach(renderType); + it('feat: select year to change the calendar', async () => { + wrapper = mount(CalendarPanel, { + propsData: { + value: new Date(2018, 4, 5), + defaultPanel: 'year', + }, + }); + await wrapper.find('.mx-table-year td > div').trigger('click'); + expect(wrapper.vm.calendarYear).toBe(2010); + await wrapper.find('.mx-table-month td > div').trigger('click'); + expect(wrapper.vm.calendarMonth).toBe(0); + }); + it('prop: disabledDate', () => { const disabledDate = date => { return date < new Date(2019, 9, 1) || date > new Date(2019, 9, 20); @@ -168,11 +206,12 @@ describe('CalendarPanel', () => { expect(wrapper.emitted().select).toBeUndefined(); }); - it('prop: partialUpdate', () => { + it('prop: partialUpdate', async () => { wrapper = mount(CalendarPanel, { propsData: { value: new Date(2019, 9, 4), partialUpdate: true, + defaultPanel: 'year', }, }); wrapper @@ -180,11 +219,8 @@ describe('CalendarPanel', () => { .at(0) .trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual(new Date(2010, 9, 4)); - wrapper.setProps({ value: new Date(2010, 9, 4) }); - wrapper - .findAll('.mx-table-month td > div') - .at(0) - .trigger('click'); + await wrapper.setProps({ value: new Date(2010, 9, 4) }); + wrapper.find('.mx-table-month td > div').trigger('click'); expect(wrapper.emitted().select[1][0]).toEqual(new Date(2010, 0, 4)); }); diff --git a/__test__/calendar-range.test.js b/__test__/calendar-range.test.js index 533b297..d45fb84 100644 --- a/__test__/calendar-range.test.js +++ b/__test__/calendar-range.test.js @@ -1,5 +1,4 @@ import { mount } from '@vue/test-utils'; -import flushPromises from 'flush-promises'; import CalendarRange from '../src/calendar/calendar-range'; import CalendarPanel from '../src/calendar/calendar-panel'; @@ -12,7 +11,6 @@ afterEach(() => { describe('CalendarRange', () => { it('feat: correct classes', () => { wrapper = mount(CalendarRange, { - sync: false, propsData: { value: [new Date(2019, 9, 30), new Date(2019, 10, 2)], }, @@ -29,38 +27,31 @@ describe('CalendarRange', () => { it('feat: click range', async () => { wrapper = mount(CalendarRange, { - sync: false, propsData: { defaultValue: new Date(2019, 9, 1), }, }); expect(wrapper.vm.calendars).toEqual([new Date(2019, 9, 1), new Date(2019, 10, 1)]); const tds = wrapper.findAll('.mx-table-date td'); - tds.at(2).trigger('click'); - await flushPromises(); + await tds.at(2).trigger('click'); expect(wrapper.emitted().select).toBeUndefined(); - tds.at(8).trigger('click'); - await flushPromises(); + await tds.at(8).trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual([new Date(2019, 9, 1), new Date(2019, 9, 7)]); }); it('feat: calendars min gap', async () => { wrapper = mount(CalendarRange, { - sync: false, propsData: { defaultValue: new Date(2019, 6, 1), }, }); - const panels = wrapper.findAll(CalendarPanel); - const startPanel = panels.at(0); - const endPanel = panels.at(1); + const firstRightIcon = wrapper.find('.mx-calendar-panel-date .mx-btn-icon-right'); + const secondLeftIcon = wrapper.find('.mx-calendar-panel-date:nth-child(2) .mx-btn-icon-left'); - startPanel.find('.mx-btn-icon-right').trigger('click'); - await flushPromises(); + await firstRightIcon.trigger('click'); expect(wrapper.vm.calendars).toEqual([new Date(2019, 7, 1), new Date(2019, 8, 1)]); - endPanel.find('.mx-btn-icon-left').trigger('click'); - await flushPromises(); + await secondLeftIcon.trigger('click'); expect(wrapper.vm.calendars).toEqual([new Date(2019, 6, 1), new Date(2019, 7, 1)]); }); @@ -70,7 +61,7 @@ describe('CalendarRange', () => { partialUpdate: true, }, }); - const panels = wrapper.findAll(CalendarPanel); + const panels = wrapper.findAllComponents(CalendarPanel); const startPanel = panels.at(0); const endPanel = panels.at(1); expect(startPanel.vm.partialUpdate).toBe(false); @@ -83,7 +74,7 @@ describe('CalendarRange', () => { defaultValue: [new Date(2019, 9, 1), new Date(2019, 11, 1)], }, }); - const panels = wrapper.findAll(CalendarPanel); + const panels = wrapper.findAllComponents(CalendarPanel); const startPanel = panels.at(0); const endPanel = panels.at(1); expect(startPanel.vm.calendarMonth).toBe(9); diff --git a/__test__/date-picker.test.js b/__test__/date-picker.test.js index 30cf6e0..d5edc50 100644 --- a/__test__/date-picker.test.js +++ b/__test__/date-picker.test.js @@ -1,6 +1,7 @@ import { shallowMount, createWrapper, mount } from '@vue/test-utils'; import { format, parse } from 'date-fns'; -import DatePicker from '../src/date-picker.vue'; +import Popup from '../src/popup'; +import DatePicker from '../src/date-picker'; let wrapper; @@ -9,28 +10,28 @@ afterEach(() => { }); describe('DatePicker', () => { - it('feat: open and close the popup', () => { + it('feat: open and close the popup', async () => { wrapper = mount(DatePicker, { - attachToDocument: true, + attachTo: document.body, }); expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(false); // expect click input should show the popup const input = wrapper.find('input'); - input.trigger('mousedown'); + await input.trigger('mousedown'); expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(true); // expect click out side should hide the popup const bodyWrapper = createWrapper(document.body); - bodyWrapper.trigger('mousedown'); + await bodyWrapper.trigger('mousedown'); expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(false); // expect focus input should show the popop - input.trigger('focus'); + await input.trigger('focus'); expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(true); // expoce keydown tab should hide the popup - input.trigger('keydown.tab'); + await input.trigger('keydown.tab'); expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(false); }); - it('feat: should init panel and calendar when reopen', () => { + it('feat: should init panel and calendar when reopen', async () => { wrapper = mount(DatePicker, { propsData: { defaultValue: new Date(2019, 9, 1), @@ -38,15 +39,15 @@ describe('DatePicker', () => { }, }); const yearBtn = wrapper.find('.mx-btn-current-year'); - yearBtn.trigger('click'); + await yearBtn.trigger('click'); // change to year panel expect(wrapper.find('.mx-calendar-panel-year').exists()).toBe(true); - wrapper.setProps({ open: false }); - wrapper.setProps({ open: true }); + await wrapper.setProps({ open: false }); + await wrapper.setProps({ open: true }); expect(wrapper.find('.mx-calendar-panel-year').exists()).toBe(false); }); - it('prop: open', () => { + it('prop: open', async () => { wrapper = mount(DatePicker, { propsData: { open: false, @@ -55,7 +56,7 @@ describe('DatePicker', () => { const { vm } = wrapper; vm.openPopup(); expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(false); - wrapper.setProps({ open: true }); + await wrapper.setProps({ open: true }); expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(true); vm.closePopup(); expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(true); @@ -161,7 +162,7 @@ describe('DatePicker', () => { expect(tableDate.element).toMatchSnapshot(); }); - it('prop: valueType', () => { + it('prop: valueType', async () => { const value = new Date(2019, 9, 20); const emitValue = new Date(2019, 9, 22); wrapper = shallowMount(DatePicker, { @@ -173,13 +174,13 @@ describe('DatePicker', () => { const { vm } = wrapper; expect(vm.text).toBe('2019/10/20'); vm.emitValue(emitValue); - wrapper.setProps({ valueType: 'format', value: '2019/10/20' }); + await wrapper.setProps({ valueType: 'format', value: '2019/10/20' }); expect(vm.text).toBe('2019/10/20'); vm.emitValue(emitValue); - wrapper.setProps({ valueType: 'timestamp', value: value.getTime() }); + await wrapper.setProps({ valueType: 'timestamp', value: value.getTime() }); expect(vm.text).toBe('2019/10/20'); vm.emitValue(emitValue); - wrapper.setProps({ valueType: 'DD/MM/YYYY', value: '20/10/2019' }); + await wrapper.setProps({ valueType: 'DD/MM/YYYY', value: '20/10/2019' }); expect(vm.text).toBe('2019/10/20'); vm.emitValue(emitValue); const emitted = wrapper.emitted(); @@ -191,12 +192,13 @@ describe('DatePicker', () => { ]); }); - it('prop: shortcut', () => { + it('prop: shortcut', async () => { const date = new Date(2019, 4, 10); wrapper = shallowMount(DatePicker, { propsData: { open: true, valueType: 'YYYY/MM/DD', + range: false, shortcuts: [ { text: 'Today', @@ -207,12 +209,11 @@ describe('DatePicker', () => { ], }, }); - const btn = wrapper.find('.mx-btn-shortcut'); - btn.trigger('click'); + await btn.trigger('click'); const emitted = wrapper.emitted(); expect(emitted.input).toEqual([['2019/05/10']]); - wrapper.setProps({ + await wrapper.setProps({ range: true, shortcuts: [ { @@ -234,7 +235,7 @@ describe('DatePicker', () => { popupClass: 'test', }, }); - const popup = wrapper.find('.mx-datepicker-popup'); + const popup = wrapper.findComponent(Popup); expect(popup.classes()).toContain('test'); }); @@ -247,25 +248,25 @@ describe('DatePicker', () => { }, }, }); - const popup = wrapper.find('.mx-datepicker-popup'); + const popup = wrapper.findComponent(Popup); expect(popup.element.style.color).toBe('red'); }); - it('prop: confirm', () => { + it('prop: confirm', async () => { wrapper = shallowMount(DatePicker, { propsData: { confirm: true, }, }); const { vm } = wrapper; - vm.openPopup(); + await wrapper.find('input').trigger('focus'); const btn = wrapper.find('.mx-datepicker-btn-confirm'); expect(btn.exists()).toBe(true); // click the date expect popup don't close vm.handleSelectDate(new Date(2018, 5, 5)); expect(wrapper.emitted().input).toBeUndefined(); expect(vm.popupVisible).toBe(true); - btn.trigger('click'); + await btn.trigger('click'); expect(wrapper.emitted().input[0][0]).toEqual(new Date(2018, 5, 5)); expect(vm.popupVisible).toBe(false); }); @@ -291,7 +292,7 @@ describe('DatePicker', () => { }, }); - const popup = wrapper.find('.mx-datepicker-popup'); + const popup = wrapper.findComponent(Popup); expect(popup.element.parentNode).toBe(document.body); }); @@ -366,7 +367,7 @@ describe('DatePicker', () => { expect(wrapper.emitted().pick[0][0]).toEqual(new Date(2019, 8, 29)); }); - it('Ignore whitespace around separator on manual range input', () => { + it('Ignore whitespace around separator on manual range input', async () => { const rangeSeparator = ' ~ '; const text = '2020-02-12'; wrapper = mount(DatePicker, { @@ -378,13 +379,13 @@ describe('DatePicker', () => { }); const input = wrapper.find('input'); - input.setValue(`${text} ${rangeSeparator} ${text}`); - input.trigger('change'); - input.setValue(`${text}${rangeSeparator.trim()}${text}`); - input.trigger('change'); - wrapper.setProps({ rangeSeparator: ' - ' }); - input.setValue(`${text} - ${text}`); - input.trigger('change'); + await input.setValue(`${text} ${rangeSeparator} ${text}`); + await input.trigger('change'); + await input.setValue(`${text}${rangeSeparator.trim()}${text}`); + await input.trigger('change'); + await wrapper.setProps({ rangeSeparator: ' - ' }); + await input.setValue(`${text} - ${text}`); + await input.trigger('change'); expect(wrapper.emitted().input).toEqual([[[text, text]], [[text, text]], [[text, text]]]); }); diff --git a/__test__/datetime-panel.test.js b/__test__/datetime-panel.test.js index c783953..820eb08 100644 --- a/__test__/datetime-panel.test.js +++ b/__test__/datetime-panel.test.js @@ -8,7 +8,7 @@ afterEach(() => { }); describe('DatetimePanel', () => { - it('feat: click date', () => { + it('feat: click date', async () => { wrapper = mount(DatetimePanel, { propsData: { type: 'datetime', @@ -16,16 +16,16 @@ describe('DatetimePanel', () => { }, }); const td = wrapper.find('.mx-table-date td:nth-child(4)'); - td.trigger('click'); + await td.trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual(new Date(2019, 9, 2, 12)); let timeTitle = wrapper.find('.mx-time-header-title'); expect(timeTitle.exists()).toBe(true); - timeTitle.trigger('click'); + await timeTitle.trigger('click'); timeTitle = wrapper.find('.mx-time-header-title'); expect(timeTitle.exists()).toBe(false); }); - it('feat: disabled time', () => { + it('feat: disabled time', async () => { const disabledDate = date => date < new Date(2019, 9, 2); const disabledTime = date => date < new Date(2019, 9, 2, 12); wrapper = mount(DatetimePanel, { @@ -36,14 +36,14 @@ describe('DatetimePanel', () => { }, }); const td = wrapper.find('.mx-table-date td:nth-child(4)'); - td.trigger('click'); + await td.trigger('click'); expect(wrapper.emitted().select).toBeUndefined(); const timeTitle = wrapper.find('.mx-time-header-title'); expect(timeTitle.text()).toBe('2019-10-02'); // set the defaultValue is not disabled const defaultValue = new Date(2019, 9, 2, 12); - wrapper.setProps({ defaultValue }); - td.trigger('click'); + await wrapper.setProps({ defaultValue }); + await td.trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual(defaultValue); }); }); diff --git a/__test__/datetime-range.test.js b/__test__/datetime-range.test.js index 93cb268..30c9e09 100644 --- a/__test__/datetime-range.test.js +++ b/__test__/datetime-range.test.js @@ -1,5 +1,4 @@ import { mount } from '@vue/test-utils'; -import flushPromises from 'flush-promises'; import DatetimeRange from '../src/datetime/datetime-range'; let wrapper; @@ -11,7 +10,6 @@ afterEach(() => { describe('DatetimeRange', () => { it('feat: click dates', async () => { wrapper = mount(DatetimeRange, { - sync: false, propsData: { type: 'datetime', value: [new Date(2019, 9, 4, 18), new Date(2019, 9, 6, 12)], @@ -20,21 +18,18 @@ describe('DatetimeRange', () => { const td = wrapper.find('.mx-table-date td:nth-child(4)'); const td2 = wrapper.find('.mx-table-date td:nth-child(5)'); td.trigger('click'); - td2.trigger('click'); - await flushPromises(); + await td2.trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual([ new Date(2019, 9, 2, 18), new Date(2019, 9, 3, 12), ]); let timeTitle = wrapper.find('.mx-time-header-title'); expect(timeTitle.exists()).toBe(true); - timeTitle.trigger('click'); - await flushPromises(); + await timeTitle.trigger('click'); timeTitle = wrapper.find('.mx-time-header-title'); expect(timeTitle.exists()).toBe(false); td.trigger('click'); - td.trigger('click'); - await flushPromises(); + await td.trigger('click'); expect(wrapper.emitted().select[1][0]).toEqual([ new Date(2019, 9, 2, 18), new Date(2019, 9, 2, 18), @@ -54,16 +49,14 @@ describe('DatetimeRange', () => { }); const td = wrapper.find('.mx-table-date td:nth-child(4)'); td.trigger('click'); - td.trigger('click'); - await flushPromises(); + await td.trigger('click'); expect(wrapper.emitted().select).toBeUndefined(); const timeTitle = wrapper.find('.mx-time-header-title'); expect(timeTitle.text()).toBe('2019-10-02'); const defaultValue = [new Date(2019, 9, 2, 12), new Date(2019, 9, 2, 12)]; - wrapper.setProps({ defaultValue }); - td.trigger('click'); - td.trigger('click'); - await flushPromises(); + await wrapper.setProps({ defaultValue }); + await td.trigger('click'); + await td.trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual(defaultValue); }); }); diff --git a/__test__/locale.test.js b/__test__/locale.test.js index ecd2eda..16637db 100644 --- a/__test__/locale.test.js +++ b/__test__/locale.test.js @@ -1,5 +1,6 @@ import { mount } from '@vue/test-utils'; -import DatePicker from '../src/date-picker.vue'; +import DatePicker from '../src/date-picker'; +import Calendar from '../src/calendar/calendar-panel'; import '../src/locale/zh-cn'; let wrapper; @@ -20,7 +21,7 @@ describe('Locale', () => { expect(wrapper.find('.mx-table-date td').element.title).toBe('2019-09-30'); }); - it('prop: lang - string', () => { + it('prop: lang - string', async () => { wrapper = mount(DatePicker, { propsData: { value: new Date(2019, 9, 10), @@ -30,14 +31,18 @@ describe('Locale', () => { }, }); expect(wrapper.find('.mx-table-date th').text()).toBe('Su'); - expect(wrapper.find('.mx-table-month td').text()).toBe('Jan'); - expect(wrapper.find('.mx-btn-current-month').text()).toBe('Oct'); expect(wrapper.find('.mx-table-date .active').element.title).toBe('Oct 10, 2019'); + expect(wrapper.find('.mx-btn-current-month').text()).toBe('Oct'); + await wrapper.findComponent(Calendar).setData({ panel: 'month' }); + // expect(wrapper.vm.panel).toBe('month'); + expect(wrapper.find('.mx-table-month td').text()).toBe('Jan'); wrapper.setProps({ lang: 'zh-cn' }); + await wrapper.findComponent(Calendar).setData({ panel: 'date' }); expect(wrapper.find('.mx-table-date th').text()).toBe('一'); - expect(wrapper.find('.mx-table-month td').text()).toBe('1月'); - expect(wrapper.find('.mx-btn-current-month').text()).toBe('10月'); expect(wrapper.find('.mx-table-date .active').element.title).toBe('10月 10, 2019'); + expect(wrapper.find('.mx-btn-current-month').text()).toBe('10月'); + await wrapper.findComponent(Calendar).setData({ panel: 'month' }); + expect(wrapper.find('.mx-table-month td').text()).toBe('1月'); }); it('prop: lang - object', () => { diff --git a/__test__/table-date.test.js b/__test__/table-date.test.js index b91e27c..03e4ad7 100644 --- a/__test__/table-date.test.js +++ b/__test__/table-date.test.js @@ -11,9 +11,7 @@ describe('TableDate', () => { it('corrent render', () => { wrapper = mount(TableDate, { propsData: { - calendarYear: 2019, - calendarMonth: 9, - firstDayOfWeek: 1, + calendar: new Date(2019, 9, 1, 0, 0, 0), titleFormat: 'DD/MM/YYYY', }, }); diff --git a/__test__/table-month.test.js b/__test__/table-month.test.js index a86801b..8083fe5 100644 --- a/__test__/table-month.test.js +++ b/__test__/table-month.test.js @@ -11,7 +11,8 @@ describe('TableMonth', () => { it('correct render', () => { wrapper = mount(TableMonth, { propsData: { - getClasses: month => { + calendar: new Date(2019, 9, 1, 0, 0, 0), + getCellClasses: month => { if (month === 9) { return 'active'; } diff --git a/__test__/table-year.test.js b/__test__/table-year.test.js index 704814d..a465831 100644 --- a/__test__/table-year.test.js +++ b/__test__/table-year.test.js @@ -11,7 +11,7 @@ describe('TableYear', () => { it('decade=2010', () => { wrapper = mount(TableYear, { propsData: { - decade: 2010, + calendar: new Date(2019, 9, 1, 0, 0, 0), }, }); expect(wrapper.element).toMatchSnapshot(); diff --git a/__test__/time-panel.test.js b/__test__/time-panel.test.js index 4a1a2c0..7f65d44 100644 --- a/__test__/time-panel.test.js +++ b/__test__/time-panel.test.js @@ -62,7 +62,7 @@ describe('TimePanel', () => { expect(wrapper.element).toMatchSnapshot(); }); - it('feat: emit select event when click', () => { + it('feat: emit select event when click', async () => { wrapper = mount(TimePanel, { propsData: { format: 'hh:mm:ss a', @@ -72,15 +72,15 @@ describe('TimePanel', () => { const hour = wrapper.find('[data-type=hour] li:nth-child(2)'); hour.trigger('click'); expect(wrapper.emitted().select[0][0]).toEqual(new Date(2019, 9, 10, 1)); - wrapper.setProps({ value: new Date(2019, 9, 10, 1) }); + await wrapper.setProps({ value: new Date(2019, 9, 10, 1) }); const minute = wrapper.find('[data-type=minute] li:nth-child(2)'); minute.trigger('click'); expect(wrapper.emitted().select[1][0]).toEqual(new Date(2019, 9, 10, 1, 1)); - wrapper.setProps({ value: new Date(2019, 9, 10, 1, 1) }); + await wrapper.setProps({ value: new Date(2019, 9, 10, 1, 1) }); const second = wrapper.find('[data-type=second] li:nth-child(2)'); second.trigger('click'); expect(wrapper.emitted().select[2][0]).toEqual(new Date(2019, 9, 10, 1, 1, 1)); - wrapper.setProps({ value: new Date(2019, 9, 10, 1, 1, 1) }); + await wrapper.setProps({ value: new Date(2019, 9, 10, 1, 1, 1) }); const pm = wrapper.find('[data-type=ampm] li:nth-child(2)'); pm.trigger('click'); expect(wrapper.emitted().select[3][0]).toEqual(new Date(2019, 9, 10, 13, 1, 1)); @@ -105,7 +105,7 @@ describe('TimePanel', () => { format: {}, }, }); - const cols = wrapper.find(ListColumns); + const cols = wrapper.findComponent(ListColumns); expect(cols.props('showHour')).toBe(true); expect(cols.props('showMinute')).toBe(true); expect(cols.props('showSecond')).toBe(true); diff --git a/package-lock.json b/package-lock.json index ad61ceb..2d744b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1800,7 +1800,8 @@ "version": "7.0.7", "resolved": "https://registry.npm.taobao.org/@types/babel-types/download/@types/babel-types-7.0.7.tgz", "integrity": "sha1-Zn6xZA6AOUNgKAVXN9K5mG7jNuM=", - "dev": true + "dev": true, + "optional": true }, "@types/babel__core": { "version": "7.1.3", @@ -1848,6 +1849,7 @@ "resolved": "https://registry.npm.taobao.org/@types/babylon/download/@types/babylon-6.16.5.tgz", "integrity": "sha1-HFZB22nrjN83jt0ltL53VL7rSLQ=", "dev": true, + "optional": true, "requires": { "@types/babel-types": "*" } @@ -2116,13 +2118,14 @@ } }, "@vue/test-utils": { - "version": "1.0.0-beta.29", - "resolved": "https://registry.npm.taobao.org/@vue/test-utils/download/@vue/test-utils-1.0.0-beta.29.tgz", - "integrity": "sha1-yULPJeiRzwgbagMzK0rh70MHJvA=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.1.1.tgz", + "integrity": "sha512-/32538ilZ9qSiu1gui7zfBn+IFy+zoTaQTZ1qiLfQXzZtaeAD23kJMrnqaoe2w8JzJoXuqHUl2ruuStG8rwFYQ==", "dev": true, "requires": { "dom-event-types": "^1.0.0", - "lodash": "^4.17.4" + "lodash": "^4.17.15", + "pretty": "^2.0.0" } }, "JSONStream": { @@ -2221,9 +2224,10 @@ }, "align-text": { "version": "0.1.4", - "resolved": "http://registry.npm.taobao.org/align-text/download/align-text-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -2235,6 +2239,7 @@ "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -2927,7 +2932,7 @@ }, "big.js": { "version": "3.2.0", - "resolved": "http://registry.npm.taobao.org/big.js/download/big.js-3.2.0.tgz", + "resolved": "https://registry.npm.taobao.org/big.js/download/big.js-3.2.0.tgz", "integrity": "sha1-pfwpi4G54Nyi5FiCR4S2XFK6WI4=", "dev": true }, @@ -3305,7 +3310,7 @@ }, "center-align": { "version": "0.1.3", - "resolved": "http://registry.npm.taobao.org/center-align/download/center-align-0.1.3.tgz", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "optional": true, @@ -3758,6 +3763,7 @@ "resolved": "https://registry.npm.taobao.org/constantinople/download/constantinople-3.1.2.tgz", "integrity": "sha1-1F7XJPV9PRBQABen06iJwTga5kc=", "dev": true, + "optional": true, "requires": { "@types/babel-types": "^7.0.0", "@types/babylon": "^6.16.2", @@ -4729,9 +4735,10 @@ } }, "date-fns": { - "version": "2.7.0", - "resolved": "https://registry.npm.taobao.org/date-fns/download/date-fns-2.7.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdate-fns%2Fdownload%2Fdate-fns-2.7.0.tgz", - "integrity": "sha1-gnHZQ8xGNqHydpjxuNap8c63QCY=" + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", + "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==", + "dev": true }, "date-format-parse": { "version": "0.2.5", @@ -4994,8 +5001,8 @@ }, "dom-event-types": { "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/dom-event-types/download/dom-event-types-1.0.0.tgz", - "integrity": "sha1-WDCgop4b+Df+UKcM2ApZcjKBPK4=", + "resolved": "https://registry.npmjs.org/dom-event-types/-/dom-event-types-1.0.0.tgz", + "integrity": "sha512-2G2Vwi2zXTHBGqXHsJ4+ak/iP0N8Ar+G8a7LiD2oup5o4sQWytwqqrZu/O6hIMV0KMID2PL69OhpshLO0n7UJQ==", "dev": true }, "dom-serializer": { @@ -5155,7 +5162,7 @@ }, "emojis-list": { "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/emojis-list/download/emojis-list-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", "dev": true }, @@ -5188,7 +5195,7 @@ }, "errno": { "version": "0.1.7", - "resolved": "http://registry.npm.taobao.org/errno/download/errno-0.1.7.tgz", + "resolved": "https://registry.npm.taobao.org/errno/download/errno-0.1.7.tgz", "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", "dev": true, "optional": true, @@ -6145,7 +6152,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6166,12 +6174,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6186,17 +6196,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -6313,7 +6326,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -6325,6 +6339,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6339,6 +6354,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6346,12 +6362,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6370,6 +6388,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -6450,7 +6469,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6462,6 +6482,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6547,7 +6568,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6583,6 +6605,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6602,6 +6625,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6645,12 +6669,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -7304,7 +7330,7 @@ }, "html-tags": { "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/html-tags/download/html-tags-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", "dev": true }, @@ -7554,7 +7580,7 @@ }, "image-size": { "version": "0.5.5", - "resolved": "https://registry.npm.taobao.org/image-size/download/image-size-0.5.5.tgz", + "resolved": "https://registry.npm.taobao.org/image-size/download/image-size-0.5.5.tgz?cache=0&sync_timestamp=1569841767280&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fimage-size%2Fdownload%2Fimage-size-0.5.5.tgz", "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", "dev": true, "optional": true @@ -7883,7 +7909,7 @@ "dependencies": { "acorn": { "version": "4.0.13", - "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-4.0.13.tgz?cache=0&sync_timestamp=1583824513691&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn%2Fdownload%2Facorn-4.0.13.tgz", + "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-4.0.13.tgz", "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", "dev": true, "optional": true @@ -8724,7 +8750,8 @@ "version": "1.0.2", "resolved": "https://registry.npm.taobao.org/js-stringify/download/js-stringify-1.0.2.tgz", "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=", - "dev": true + "dev": true, + "optional": true }, "js-tokens": { "version": "4.0.0", @@ -8895,7 +8922,7 @@ }, "lazy-cache": { "version": "1.0.4", - "resolved": "http://registry.npm.taobao.org/lazy-cache/download/lazy-cache-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true, "optional": true @@ -9317,7 +9344,7 @@ }, "loader-utils": { "version": "0.2.17", - "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-0.2.17.tgz?cache=0&sync_timestamp=1584445611428&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-utils%2Fdownload%2Floader-utils-0.2.17.tgz", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { @@ -9371,7 +9398,7 @@ }, "lodash.kebabcase": { "version": "4.1.1", - "resolved": "https://registry.npm.taobao.org/lodash.kebabcase/download/lodash.kebabcase-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", "dev": true }, @@ -9523,9 +9550,10 @@ }, "longest": { "version": "1.0.1", - "resolved": "http://registry.npm.taobao.org/longest/download/longest-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true + "dev": true, + "optional": true }, "loose-envify": { "version": "1.4.0", @@ -11444,7 +11472,7 @@ }, "postcss": { "version": "5.2.18", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz?cache=0&sync_timestamp=1581994853208&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss%2Fdownload%2Fpostcss-5.2.18.tgz", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", "dev": true, "requires": { @@ -11456,7 +11484,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz?cache=0&sync_timestamp=1573280518303&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -11803,7 +11831,7 @@ }, "promise": { "version": "7.3.1", - "resolved": "https://registry.npm.taobao.org/promise/download/promise-7.3.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpromise%2Fdownload%2Fpromise-7.3.1.tgz", + "resolved": "https://registry.npm.taobao.org/promise/download/promise-7.3.1.tgz", "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", "dev": true, "optional": true, @@ -11829,7 +11857,7 @@ }, "prr": { "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "dev": true, "optional": true @@ -11910,7 +11938,8 @@ "version": "1.3.3", "resolved": "https://registry.npm.taobao.org/pug-error/download/pug-error-1.3.3.tgz", "integrity": "sha1-80L7AIdS1YA0wYXeA2At2f/hX6Y=", - "dev": true + "dev": true, + "optional": true }, "pug-filters": { "version": "3.1.1", @@ -11930,7 +11959,7 @@ "dependencies": { "camelcase": { "version": "1.2.1", - "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-1.2.1.tgz?cache=0&sync_timestamp=1586229901005&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcamelcase%2Fdownload%2Fcamelcase-1.2.1.tgz", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-1.2.1.tgz", "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", "dev": true, "optional": true @@ -11949,7 +11978,7 @@ }, "uglify-js": { "version": "2.8.29", - "resolved": "https://registry.npm.taobao.org/uglify-js/download/uglify-js-2.8.29.tgz", + "resolved": "https://registry.npm.taobao.org/uglify-js/download/uglify-js-2.8.29.tgz?cache=0&sync_timestamp=1573031889241&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuglify-js%2Fdownload%2Fuglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "optional": true, @@ -11961,14 +11990,14 @@ }, "wordwrap": { "version": "0.0.2", - "resolved": "http://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.2.tgz", + "resolved": "https://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.2.tgz", "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", "dev": true, "optional": true }, "yargs": { "version": "3.10.0", - "resolved": "https://registry.npm.taobao.org/yargs/download/yargs-3.10.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs%2Fdownload%2Fyargs-3.10.0.tgz", + "resolved": "https://registry.npm.taobao.org/yargs/download/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "optional": true, @@ -12030,7 +12059,8 @@ "version": "2.0.5", "resolved": "https://registry.npm.taobao.org/pug-runtime/download/pug-runtime-2.0.5.tgz", "integrity": "sha1-baeXbDa/IvaOczw1kkDYrnoylTo=", - "dev": true + "dev": true, + "optional": true }, "pug-strip-comments": { "version": "1.0.4", @@ -12046,7 +12076,8 @@ "version": "1.1.8", "resolved": "https://registry.npm.taobao.org/pug-walk/download/pug-walk-1.1.8.tgz", "integrity": "sha1-tAj2fyeRL4wh2i9FtyMMS9Kl6no=", - "dev": true + "dev": true, + "optional": true }, "pump": { "version": "3.0.0", @@ -12622,7 +12653,7 @@ }, "right-align": { "version": "0.1.3", - "resolved": "http://registry.npm.taobao.org/right-align/download/right-align-0.1.3.tgz", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "optional": true, @@ -13685,7 +13716,7 @@ }, "stylus": { "version": "0.54.7", - "resolved": "https://registry.npm.taobao.org/stylus/download/stylus-0.54.7.tgz", + "resolved": "https://registry.npm.taobao.org/stylus/download/stylus-0.54.7.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstylus%2Fdownload%2Fstylus-0.54.7.tgz", "integrity": "sha1-xs5Hk5Ze5Ti86+UPMVN7/ATYjNI=", "dev": true, "optional": true, @@ -13702,7 +13733,7 @@ "dependencies": { "debug": { "version": "3.1.0", - "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "optional": true, @@ -13726,7 +13757,7 @@ }, "source-map": { "version": "0.7.3", - "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.7.3.tgz", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.7.3.tgz", "integrity": "sha1-UwL4FpAxc1ImVECS5kmB91F1A4M=", "dev": true, "optional": true @@ -13744,7 +13775,7 @@ }, "svg-tags": { "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/svg-tags/download/svg-tags-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", "dev": true }, @@ -14178,7 +14209,7 @@ }, "uglify-to-browserify": { "version": "1.0.2", - "resolved": "http://registry.npm.taobao.org/uglify-to-browserify/download/uglify-to-browserify-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "dev": true, "optional": true @@ -14723,7 +14754,7 @@ }, "window-size": { "version": "0.1.0", - "resolved": "http://registry.npm.taobao.org/window-size/download/window-size-0.1.0.tgz", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "dev": true, "optional": true @@ -14741,14 +14772,14 @@ "dependencies": { "acorn": { "version": "3.3.0", - "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-3.3.0.tgz?cache=0&sync_timestamp=1583824513691&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn%2Fdownload%2Facorn-3.3.0.tgz", + "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-3.3.0.tgz", "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", "dev": true, "optional": true }, "acorn-globals": { "version": "3.1.0", - "resolved": "https://registry.npm.taobao.org/acorn-globals/download/acorn-globals-3.1.0.tgz?cache=0&sync_timestamp=1583643398087&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn-globals%2Fdownload%2Facorn-globals-3.1.0.tgz", + "resolved": "https://registry.npm.taobao.org/acorn-globals/download/acorn-globals-3.1.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn-globals%2Fdownload%2Facorn-globals-3.1.0.tgz", "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", "dev": true, "optional": true, @@ -14758,7 +14789,7 @@ "dependencies": { "acorn": { "version": "4.0.13", - "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-4.0.13.tgz?cache=0&sync_timestamp=1583824513691&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn%2Fdownload%2Facorn-4.0.13.tgz", + "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-4.0.13.tgz", "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", "dev": true, "optional": true diff --git a/package.json b/package.json index 7530a1f..8e7e719 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "vue": "^2.5.0" }, "dependencies": { - "date-fns": "^2.0.1", "date-format-parse": "^0.2.5" }, "devDependencies": { @@ -62,7 +61,7 @@ "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", "@vue/babel-preset-jsx": "^1.1.2", "@vue/component-compiler-utils": "^3.0.0", - "@vue/test-utils": "^1.0.0-beta.29", + "@vue/test-utils": "^1.1.1", "acorn": "^7.0.0", "autoprefixer": "^9.7.1", "babel-core": "^7.0.0-bridge.0", @@ -72,6 +71,7 @@ "core-js": "^3.3.5", "coveralls": "^3.0.7", "cross-env": "^6.0.3", + "date-fns": "^2.16.1", "eslint": "^6.2.2", "eslint-config-airbnb-base": "^14.0.0", "eslint-config-prettier": "^6.1.0", diff --git a/rollup.config.js b/rollup.config.js index 5c05736..a5b7d89 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -5,7 +5,7 @@ import vue from 'rollup-plugin-vue'; import { terser } from 'rollup-plugin-terser'; import pkg from './package.json'; -const external = id => /date-fns/.test(id); +const external = Object.keys(pkg.dependencies); const input = 'src/index.js'; diff --git a/src/calendar/calendar-panel.js b/src/calendar/calendar-panel.js new file mode 100644 index 0000000..b15465f --- /dev/null +++ b/src/calendar/calendar-panel.js @@ -0,0 +1,259 @@ +import { + getValidDate, + isValidDate, + createDate, + setMonth, + startOfYear, + startOfMonth, + startOfDay, +} from '../util/date'; +import TableDate from './table-date'; +import TableMonth from './table-month'; +import TableYear from './table-year'; + +export default { + name: 'CalendarPanel', + inject: { + prefixClass: { + default: 'mx', + }, + dispatchDatePicker: { + default: () => () => {}, + }, + }, + props: { + value: {}, + defaultValue: { + default() { + const date = new Date(); + date.setHours(0, 0, 0, 0); + return date; + }, + }, + defaultPanel: { + type: String, + }, + disabledDate: { + type: Function, + default: () => false, + }, + type: { + type: String, + default: 'date', + }, + getClasses: { + type: Function, + default: () => [], + }, + showWeekNumber: { + type: Boolean, + default: undefined, + }, + titleFormat: { + type: String, + default: 'YYYY-MM-DD', + }, + calendar: Date, + // update date when select year or month + partialUpdate: { + type: Boolean, + default: false, + }, + }, + data() { + const panels = ['date', 'month', 'year']; + const index = Math.max(panels.indexOf(this.type), panels.indexOf(this.defaultPanel)); + const panel = index !== -1 ? panels[index] : 'date'; + return { + panel, + innerCalendar: new Date(), + }; + }, + computed: { + innerValue() { + const value = Array.isArray(this.value) ? this.value : [this.value]; + const map = { + year: startOfYear, + month: startOfMonth, + date: startOfDay, + }; + const start = map[this.type] || map.date; + return value.filter(isValidDate).map(v => start(v)); + }, + calendarYear() { + return this.innerCalendar.getFullYear(); + }, + calendarMonth() { + return this.innerCalendar.getMonth(); + }, + }, + watch: { + value: { + immediate: true, + handler: 'initCalendar', + }, + calendar: { + handler: 'initCalendar', + }, + defaultValue: { + handler: 'initCalendar', + }, + }, + methods: { + initCalendar() { + let calendarDate = this.calendar; + if (!isValidDate(calendarDate)) { + const { length } = this.innerValue; + calendarDate = getValidDate(length > 0 ? this.innerValue[length - 1] : this.defaultValue); + } + this.innerCalendar = startOfMonth(calendarDate); + }, + isDisabled(date) { + return this.disabledDate(new Date(date), this.innerValue); + }, + emitDate(date, type) { + if (!this.isDisabled(date)) { + this.$emit('select', date, type, this.innerValue); + // someone need get the first selected date to set range value. (#429) + this.dispatchDatePicker('pick', date, type); + } + }, + handleCalendarChange(calendar, type) { + const oldCalendar = new Date(this.innerCalendar); + this.innerCalendar = calendar; + this.$emit('update:calendar', calendar); + this.dispatchDatePicker('calendar-change', calendar, oldCalendar, type); + }, + handelPanelChange(panel) { + this.panel = panel; + }, + handleSelectYear(year) { + if (this.type === 'year') { + const date = this.getYearCellDate(year); + this.emitDate(date, 'year'); + } else { + this.handleCalendarChange(createDate(year, this.calendarMonth), 'year'); + this.handelPanelChange('month'); + if (this.partialUpdate && this.innerValue.length === 1) { + const date = new Date(this.innerValue[0]); + date.setFullYear(year); + this.emitDate(date, 'year'); + } + } + }, + handleSelectMonth(month) { + if (this.type === 'month') { + const date = this.getMonthCellDate(month); + this.emitDate(date, 'month'); + } else { + this.handleCalendarChange(createDate(this.calendarYear, month), 'month'); + this.handelPanelChange('date'); + if (this.partialUpdate && this.innerValue.length === 1) { + const date = new Date(this.innerValue[0]); + date.setFullYear(this.calendarYear); + this.emitDate(setMonth(date, month), 'month'); + } + } + }, + handleSelectDate(date) { + this.emitDate(date, this.type === 'week' ? 'week' : 'date'); + }, + getMonthCellDate(month) { + return createDate(this.calendarYear, month); + }, + getYearCellDate(year) { + return createDate(year, 0); + }, + getDateClasses(cellDate) { + const notCurrentMonth = cellDate.getMonth() !== this.calendarMonth; + const classes = []; + if (cellDate.getTime() === new Date().setHours(0, 0, 0, 0)) { + classes.push('today'); + } + if (notCurrentMonth) { + classes.push('not-current-month'); + } + const state = this.getStateClass(cellDate); + if (!(state === 'active' && notCurrentMonth)) { + classes.push(state); + } + return classes.concat(this.getClasses(cellDate, this.innerValue, classes.join(' '))); + }, + getMonthClasses(month) { + if (this.type !== 'month') { + return this.calendarMonth === month ? 'active' : ''; + } + const classes = []; + const cellDate = this.getMonthCellDate(month); + classes.push(this.getStateClass(cellDate)); + return classes.concat(this.getClasses(cellDate, this.innerValue, classes.join(' '))); + }, + getYearClasses(year) { + if (this.type !== 'year') { + return this.calendarYear === year ? 'active' : ''; + } + const classes = []; + const cellDate = this.getYearCellDate(year); + classes.push(this.getStateClass(cellDate)); + return classes.concat(this.getClasses(cellDate, this.innerValue, classes.join(' '))); + }, + getStateClass(cellDate) { + if (this.isDisabled(cellDate)) { + return 'disabled'; + } + if (this.innerValue.some(v => v.getTime() === cellDate.getTime())) { + return 'active'; + } + return ''; + }, + getWeekState(row) { + if (this.type !== 'week') return ''; + const start = row[0].getTime(); + const end = row[6].getTime(); + const active = this.innerValue.some(v => { + const time = v.getTime(); + return time >= start && time <= end; + }); + return active ? `${this.prefixClass}-active-week` : ''; + }, + }, + render() { + const { panel, innerCalendar } = this; + if (panel === 'year') { + return ( + + ); + } + if (panel === 'month') { + return ( + + ); + } + return ( + + ); + }, +}; diff --git a/src/calendar/calendar-panel.vue b/src/calendar/calendar-panel.vue deleted file mode 100644 index 7facb5f..0000000 --- a/src/calendar/calendar-panel.vue +++ /dev/null @@ -1,379 +0,0 @@ - - - diff --git a/src/calendar/calendar-range.js b/src/calendar/calendar-range.js index 059b61b..d4c3665 100644 --- a/src/calendar/calendar-range.js +++ b/src/calendar/calendar-range.js @@ -1,6 +1,5 @@ -import { addMonths, subMonths, differenceInCalendarMonths } from 'date-fns'; import CalendarPanel from './calendar-panel'; -import { getValidDate, isValidDate, isValidRangeDate } from '../util/date'; +import { getValidDate, isValidDate, isValidRangeDate, startOfMonth } from '../util/date'; export default { name: 'CalendarRange', @@ -45,8 +44,10 @@ export default { this.innerValue = isValidRangeDate(this.value) ? this.value : [new Date(NaN), new Date(NaN)]; - this.calendars = this.innerValue.map((v, i) => getValidDate(v, this.defaultValues[i])); - this.validateCalendars(1); + const calendars = this.innerValue.map((v, i) => + startOfMonth(getValidDate(v, this.defaultValues[i])) + ); + this.updateCalendars(calendars); }, }, }, @@ -68,27 +69,25 @@ export default { this.$emit('select', dates, type); }, updateStartCalendar(value) { - this.calendars.splice(0, 1, value); - this.validateCalendars(1); + this.updateCalendars([value, this.calendars[1]], 1); }, updateEndCalendar(value) { - this.calendars.splice(1, 1, value); - this.validateCalendars(0); + this.updateCalendars([this.calendars[0], value], 0); }, - validateCalendars(index) { - const gap = this.getCalendarGap(); + updateCalendars(calendars, adjustIndex = 1) { + const gap = this.getCalendarGap(calendars); if (gap) { - let calendar = this.calendars[index]; - if (index === 0) { - calendar = subMonths(calendar, gap); - } else { - calendar = addMonths(calendar, gap); - } - this.calendars.splice(index, 1, calendar); + const calendar = new Date(calendars[adjustIndex]); + calendar.setMonth(calendar.getMonth() + (adjustIndex === 0 ? -gap : gap)); + calendars[adjustIndex] = calendar; } + this.calendars = calendars; }, - getCalendarGap() { - const diff = differenceInCalendarMonths(this.calendars[1], this.calendars[0]); + getCalendarGap(calendars) { + const [calendarLeft, calendarRight] = calendars; + const yearDiff = calendarRight.getFullYear() - calendarLeft.getFullYear(); + const monthDiff = calendarRight.getMonth() - calendarLeft.getMonth(); + const diff = yearDiff * 12 + monthDiff; const min = this.calendarMinDiff; const max = this.calendarMaxDiff; if (diff < min) { diff --git a/src/calendar/icon-button.vue b/src/calendar/icon-button.vue new file mode 100644 index 0000000..552bebf --- /dev/null +++ b/src/calendar/icon-button.vue @@ -0,0 +1,22 @@ + + + diff --git a/src/calendar/table-date.vue b/src/calendar/table-date.vue index f44a22d..8540bbd 100644 --- a/src/calendar/table-date.vue +++ b/src/calendar/table-date.vue @@ -1,46 +1,71 @@ diff --git a/src/datetime/datetime-panel.js b/src/datetime/datetime-panel.js index a8745b7..ee77585 100644 --- a/src/datetime/datetime-panel.js +++ b/src/datetime/datetime-panel.js @@ -1,5 +1,5 @@ import CalendarPanel from '../calendar/calendar-panel'; -import TimePanel from '../time/time-panel.vue'; +import TimePanel from '../time/time-panel'; import { assignTime, getValidDate } from '../util/date'; import { pick } from '../util/base'; @@ -64,7 +64,7 @@ export default { render() { const calendarProps = { props: { - ...pick(this, Object.keys(CalendarPanel.props)), + ...pick(this.$props, Object.keys(CalendarPanel.props)), type: 'date', value: this.currentValue, }, @@ -74,13 +74,13 @@ export default { }; const timeProps = { props: { - ...pick(this, Object.keys(TimePanel.props)), + ...pick(this.$props, Object.keys(TimePanel.props)), showTimeHeader: true, value: this.currentValue, }, on: { select: this.emitDate, - 'title-click': this.closeTimePanel, + clicktitle: this.closeTimePanel, }, }; diff --git a/src/datetime/datetime-range.js b/src/datetime/datetime-range.js index d2fedb4..7c97798 100644 --- a/src/datetime/datetime-range.js +++ b/src/datetime/datetime-range.js @@ -71,7 +71,7 @@ export default { render() { const calendarProps = { props: { - ...pick(this, Object.keys(CalendarRange.props)), + ...pick(this.$props, Object.keys(CalendarRange.props)), type: 'date', value: this.currentValue, }, @@ -81,13 +81,13 @@ export default { }; const timeProps = { props: { - ...pick(this, Object.keys(TimeRange.props)), + ...pick(this.$props, Object.keys(TimeRange.props)), value: this.currentValue, showTimeHeader: true, }, on: { select: this.emitDate, - 'title-click': this.closeTimePanel, + clicktitle: this.closeTimePanel, }, }; diff --git a/src/icon/icon-calendar.vue b/src/icon/icon-calendar.vue index e3a9c32..9429b70 100644 --- a/src/icon/icon-calendar.vue +++ b/src/icon/icon-calendar.vue @@ -1,5 +1,5 @@ + + diff --git a/src/time/list-options.vue b/src/time/list-options.vue index d0651e7..c41d6e3 100644 --- a/src/time/list-options.vue +++ b/src/time/list-options.vue @@ -15,7 +15,7 @@ import { format } from 'date-format-parse'; import ScrollbarVertical from '../scrollbar/scrollbar-vertical'; import { getScrollParent } from '../util/dom'; -import { getLocaleFieldValue } from '../locale'; +import { getLocale } from '../locale'; function parseOption(time = '') { const values = time.split(':'); @@ -40,8 +40,8 @@ export default { name: 'ListOptions', components: { ScrollbarVertical }, inject: { - translateFn: { - default: () => getLocaleFieldValue, + getLocale: { + default: () => getLocale, }, prefixClass: { default: 'mx', @@ -99,7 +99,7 @@ export default { }, methods: { formatDate(date, fmt) { - return format(date, fmt, { locale: this.translateFn('formatLocale') }); + return format(date, fmt, { locale: this.getLocale().formatLocale }); }, scrollToSelected() { const element = this.$el.querySelector('.active'); diff --git a/src/time/time-panel.vue b/src/time/time-panel.vue index e666e6c..ce7d14e 100644 --- a/src/time/time-panel.vue +++ b/src/time/time-panel.vue @@ -41,14 +41,14 @@ import { format } from 'date-format-parse'; import { getValidDate } from '../util/date'; import ListColumns from './list-columns'; import ListOptions from './list-options'; -import { getLocaleFieldValue } from '../locale'; +import { getLocale } from '../locale'; export default { name: 'TimePanel', components: { ListColumns, ListOptions }, inject: { - translateFn: { - default: () => getLocaleFieldValue, + getLocale: { + default: () => getLocale, }, prefixClass: { default: 'mx', @@ -149,7 +149,7 @@ export default { }, methods: { formatDate(date, fmt) { - return format(date, fmt, { locale: this.translateFn('formatLocale') }); + return format(date, fmt, { locale: this.getLocale().formatLocale }); }, isDisabled(date) { return this.disabledTime(new Date(date)); @@ -161,7 +161,7 @@ export default { } }, handleClickTitle() { - this.$emit('title-click'); + this.$emit('clicktitle'); }, getClasses(value) { const cellDate = new Date(value); diff --git a/src/util/date.js b/src/util/date.js index f8b864a..b32001a 100644 --- a/src/util/date.js +++ b/src/util/date.js @@ -30,6 +30,61 @@ export function getValidDate(value, ...backup) { return new Date(); } +export function startOfYear(value) { + const date = new Date(value); + date.setMonth(0, 1); + date.setHours(0, 0, 0, 0); + return date; +} + +export function startOfMonth(value) { + const date = new Date(value); + date.setDate(1); + date.setHours(0, 0, 0, 0); + return date; +} + +export function startOfDay(value) { + const date = new Date(value); + date.setHours(0, 0, 0, 0); + return date; +} + +export function getCalendar({ firstDayOfWeek, year, month }) { + const arr = []; + // change to the last day of the last month + const calendar = createDate(year, month, 0); + const lastDayInLastMonth = calendar.getDate(); + // getDay() 0 is Sunday, 1 is Monday + const firstDayInLastMonth = lastDayInLastMonth - ((calendar.getDay() + 7 - firstDayOfWeek) % 7); + for (let i = firstDayInLastMonth; i <= lastDayInLastMonth; i++) { + arr.push(createDate(year, month, i - lastDayInLastMonth)); + } + // change to the last day of the current month + calendar.setMonth(month + 1, 0); + const lastDayInCurrentMonth = calendar.getDate(); + for (let i = 1; i <= lastDayInCurrentMonth; i++) { + arr.push(createDate(year, month, i)); + } + + const lastMonthLength = lastDayInLastMonth - firstDayInLastMonth + 1; + const nextMonthLength = 6 * 7 - lastMonthLength - lastDayInCurrentMonth; + for (let i = 1; i <= nextMonthLength; i++) { + arr.push(createDate(year, month, lastDayInCurrentMonth + i)); + } + return arr; +} + +export function setMonth(dirtyDate, dirtyMonth) { + const date = new Date(dirtyDate); + const month = Number(dirtyMonth); + const year = date.getFullYear(); + const daysInMonth = createDate(year, month + 1, 0).getDate(); + const day = date.getDate(); + date.setMonth(month, Math.min(day, daysInMonth)); + return date; +} + export function assignTime(target, source) { const date = new Date(target); const time = new Date(source);