Date picker refactor dates selection: fix 12323 (#12347)

* date-picker: refactor type='dates'

fix issue #12323
{month, year} table highlights all selected dates
nuke selectedDates to provide cleaner data flow

* doc: correct date-picker's array usage

empty value must be falsy (empty array should not be used)
pull/12357/merge
Jiewei Qian 2018-08-14 18:19:51 +10:00 committed by Jikkai Xiao
parent 74e62819c0
commit 02176e26f4
10 changed files with 76 additions and 76 deletions

View File

@ -66,8 +66,8 @@
value10: '', value10: '',
value11: '', value11: '',
value12: '', value12: '',
value13: [], value13: '',
value14: [] value14: ''
}; };
} }
}; };
@ -232,7 +232,7 @@ You can choose week, month, year or multiple dates by extending the standard dat
value3: '', value3: '',
value4: '', value4: '',
value5: '', value5: '',
value14: [] value14: ''
}; };
} }
}; };
@ -463,7 +463,7 @@ When picking a date range, you can assign the time part for start date and end d
export default { export default {
data() { data() {
return { return {
value13: [] value13: ''
}; };
} }
}; };

View File

@ -66,8 +66,8 @@
value10: '', value10: '',
value11: '', value11: '',
value12: '', value12: '',
value13: [], value13: '',
value14: [] value14: ''
}; };
} }
}; };
@ -234,7 +234,7 @@ You can choose week, month, year or multiple dates by extending the standard dat
value3: '', value3: '',
value4: '', value4: '',
value5: '', value5: '',
value14: [] value14: ''
}; };
} }
}; };
@ -466,7 +466,7 @@ Al seleccionar un intervalo de fechas, puede asignar la hora para la fecha de in
export default { export default {
data() { data() {
return { return {
value12: [] value12: ''
}; };
} }
}; };

View File

@ -66,8 +66,8 @@
value10: '', value10: '',
value11: '', value11: '',
value12: '', value12: '',
value13: [], value13: '',
value14: [] value14: ''
}; };
} }
}; };
@ -89,7 +89,7 @@
border-right: none; border-right: none;
} }
} }
.demo-date-picker .container { .demo-date-picker .container {
flex: 1; flex: 1;
border-right: solid 1px #EFF2F6; border-right: solid 1px #EFF2F6;
@ -232,7 +232,7 @@
value3: '', value3: '',
value4: '', value4: '',
value5: '', value5: '',
value14: [] value14: ''
}; };
} }
}; };
@ -418,7 +418,7 @@
export default { export default {
data() { data() {
return { return {
value13: [] value13: ''
}; };
} }
}; };

View File

@ -35,6 +35,7 @@
import { getFirstDayOfMonth, getDayCountOfMonth, getWeekNumber, getStartDateOfMonth, nextDate, isDate } from '../util'; import { getFirstDayOfMonth, getDayCountOfMonth, getWeekNumber, getStartDateOfMonth, nextDate, isDate } from '../util';
import { hasClass } from 'element-ui/src/utils/dom'; import { hasClass } from 'element-ui/src/utils/dom';
import Locale from 'element-ui/src/mixins/locale'; import Locale from 'element-ui/src/mixins/locale';
import { arrayFindIndex, arrayFind, coerceTruthyValueToArray } from 'element-ui/src/utils/util';
const WEEKS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; const WEEKS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
const clearHours = function(time) { const clearHours = function(time) {
@ -43,6 +44,14 @@
return cloneDate.getTime(); return cloneDate.getTime();
}; };
// remove the first element that satisfies `pred` from arr
// return a new array if modification occurs
// return the original array otherwise
const removeFromArray = function(arr, pred) {
const idx = typeof pred === 'function' ? arrayFindIndex(arr, pred) : arr.indexOf(pred);
return idx >= 0 ? [...arr.slice(0, idx), ...arr.slice(idx + 1)] : arr;
};
export default { export default {
mixins: [Locale], mixins: [Locale],
@ -75,10 +84,6 @@
disabledDate: {}, disabledDate: {},
selectedDate: {
type: Array
},
minDate: {}, minDate: {},
maxDate: {}, maxDate: {},
@ -135,7 +140,7 @@
const startDate = this.startDate; const startDate = this.startDate;
const disabledDate = this.disabledDate; const disabledDate = this.disabledDate;
const selectedDate = this.selectedDate || this.value; const selectedDate = this.selectionMode === 'dates' ? coerceTruthyValueToArray(this.value) : [];
const now = clearHours(new Date()); const now = clearHours(new Date());
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
@ -188,10 +193,9 @@
} }
} }
let newDate = new Date(time); let cellDate = new Date(time);
cell.disabled = typeof disabledDate === 'function' && disabledDate(newDate); cell.disabled = typeof disabledDate === 'function' && disabledDate(cellDate);
cell.selected = Array.isArray(selectedDate) && cell.selected = arrayFind(selectedDate, date => date.getTime() === cellDate.getTime());
selectedDate.filter(date => date.toString() === newDate.toString())[0];
this.$set(row, this.showWeekNumber ? j + 1 : j, cell); this.$set(row, this.showWeekNumber ? j + 1 : j, cell);
} }
@ -483,19 +487,11 @@
date: newDate date: newDate
}); });
} else if (selectionMode === 'dates') { } else if (selectionMode === 'dates') {
let selectedDate = this.selectedDate; const value = this.value || [];
const newValue = cell.selected
if (!cell.selected) { ? removeFromArray(value, date => date.getTime() === newDate.getTime())
selectedDate.push(newDate); : [...value, newDate];
} else { this.$emit('pick', newValue);
selectedDate.forEach((date, index) => {
if (date.toString() === newDate.toString()) {
selectedDate.splice(index, 1);
}
});
}
this.$emit('select', selectedDate);
} }
} }
} }

View File

@ -51,6 +51,7 @@
import Locale from 'element-ui/src/mixins/locale'; import Locale from 'element-ui/src/mixins/locale';
import { isDate, range, getDayCountOfMonth, nextDate } from '../util'; import { isDate, range, getDayCountOfMonth, nextDate } from '../util';
import { hasClass } from 'element-ui/src/utils/dom'; import { hasClass } from 'element-ui/src/utils/dom';
import { arrayFindIndex, coerceTruthyValueToArray } from 'element-ui/src/utils/util';
const datesInMonth = (year, month) => { const datesInMonth = (year, month) => {
const numOfDays = getDayCountOfMonth(year, month); const numOfDays = getDayCountOfMonth(year, month);
@ -80,7 +81,7 @@
style.disabled = typeof this.disabledDate === 'function' style.disabled = typeof this.disabledDate === 'function'
? datesInMonth(year, month).every(this.disabledDate) ? datesInMonth(year, month).every(this.disabledDate)
: false; : false;
style.current = this.value.getFullYear() === year && this.value.getMonth() === month; style.current = arrayFindIndex(coerceTruthyValueToArray(this.value), date => date.getFullYear() === year && date.getMonth() === month) >= 0;
style.today = today.getFullYear() === year && today.getMonth() === month; style.today = today.getFullYear() === year && today.getMonth() === month;
style.default = this.defaultValue && style.default = this.defaultValue &&
this.defaultValue.getFullYear() === year && this.defaultValue.getFullYear() === year &&

View File

@ -46,6 +46,7 @@
<script type="text/babel"> <script type="text/babel">
import { hasClass } from 'element-ui/src/utils/dom'; import { hasClass } from 'element-ui/src/utils/dom';
import { isDate, range, nextDate, getDayCountOfYear } from '../util'; import { isDate, range, nextDate, getDayCountOfYear } from '../util';
import { arrayFindIndex, coerceTruthyValueToArray } from 'element-ui/src/utils/util';
const datesInYear = year => { const datesInYear = year => {
const numOfDays = getDayCountOfYear(year); const numOfDays = getDayCountOfYear(year);
@ -80,7 +81,7 @@
style.disabled = typeof this.disabledDate === 'function' style.disabled = typeof this.disabledDate === 'function'
? datesInYear(year).every(this.disabledDate) ? datesInYear(year).every(this.disabledDate)
: false; : false;
style.current = this.value.getFullYear() === year; style.current = arrayFindIndex(coerceTruthyValueToArray(this.value), date => date.getFullYear() === year) >= 0;
style.today = today.getFullYear() === year; style.today = today.getFullYear() === year;
style.default = this.defaultValue && this.defaultValue.getFullYear() === year; style.default = this.defaultValue && this.defaultValue.getFullYear() === year;

View File

@ -91,19 +91,17 @@
<date-table <date-table
v-show="currentView === 'date'" v-show="currentView === 'date'"
@pick="handleDatePick" @pick="handleDatePick"
@select="handleDateSelect"
:selection-mode="selectionMode" :selection-mode="selectionMode"
:first-day-of-week="firstDayOfWeek" :first-day-of-week="firstDayOfWeek"
:value="new Date(value)" :value="value"
:default-value="defaultValue ? new Date(defaultValue) : null" :default-value="defaultValue ? new Date(defaultValue) : null"
:date="date" :date="date"
:disabled-date="disabledDate" :disabled-date="disabledDate">
:selected-date="selectedDate">
</date-table> </date-table>
<year-table <year-table
v-show="currentView === 'year'" v-show="currentView === 'year'"
@pick="handleYearPick" @pick="handleYearPick"
:value="new Date(value)" :value="value"
:default-value="defaultValue ? new Date(defaultValue) : null" :default-value="defaultValue ? new Date(defaultValue) : null"
:date="date" :date="date"
:disabled-date="disabledDate"> :disabled-date="disabledDate">
@ -111,7 +109,7 @@
<month-table <month-table
v-show="currentView === 'month'" v-show="currentView === 'month'"
@pick="handleMonthPick" @pick="handleMonthPick"
:value="new Date(value)" :value="value"
:default-value="defaultValue ? new Date(defaultValue) : null" :default-value="defaultValue ? new Date(defaultValue) : null"
:date="date" :date="date"
:disabled-date="disabledDate"> :disabled-date="disabledDate">
@ -333,12 +331,6 @@
} }
}, },
handleDateSelect(value) {
if (this.selectionMode === 'dates') {
this.selectedDate = value;
}
},
handleDatePick(value) { handleDatePick(value) {
if (this.selectionMode === 'day') { if (this.selectionMode === 'day') {
this.date = this.value this.date = this.value
@ -347,6 +339,8 @@
this.emit(this.date, this.showTime); this.emit(this.date, this.showTime);
} else if (this.selectionMode === 'week') { } else if (this.selectionMode === 'week') {
this.emit(value.date); this.emit(value.date);
} else if (this.selectionMode === 'dates') {
this.emit(value, true); // set false to keep panel open
} }
}, },
@ -373,7 +367,7 @@
confirm() { confirm() {
if (this.selectionMode === 'dates') { if (this.selectionMode === 'dates') {
this.emit(this.selectedDate); this.emit(this.value);
} else { } else {
// value were emitted in handle{Date,Time}Pick, nothing to update here // value were emitted in handle{Date,Time}Pick, nothing to update here
// deal with the scenario where: user opens the picker, then confirm without doing anything // deal with the scenario where: user opens the picker, then confirm without doing anything
@ -506,7 +500,6 @@
visible: false, visible: false,
currentView: 'date', currentView: 'date',
disabledDate: '', disabledDate: '',
selectedDate: [],
firstDayOfWeek: 7, firstDayOfWeek: 7,
showWeekNumber: false, showWeekNumber: false,
timePickerVisible: false, timePickerVisible: false,

View File

@ -419,7 +419,6 @@ export default {
handler(val) { handler(val) {
if (this.picker) { if (this.picker) {
this.picker.value = val; this.picker.value = val;
this.picker.selectedDate = Array.isArray(val) ? val : [];
} }
} }
}, },
@ -705,15 +704,11 @@ export default {
handleClose() { handleClose() {
if (!this.pickerVisible) return; if (!this.pickerVisible) return;
this.pickerVisible = false; this.pickerVisible = false;
const {
type, if (this.type === 'dates') {
valueOnOpen, // restore to former value
valueFormat, const oldValue = parseAsFormatAndType(this.valueOnOpen, this.valueFormat, this.type, this.rangeSeparator) || this.valueOnOpen;
rangeSeparator this.emitInput(oldValue);
} = this;
if (type === 'dates' && this.picker) {
this.picker.selectedDate = parseAsFormatAndType(valueOnOpen, valueFormat, type, rangeSeparator) || valueOnOpen;
this.emitInput(this.picker.selectedDate);
} }
}, },
@ -828,7 +823,6 @@ export default {
this.picker.selectionMode = this.selectionMode; this.picker.selectionMode = this.selectionMode;
this.picker.unlinkPanels = this.unlinkPanels; this.picker.unlinkPanels = this.unlinkPanels;
this.picker.arrowControl = this.arrowControl || this.timeArrowControl || false; this.picker.arrowControl = this.arrowControl || this.timeArrowControl || false;
this.picker.selectedDate = Array.isArray(this.value) && this.value || [];
this.$watch('format', (format) => { this.$watch('format', (format) => {
this.picker.format = format; this.picker.format = format;
}); });

View File

@ -84,3 +84,29 @@ export const valueEquals = (a, b) => {
}; };
export const escapeRegexpString = (value = '') => String(value).replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); export const escapeRegexpString = (value = '') => String(value).replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
// TODO: use native Array.find, Array.findIndex when IE support is dropped
export const arrayFindIndex = function(arr, pred) {
for (let i = 0; i !== arr.length; ++i) {
if (pred(arr[i])) {
return i;
}
}
return -1;
};
export const arrayFind = function(arr, pred) {
const idx = arrayFindIndex(arr, pred);
return idx !== -1 ? arr[idx] : undefined;
};
// coerce truthy value to array
export const coerceTruthyValueToArray = function(val) {
if (Array.isArray(val)) {
return val;
} else if (val) {
return [val];
} else {
return [];
}
};

View File

@ -1517,25 +1517,14 @@ describe('DatePicker', () => {
const td = vm.$refs.compo.picker.$el.querySelector('.el-date-table__row .available'); const td = vm.$refs.compo.picker.$el.querySelector('.el-date-table__row .available');
td.click(); td.click();
setTimeout(_ => { setTimeout(_ => {
expect(vm.$refs.compo.picker.selectedDate).to.exist; expect(vm.$refs.compo.value).to.be.an('array');
expect(vm.$refs.compo.value.length).to.equal(1);
expect(vm.$refs.compo.value[0]).to.be.a('number');
expect(vm.value.length).to.equal(1); expect(vm.value.length).to.equal(1);
done(); done();
}, DELAY); }, DELAY);
}); });
it('value format', done => {
const td = vm.$refs.compo.picker.$el.querySelector('.el-date-table__row .available');
td.click();
setTimeout(_ => {
vm.$refs.compo.picker.$el.querySelector('.el-button--default').click();
setTimeout(() => {
expect(vm.$refs.compo.picker.selectedDate).to.exist;
expect(vm.value.length).to.equal(1);
done();
}, DELAY);
}, DELAY);
});
it('restore value when cancel', done => { it('restore value when cancel', done => {
const td = vm.$refs.compo.picker.$el.querySelector('.el-date-table__row .available'); const td = vm.$refs.compo.picker.$el.querySelector('.el-date-table__row .available');
td.click(); td.click();