From 867e4478143414dc540d68167f08bad5a2582afc Mon Sep 17 00:00:00 2001 From: ziyoung Date: Thu, 28 Mar 2019 16:19:21 +0800 Subject: [PATCH 01/16] Calendar: add calendar component --- components.json | 3 +- examples/docs/en-US/calendar.md | 1 + examples/docs/es/calendar.md | 1 + examples/docs/zh-CN/calendar.md | 1 + examples/nav.config.json | 16 ++++++++ packages/calendar/index.js | 8 ++++ packages/calendar/src/main.vue | 37 +++++++++++++++++++ packages/date-picker/src/basic/date-table.vue | 2 +- .../date-picker/src/basic/month-table.vue | 2 +- .../date-picker/src/basic/time-spinner.vue | 2 +- packages/date-picker/src/basic/year-table.vue | 2 +- packages/date-picker/src/panel/date-range.vue | 2 +- packages/date-picker/src/panel/date.vue | 2 +- .../date-picker/src/panel/month-range.vue | 2 +- packages/date-picker/src/panel/time-range.vue | 2 +- packages/date-picker/src/panel/time.vue | 2 +- packages/date-picker/src/picker.vue | 2 +- packages/theme-chalk/src/calendar.scss | 5 +++ .../util/index.js => src/utils/date-util.js | 8 ++-- test/unit/specs/calendar.spec.js | 15 ++++++++ types/calendar.d.ts | 5 +++ 21 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 examples/docs/en-US/calendar.md create mode 100644 examples/docs/es/calendar.md create mode 100644 examples/docs/zh-CN/calendar.md create mode 100644 packages/calendar/index.js create mode 100644 packages/calendar/src/main.vue create mode 100644 packages/theme-chalk/src/calendar.scss rename packages/date-picker/src/util/index.js => src/utils/date-util.js (96%) create mode 100644 test/unit/specs/calendar.spec.js create mode 100644 types/calendar.d.ts diff --git a/components.json b/components.json index 54389a833b..8d6a6b8005 100644 --- a/components.json +++ b/components.json @@ -72,5 +72,6 @@ "timeline-item": "./packages/timeline-item/index.js", "link": "./packages/link/index.js", "divider": "./packages/divider/index.js", - "image": "./packages/image/index.js" + "image": "./packages/image/index.js", + "calendar": "./packages/calendar/index.js" } diff --git a/examples/docs/en-US/calendar.md b/examples/docs/en-US/calendar.md new file mode 100644 index 0000000000..f50ca9a102 --- /dev/null +++ b/examples/docs/en-US/calendar.md @@ -0,0 +1 @@ +## Calendar diff --git a/examples/docs/es/calendar.md b/examples/docs/es/calendar.md new file mode 100644 index 0000000000..f50ca9a102 --- /dev/null +++ b/examples/docs/es/calendar.md @@ -0,0 +1 @@ +## Calendar diff --git a/examples/docs/zh-CN/calendar.md b/examples/docs/zh-CN/calendar.md new file mode 100644 index 0000000000..71247b466a --- /dev/null +++ b/examples/docs/zh-CN/calendar.md @@ -0,0 +1 @@ +## Calendar calendar diff --git a/examples/nav.config.json b/examples/nav.config.json index 11f0d60e61..b220638246 100644 --- a/examples/nav.config.json +++ b/examples/nav.config.json @@ -260,6 +260,10 @@ "path": "/divider", "title": "Divider 分割线" }, + { + "path": "/calendar", + "title": "Calendar" + }, { "path": "/image", "title": "Image 图片" @@ -530,6 +534,10 @@ "path": "/divider", "title": "Divider" }, + { + "path": "/calendar", + "title": "Calendar" + }, { "path": "/image", "title": "Image" @@ -800,6 +808,10 @@ "path": "/divider", "title": "Divider" }, + { + "path": "/calendar", + "title": "Calendar" + }, { "path": "/image", "title": "Image" @@ -1070,6 +1082,10 @@ "path": "/divider", "title": "Divider" }, + { + "path": "/calendar", + "title": "Calendar" + }, { "path": "/image", "title": "Image" diff --git a/packages/calendar/index.js b/packages/calendar/index.js new file mode 100644 index 0000000000..ec79cd99a1 --- /dev/null +++ b/packages/calendar/index.js @@ -0,0 +1,8 @@ +import Calendar from './src/main'; + +/* istanbul ignore next */ +Calendar.install = function(Vue) { + Vue.component(Calendar.name, Calendar); +}; + +export default Calendar; diff --git a/packages/calendar/src/main.vue b/packages/calendar/src/main.vue new file mode 100644 index 0000000000..8c3481deeb --- /dev/null +++ b/packages/calendar/src/main.vue @@ -0,0 +1,37 @@ + + + diff --git a/packages/date-picker/src/basic/date-table.vue b/packages/date-picker/src/basic/date-table.vue index ec089e6167..f7b08843bf 100644 --- a/packages/date-picker/src/basic/date-table.vue +++ b/packages/date-picker/src/basic/date-table.vue @@ -32,7 +32,7 @@ diff --git a/packages/calendar/src/main.vue b/packages/calendar/src/main.vue index 8c3481deeb..bbe572db39 100644 --- a/packages/calendar/src/main.vue +++ b/packages/calendar/src/main.vue @@ -2,36 +2,116 @@
- 2019年3月 + {{ formatedDate }}
- 上月 - 今天 - 下月 + + 上月 + + + 今天 + + + 下月 +
- - - {{ i }} - - - - - - -
+
diff --git a/packages/theme-chalk/src/calendar.scss b/packages/theme-chalk/src/calendar.scss index b3fee70ef9..5e35c4f9d2 100644 --- a/packages/theme-chalk/src/calendar.scss +++ b/packages/theme-chalk/src/calendar.scss @@ -2,4 +2,71 @@ @import "common/var"; @include b(calendar) { + background-color:#fff; + + @include e(header) { + display: flex; + justify-content: space-between; + padding: 12px 20px; + border-bottom: $--table-border; + } + + @include e(title) { + color: #000000; + align-self: center; + } + + @include e(body) { + padding: 15px 20px 35px; + } +} + +@include b(calendar-table) { + table-layout: fixed; + padding-top: 12px; + width: 100%; + + thead th { + padding: 12px 0; + color: $--color-text-regular; + font-weight: normal; + } + + td { + border-bottom: $--calendar-border; + border-right: $--calendar-border; + vertical-align: top; + transition: background-color 0.2s ease; + + &.prev, + &.next { + color: $--color-text-placeholder; + } + + @include when(selected) { + background-color: $--calendar-selected-background-color; + } + + @include when(today) { + color: $--color-primary; + } + } + + tr:first-child td { + border-top: $--calendar-border; + } + + tr td:first-child { + border-left: $--calendar-border; + } + + @include b(calendar-day) { + box-sizing: border-box; + padding: 8px; + height: 85px; + &:hover { + cursor: pointer; + background-color: $--calendar-selected-background-color; + } + } } diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss index 61b7a934e1..a3f807b84d 100644 --- a/packages/theme-chalk/src/common/var.scss +++ b/packages/theme-chalk/src/common/var.scss @@ -885,6 +885,12 @@ $--link-warning-font-color: $--color-warning !default; $--link-danger-font-color: $--color-danger !default; /// color||Color|0 $--link-info-font-color: $--color-info !default; +/* Calendar +--------------------------*/ +/// border||Other|4 +$--calendar-border: $--table-border !default; +/// color||Other|4 +$--calendar-selected-background-color: #F2F8FE !default; /* Break-point --------------------------*/ diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss index 4e5488691c..e933ae1325 100644 --- a/packages/theme-chalk/src/index.scss +++ b/packages/theme-chalk/src/index.scss @@ -70,3 +70,4 @@ @import "./link.scss"; @import "./divider.scss"; @import "./image.scss"; +@import "./calendar.scss"; diff --git a/src/index.js b/src/index.js index 8337e209f6..b858acd9bc 100644 --- a/src/index.js +++ b/src/index.js @@ -74,6 +74,7 @@ import TimelineItem from '../packages/timeline-item/index.js'; import Link from '../packages/link/index.js'; import Divider from '../packages/divider/index.js'; import Image from '../packages/image/index.js'; +import Calendar from '../packages/calendar/index.js'; import locale from 'element-ui/src/locale'; import CollapseTransition from 'element-ui/src/transitions/collapse-transition'; @@ -148,6 +149,7 @@ const components = [ Link, Divider, Image, + Calendar, CollapseTransition ]; diff --git a/src/utils/date-util.js b/src/utils/date-util.js index baf5a56010..decf8def92 100644 --- a/src/utils/date-util.js +++ b/src/utils/date-util.js @@ -3,15 +3,6 @@ import { t } from 'element-ui/src/locale'; const weeks = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; const months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']; -const getI18nSettings = () => { - return { - dayNamesShort: weeks.map(week => t(`el.datepicker.weeks.${ week }`)), - dayNames: weeks.map(week => t(`el.datepicker.weeks.${ week }`)), - monthNamesShort: months.map(month => t(`el.datepicker.months.${ month }`)), - monthNames: months.map((month, index) => t(`el.datepicker.month${ index + 1 }`)), - amPm: ['am', 'pm'] - }; -}; const newArray = function(start, end) { let result = []; @@ -21,6 +12,16 @@ const newArray = function(start, end) { return result; }; +export const getI18nSettings = () => { + return { + dayNamesShort: weeks.map(week => t(`el.datepicker.weeks.${ week }`)), + dayNames: weeks.map(week => t(`el.datepicker.weeks.${ week }`)), + monthNamesShort: months.map(month => t(`el.datepicker.months.${ month }`)), + monthNames: months.map((month, index) => t(`el.datepicker.month${ index + 1 }`)), + amPm: ['am', 'pm'] + }; +}; + export const toDate = function(date) { return isDate(date) ? new Date(date) : null; }; @@ -131,6 +132,20 @@ export const getRangeHours = function(ranges) { return hours; }; +export const getPrevMonthLastDays = (date, amount) => { + if (amount <= 0) return []; + const temp = new Date(date.getTime()); + temp.setDate(0); + const lastDay = temp.getDate(); + return range(amount).map((_, index) => lastDay - (amount - index - 1)); +}; + +export const getMonthDays = (date) => { + const temp = new Date(date.getFullYear(), date.getMonth() + 1, 0); + const days = temp.getDate(); + return range(days).map((_, index) => index + 1); +}; + function setRangeData(arr, start, end, value) { for (let i = start; i < end; i++) { arr[i] = value; From becc230a562cb2755fe7641cfa88f06a0fa3ee10 Mon Sep 17 00:00:00 2001 From: ziyoung Date: Fri, 29 Mar 2019 16:08:23 +0800 Subject: [PATCH 03/16] support date cell render --- packages/calendar/src/date-table.vue | 80 ++++++++++++++++---------- packages/calendar/src/main.vue | 6 +- packages/theme-chalk/src/calendar.scss | 3 +- 3 files changed, 54 insertions(+), 35 deletions(-) diff --git a/packages/calendar/src/date-table.vue b/packages/calendar/src/date-table.vue index 9bc12b5adb..6a50b9e05a 100644 --- a/packages/calendar/src/date-table.vue +++ b/packages/calendar/src/date-table.vue @@ -1,36 +1,9 @@ - - diff --git a/packages/calendar/src/main.vue b/packages/calendar/src/main.vue index bbe572db39..6666346c67 100644 --- a/packages/calendar/src/main.vue +++ b/packages/calendar/src/main.vue @@ -30,7 +30,7 @@
@@ -58,7 +58,7 @@ export default { methods: { pickDay(day) { - this.highlightDay = day; + this.selectedDay = day; this.date = new Date(day); }, @@ -109,7 +109,7 @@ export default { const now = new Date(); return { date: now, // defualt value is now - highlightDay: '', + selectedDay: '', now }; } diff --git a/packages/theme-chalk/src/calendar.scss b/packages/theme-chalk/src/calendar.scss index 5e35c4f9d2..22427297de 100644 --- a/packages/theme-chalk/src/calendar.scss +++ b/packages/theme-chalk/src/calendar.scss @@ -17,13 +17,12 @@ } @include e(body) { - padding: 15px 20px 35px; + padding: 12px 20px 35px; } } @include b(calendar-table) { table-layout: fixed; - padding-top: 12px; width: 100%; thead th { From 9758cd30d8de5e3e48abef5d8009a0f3f0eacb3a Mon Sep 17 00:00:00 2001 From: ziyoung Date: Fri, 29 Mar 2019 19:05:36 +0800 Subject: [PATCH 04/16] support range --- packages/calendar/src/date-table.vue | 60 +++++++++++------- packages/calendar/src/main.vue | 94 +++++++++++++++++++++++++--- 2 files changed, 124 insertions(+), 30 deletions(-) diff --git a/packages/calendar/src/date-table.vue b/packages/calendar/src/date-table.vue index 6a50b9e05a..f4bfd3f5ec 100644 --- a/packages/calendar/src/date-table.vue +++ b/packages/calendar/src/date-table.vue @@ -4,12 +4,20 @@ export default { props: { selectedDay: String, // formated date yyyy-MM-dd + range: Array, date: Date }, inject: ['elCalendar'], methods: { + toNestedArr(days) { + return range(days.length / 7).map((_, index) => { + const start = index * 7; + return days.slice(start, start + 7); + }); + }, + getFormateDate(day, type) { if (!day || ['prev', 'current', 'next'].indexOf(type) === -1) { throw new Error('invalid day or type'); @@ -54,9 +62,7 @@ export default { type: `${type}-month`, day }; - return render({ - date, data - }); + return render({ date, data }); } }, @@ -78,27 +84,35 @@ export default { }, rows() { - const date = this.date; let days = []; - const firstDay = getFirstDayOfMonth(date); - const prevMonthDays = getPrevMonthLastDays(date, firstDay - 1).map(day => ({ - text: day, - type: 'prev' - })); - const currentMonthDays = getMonthDays(date).map(day => ({ - text: day, - type: 'current' - }));; - days = [...prevMonthDays, ...currentMonthDays]; - const nextMonthDays = range(42 - days.length).map((_, index) => ({ - text: index + 1, - type: 'next' - })); - days = days.concat(nextMonthDays); - return range(days.length / 7).map((_, index) => { - const start = index * 7; - return days.slice(start, start + 7); - }); + // if range exists, should render days in range + if (this.range && this.range.length) { + const [start, end] = this.range; + for (let index = start.getDate(); index <= end.getDate(); index++) { + days.push({ + text: index, + type: 'current' + }); + } + } else { + const date = this.date; + const firstDay = getFirstDayOfMonth(date); + const prevMonthDays = getPrevMonthLastDays(date, firstDay - 1).map(day => ({ + text: day, + type: 'prev' + })); + const currentMonthDays = getMonthDays(date).map(day => ({ + text: day, + type: 'current' + }));; + days = [...prevMonthDays, ...currentMonthDays]; + const nextMonthDays = range(42 - days.length).map((_, index) => ({ + text: index + 1, + type: 'next' + })); + days = days.concat(nextMonthDays); + } + return this.toNestedArr(days); } }, diff --git a/packages/calendar/src/main.vue b/packages/calendar/src/main.vue index 6666346c67..0b64b80cb7 100644 --- a/packages/calendar/src/main.vue +++ b/packages/calendar/src/main.vue @@ -4,7 +4,9 @@
{{ formatedDate }}
-
+
@@ -50,6 +53,20 @@ export default { DateTable }, + props: { + value: [Date, String], + range: { + type: Array, + validator(range) { + if (Array.isArray(range)) { + return range.length === 2 && range.every(item => typeof item === 'string' || item instanceof Date); + } else { + return true; + } + } + } + }, + provide() { return { elCalendar: this @@ -58,8 +75,7 @@ export default { methods: { pickDay(day) { - this.selectedDay = day; - this.date = new Date(day); + this.realSelectedDay = day; }, selectDate(type) { @@ -77,6 +93,13 @@ export default { if (day === this.formatedDate) return; this.pickDay(day); + }, + + toDate(val) { + if (!val) { + throw new Error('invalid val'); + } + return val instanceof Date ? val : new Date(val); } }, @@ -102,15 +125,72 @@ export default { formatedToday() { return fecha.format(this.now, 'yyyy-MM-dd'); + }, + + realSelectedDay: { + get() { + if (!this.value) return this.selectedDay; + return this.formatedDate; + }, + set(val) { + this.selectedDay = val; + const date = new Date(val); + this.$emit('input', date); + } + }, + + date() { + if (!this.value) { + if (this.realSelectedDay) { + return new Date(this.selectedDay); + } else if (this.validatedRange.length) { + return this.validatedRange[0]; + } + return this.now; + } else { + return this.toDate(this.value); + } + }, + + validatedRange() { + let range = this.range; + if (!range) return []; + const expetedMap = { + 0: { + value: 1, + message: 'start of range should be Monday.' + }, + 1: { + value: 0, + message: 'end of range should be Sunday.' + } + }; + range = range.reduce((prev, val, index) => { + const date = this.toDate(val); + if (date.getDay() !== expetedMap[index].value) { + console.warn('[ElementCalendar]', expetedMap[index].message, ' validRange will be ignored'); + } else { + prev = prev.concat(date); + } + return prev; + }, []); + if (range.length === 2) { + if (range[0].getMonth() === range[1].getMonth() && range[0].getFullYear() === range[1].getFullYear()) { + return range; + } else { + console.warn('[ElementCalendar] range must be within one month'); + return []; + } + } else { + return []; + } } }, data() { - const now = new Date(); return { - date: now, // defualt value is now selectedDay: '', - now + now: new Date() }; } }; From 0820be92b550be8fd623089e924e2b0d3cbb2aba Mon Sep 17 00:00:00 2001 From: ziyoung Date: Fri, 29 Mar 2019 19:23:48 +0800 Subject: [PATCH 05/16] i18n --- packages/calendar/src/main.vue | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/calendar/src/main.vue b/packages/calendar/src/main.vue index 0b64b80cb7..656ead4bec 100644 --- a/packages/calendar/src/main.vue +++ b/packages/calendar/src/main.vue @@ -2,7 +2,7 @@
- {{ formatedDate }} + {{ i18nDate }}
- 上月 + {{ t('el.datepicker.prevMonth') }} - 今天 + {{ t('el.datepicker.today') }} - 下月 + {{ t('el.datepicker.nextMonth') }}
@@ -40,7 +40,7 @@ +``` +::: + +### 自定义内容 + +:::demo 通过设置名为 `dateCell` 的 `scoped-slot` 来自定义日历单元格中显示的内容。在 `scoped-slot` 可以获取到 date(当前单元格的日期), data(包括 type,isSelected,day 属性)。详情解释参考下方的 API 文档。 +```html + + + + +``` +::: + +### 自定义范围 + +:::demo 设置 `range` 属性指定日历的显示范围。开始时间必须是周一,结束时间必须是周日,且两者必须在一个月内。 + + +::: + +### Attributes +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|-----------------|-------------- |---------- |------------ |-------- | +| value / v-model | 绑定值 | Date/string/number | — | — | +| range | 时间范围 | Array | — | — | + +### dateCell scoped slot 参数 +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|-----------------|-------------- |---------- |------------ |-------- | +| date | 单元格代表的日期 | Date | — | — | +| data | { type, isSelected, day},type 的可选择有 prev-month,current-month,next-month, isSelected 标明日期是否被选中,day 是格式化的日期,格式为 yyyy-MM-dd | Object | — | — | From 7d7e6c040c7be2d1d4b97220e0989ea1dde25ede Mon Sep 17 00:00:00 2001 From: ziyoung Date: Mon, 1 Apr 2019 12:30:24 +0800 Subject: [PATCH 07/16] =?UTF-8?q?Calendar=EF=BC=9A=20update=20docs=20and?= =?UTF-8?q?=20unit=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/docs/en-US/calendar.md | 59 ++++++++++++++++++++++++++++++++ examples/docs/zh-CN/calendar.md | 16 +++++---- examples/nav.config.json | 2 +- packages/calendar/src/main.vue | 7 ++-- src/index.js | 3 +- test/unit/specs/calendar.spec.js | 44 +++++++++++++++++++++--- 6 files changed, 115 insertions(+), 16 deletions(-) diff --git a/examples/docs/en-US/calendar.md b/examples/docs/en-US/calendar.md index f50ca9a102..0c61440206 100644 --- a/examples/docs/en-US/calendar.md +++ b/examples/docs/en-US/calendar.md @@ -1 +1,60 @@ ## Calendar + +Display date. + +### Basic + +:::demo Set `value` to specify the currently displayed month. If `value` is not specified, current month is displayed. `value` supports two-way binding. +```html + + + + +``` +::: + +### Custom Content + +:::demo Customize what is displayed in the calendar cell by setting `scoped-slot` named `dateCell`. In `scoped-slot` you can get the date (the date of the current cell), data (including the type, isSelected, day attribute). For details, please refer to the API documentation below. +```html + + + + +``` +::: + +### Range + +:::demo Set the `range` attribute to specify the display range of the calendar. Start time must be Monday, end time must be Sunday, and both must be within one month. +```html + + +``` +::: + +### Attributes +| Attribute | Description | Type | Accepted Values | Default | +|-----------------|-------------- |---------- |---------------------- |--------- | +| value / v-model | binding value | Date/string/number | — | — | +| range | time range, including start time and end time. Start time must be Monday, end time must be Sunday, and both must be in the same month | Array | — | — | + +### dateCell scoped slot 参数 +| Attribute | Description | Type | Accepted Values | Default | +|-----------------|-------------- |---------- |---------------------- |--------- | +| date | date the cell represents | Date | — | — | +| data | { type, isSelected, day}. `type` indicates which month the date belongs, optional values are prev-month, current-month, next-month; `isSelected` indicates whether the date is selected; `day` is the formatted date in the format yyyy-MM-dd | Object | — | — | diff --git a/examples/docs/zh-CN/calendar.md b/examples/docs/zh-CN/calendar.md index 44f37b51f2..db1da5ebb9 100644 --- a/examples/docs/zh-CN/calendar.md +++ b/examples/docs/zh-CN/calendar.md @@ -4,7 +4,7 @@ ### 基本 -:::demo 设置 `value` 来指定当前显示的月份。如果 `value` 未指定则显示当月。`value` 支持 `v-model` 双向绑定。 +:::demo 设置 `value` 来指定当前显示的月份。如果 `value` 未指定,则显示当月。`value` 支持 `v-model` 双向绑定。 ```html @@ -26,13 +26,13 @@ :::demo 通过设置名为 `dateCell` 的 `scoped-slot` 来自定义日历单元格中显示的内容。在 `scoped-slot` 可以获取到 date(当前单元格的日期), data(包括 type,isSelected,day 属性)。详情解释参考下方的 API 文档。 ```html - + ``` @@ -41,18 +41,20 @@ ### 自定义范围 :::demo 设置 `range` 属性指定日历的显示范围。开始时间必须是周一,结束时间必须是周日,且两者必须在一个月内。 +```html +``` ::: ### Attributes | 参数 | 说明 | 类型 | 可选值 | 默认值 | |-----------------|-------------- |---------- |------------ |-------- | | value / v-model | 绑定值 | Date/string/number | — | — | -| range | 时间范围 | Array | — | — | +| range | 时间范围,包括开始时间与结束时间。开始时间必须是周一,结束时间必须是周日,且必须在同一个月内 | Array | — | — | ### dateCell scoped slot 参数 | 参数 | 说明 | 类型 | 可选值 | 默认值 | |-----------------|-------------- |---------- |------------ |-------- | | date | 单元格代表的日期 | Date | — | — | -| data | { type, isSelected, day},type 的可选择有 prev-month,current-month,next-month, isSelected 标明日期是否被选中,day 是格式化的日期,格式为 yyyy-MM-dd | Object | — | — | +| data | { type, isSelected, day},`type` 表示该日期的所属月份,可选值有 prev-month,current-month,next-month;`isSelected` 标明该日期是否被选中;`day` 是格式化的日期,格式为 yyyy-MM-dd | Object | — | — | diff --git a/examples/nav.config.json b/examples/nav.config.json index b220638246..bfd297fc00 100644 --- a/examples/nav.config.json +++ b/examples/nav.config.json @@ -262,7 +262,7 @@ }, { "path": "/calendar", - "title": "Calendar" + "title": "Calendar 日历" }, { "path": "/image", diff --git a/packages/calendar/src/main.vue b/packages/calendar/src/main.vue index 656ead4bec..d4f208118b 100644 --- a/packages/calendar/src/main.vue +++ b/packages/calendar/src/main.vue @@ -56,12 +56,15 @@ export default { }, props: { - value: [Date, String], + value: [Date, String, Number], range: { type: Array, validator(range) { if (Array.isArray(range)) { - return range.length === 2 && range.every(item => typeof item === 'string' || item instanceof Date); + return range.length === 2 && range.every( + item => typeof item === 'string' || + typeof item === 'number' || + item instanceof Date); } else { return true; } diff --git a/src/index.js b/src/index.js index b858acd9bc..3c0b7c108a 100644 --- a/src/index.js +++ b/src/index.js @@ -262,5 +262,6 @@ export default { TimelineItem, Link, Divider, - Image + Image, + Calendar }; diff --git a/test/unit/specs/calendar.spec.js b/test/unit/specs/calendar.spec.js index 7fccec6cc2..9afd7fd979 100644 --- a/test/unit/specs/calendar.spec.js +++ b/test/unit/specs/calendar.spec.js @@ -1,5 +1,4 @@ -import { createTest, destroyVM } from '../util'; -import Calendar from 'packages/calendar'; +import { createVue, destroyVM, waitImmediate } from '../util'; describe('Calendar', () => { let vm; @@ -7,9 +6,44 @@ describe('Calendar', () => { destroyVM(vm); }); - it('create', () => { - vm = createTest(Calendar, true); - expect(vm.$el).to.exist; + it('create', async() => { + vm = createVue({ + template: ` + + `, + data() { + return { + value: new Date('2019-04-01') + }; + } + }, true); + const titleEl = vm.$el.querySelector('.el-calendar__title'); + expect(/2019.*4/.test(titleEl.innerText)).to.be.true; + expect(vm.$el.querySelectorAll('thead th').length).to.equal(7); + const rows = vm.$el.querySelectorAll('.el-calendar-table__row'); + expect(rows.length).to.equal(6); + rows[5].firstElementChild.click(); + + waitImmediate(); + + expect(/2019.*5/.test(titleEl.innerText)).to.be.true; + const value = vm.value; + expect(value.getFullYear()).to.be.equal(2019); + expect(value.getMonth()).to.be.equal(4); + expect(vm.$el.querySelector('.is-selected span').innerText).to.be.equal('6'); + }); + + it('range', () => { + vm = createVue({ + template: ` + + ` + }, true); + const titleEl = vm.$el.querySelector('.el-calendar__title'); + expect(/2019.*3/.test(titleEl.innerText)).to.be.true; + const rows = vm.$el.querySelectorAll('.el-calendar-table__row'); + expect(rows.length).to.equal(3); + expect(vm.$el.querySelector('.el-calendar__button-group')).to.be.a('null'); }); }); From 911792fca2dd017671a5eceb8081c32b16cfbea3 Mon Sep 17 00:00:00 2001 From: ziyoung Date: Mon, 1 Apr 2019 14:38:07 +0800 Subject: [PATCH 08/16] =?UTF-8?q?Calendar=EF=BC=9A=20fix=20test=20and=20ad?= =?UTF-8?q?d=20width=20as=20scss=20variable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/theme-chalk/src/calendar.scss | 2 +- packages/theme-chalk/src/common/var.scss | 1 + test/unit/specs/calendar.spec.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/theme-chalk/src/calendar.scss b/packages/theme-chalk/src/calendar.scss index 22427297de..caf907bbc9 100644 --- a/packages/theme-chalk/src/calendar.scss +++ b/packages/theme-chalk/src/calendar.scss @@ -62,7 +62,7 @@ @include b(calendar-day) { box-sizing: border-box; padding: 8px; - height: 85px; + height: $--calendar-cell-width; &:hover { cursor: pointer; background-color: $--calendar-selected-background-color; diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss index a3f807b84d..09dc10e429 100644 --- a/packages/theme-chalk/src/common/var.scss +++ b/packages/theme-chalk/src/common/var.scss @@ -891,6 +891,7 @@ $--link-info-font-color: $--color-info !default; $--calendar-border: $--table-border !default; /// color||Other|4 $--calendar-selected-background-color: #F2F8FE !default; +$--calendar-cell-width: 85px !default; /* Break-point --------------------------*/ diff --git a/test/unit/specs/calendar.spec.js b/test/unit/specs/calendar.spec.js index 9afd7fd979..b46cc52529 100644 --- a/test/unit/specs/calendar.spec.js +++ b/test/unit/specs/calendar.spec.js @@ -24,7 +24,7 @@ describe('Calendar', () => { expect(rows.length).to.equal(6); rows[5].firstElementChild.click(); - waitImmediate(); + await waitImmediate(); expect(/2019.*5/.test(titleEl.innerText)).to.be.true; const value = vm.value; From 544e8d06d923fc0012f1509e82e84d29e586286c Mon Sep 17 00:00:00 2001 From: ziyoung Date: Tue, 16 Apr 2019 17:38:52 +0800 Subject: [PATCH 09/16] support range --- packages/calendar/src/date-table.vue | 81 ++++++++++++++++++-------- packages/calendar/src/main.vue | 63 ++++++++++++++++---- packages/theme-chalk/src/calendar.scss | 13 ++++- src/utils/date-util.js | 4 ++ 4 files changed, 125 insertions(+), 36 deletions(-) diff --git a/packages/calendar/src/date-table.vue b/packages/calendar/src/date-table.vue index f4bfd3f5ec..85cfdefcd2 100644 --- a/packages/calendar/src/date-table.vue +++ b/packages/calendar/src/date-table.vue @@ -1,18 +1,30 @@ +``` +::: + +### Custom Content + +:::demo Customize what is displayed in the calendar cell by setting `scoped-slot` named `dateCell`. In `scoped-slot` you can get the date (the date of the current cell), data (including the type, isSelected, day attribute). For details, please refer to the API documentation below. +```html + + + + +``` +::: + +### Range + +:::demo Set the `range` attribute to specify the display range of the calendar. Start time must be Monday, end time must be Sunday, and the time span cannot exceed two months. +```html + + +``` +::: + +### Attributes +| Attribute | Description | Type | Accepted Values | Default | +|-----------------|-------------- |---------- |---------------------- |--------- | +| value / v-model | binding value | Date/string/number | — | — | +| range | time range, including start time and end time. Start time must be Monday, end time must be Sunday, the time span cannot exceed two months | Array | — | — | + +### dateCell scoped slot 参数 +| Attribute | Description | Type | Accepted Values | Default | +|-----------------|-------------- |---------- |---------------------- |--------- | +| date | date the cell represents | Date | — | — | +| data | { type, isSelected, day}. `type` indicates which month the date belongs, optional values are prev-month, current-month, next-month; `isSelected` indicates whether the date is selected; `day` is the formatted date in the format yyyy-MM-dd | Object | — | — | diff --git a/examples/docs/fr-FR/calendar.md b/examples/docs/fr-FR/calendar.md new file mode 100644 index 0000000000..6dc2d14445 --- /dev/null +++ b/examples/docs/fr-FR/calendar.md @@ -0,0 +1,60 @@ +## Calendar + +Display date. + +### Basic + +:::demo Set `value` to specify the currently displayed month. If `value` is not specified, current month is displayed. `value` supports two-way binding. +```html + + + + +``` +::: + +### Custom Content + +:::demo Customize what is displayed in the calendar cell by setting `scoped-slot` named `dateCell`. In `scoped-slot` you can get the date (the date of the current cell), data (including the type, isSelected, day attribute). For details, please refer to the API documentation below. +```html + + + + +``` +::: + +### Range + +:::demo Set the `range` attribute to specify the display range of the calendar. Start time must be Monday, end time must be Sunday, and the time span cannot exceed two months. +```html + + +``` +::: + +### Attributes +| Attribute | Description | Type | Accepted Values | Default | +|-----------------|-------------- |---------- |---------------------- |--------- | +| value / v-model | binding value | Date/string/number | — | — | +| range | time range, including start time and end time. Start time must be Monday, end time must be Sunday, the time span cannot exceed two months | Array | — | — | + +### dateCell scoped slot 参数 +| Attribute | Description | Type | Accepted Values | Default | +|-----------------|-------------- |---------- |---------------------- |--------- | +| date | date the cell represents | Date | — | — | +| data | { type, isSelected, day}. `type` indicates which month the date belongs, optional values are prev-month, current-month, next-month; `isSelected` indicates whether the date is selected; `day` is the formatted date in the format yyyy-MM-dd | Object | — | — | diff --git a/examples/docs/zh-CN/calendar.md b/examples/docs/zh-CN/calendar.md index db1da5ebb9..7a18d24eb2 100644 --- a/examples/docs/zh-CN/calendar.md +++ b/examples/docs/zh-CN/calendar.md @@ -40,7 +40,7 @@ ### 自定义范围 -:::demo 设置 `range` 属性指定日历的显示范围。开始时间必须是周一,结束时间必须是周日,且两者必须在一个月内。 +:::demo 设置 `range` 属性指定日历的显示范围。开始时间必须是周一,结束时间必须是周日,且时间跨度不能超过两个月。 ```html @@ -51,7 +51,7 @@ | 参数 | 说明 | 类型 | 可选值 | 默认值 | |-----------------|-------------- |---------- |------------ |-------- | | value / v-model | 绑定值 | Date/string/number | — | — | -| range | 时间范围,包括开始时间与结束时间。开始时间必须是周一,结束时间必须是周日,且必须在同一个月内 | Array | — | — | +| range | 时间范围,包括开始时间与结束时间。开始时间必须是周一,结束时间必须是周日,且时间跨度不能超过两个月。 | Array | — | — | ### dateCell scoped slot 参数 | 参数 | 说明 | 类型 | 可选值 | 默认值 | diff --git a/test/unit/specs/calendar.spec.js b/test/unit/specs/calendar.spec.js index b46cc52529..d001565201 100644 --- a/test/unit/specs/calendar.spec.js +++ b/test/unit/specs/calendar.spec.js @@ -45,5 +45,26 @@ describe('Calendar', () => { expect(rows.length).to.equal(3); expect(vm.$el.querySelector('.el-calendar__button-group')).to.be.a('null'); }); + + it('range tow monthes', async() => { + vm = createVue({ + template: ` + + ` + }, true); + const titleEl = vm.$el.querySelector('.el-calendar__title'); + expect(/2019.*4/.test(titleEl.innerText)).to.be.true; + const dateTables = vm.$el.querySelectorAll('.el-calendar-table.is-range'); + expect(dateTables.length).to.be.equal(2); + const rows = vm.$el.querySelectorAll('.el-calendar-table__row'); + expect(rows.length).to.equal(5); + const cell = rows[rows.length - 1].firstElementChild(); + cell.click(); + + await waitImmediate(); + + expect(/2019.*5/.test(titleEl.innerText)).to.be.true; + expect(cell.classList.contains('.is-selected')).to.be.true; + }); }); From 0be44db6c00cc91a525739ce3eb9002a86e53b88 Mon Sep 17 00:00:00 2001 From: ziyoung Date: Tue, 16 Apr 2019 18:01:13 +0800 Subject: [PATCH 11/16] fix --- packages/calendar/src/date-table.vue | 5 +---- test/unit/specs/calendar.spec.js | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/calendar/src/date-table.vue b/packages/calendar/src/date-table.vue index 85cfdefcd2..3178cd92c6 100644 --- a/packages/calendar/src/date-table.vue +++ b/packages/calendar/src/date-table.vue @@ -14,10 +14,7 @@ export default { } }, date: Date, - hideHeader: { - type: Boolean, - default: true - } + hideHeader: Boolean }, inject: ['elCalendar'], diff --git a/test/unit/specs/calendar.spec.js b/test/unit/specs/calendar.spec.js index d001565201..2c95d42e1e 100644 --- a/test/unit/specs/calendar.spec.js +++ b/test/unit/specs/calendar.spec.js @@ -58,13 +58,13 @@ describe('Calendar', () => { expect(dateTables.length).to.be.equal(2); const rows = vm.$el.querySelectorAll('.el-calendar-table__row'); expect(rows.length).to.equal(5); - const cell = rows[rows.length - 1].firstElementChild(); + const cell = rows[rows.length - 1].firstElementChild; cell.click(); await waitImmediate(); expect(/2019.*5/.test(titleEl.innerText)).to.be.true; - expect(cell.classList.contains('.is-selected')).to.be.true; + expect(cell.classList.contains('is-selected')).to.be.true; }); }); From 2abedcebff27db1c1f6bdc5d3997d4860a053f2c Mon Sep 17 00:00:00 2001 From: ziyoung Date: Mon, 22 Apr 2019 11:11:58 +0800 Subject: [PATCH 12/16] fix style --- packages/theme-chalk/src/calendar.scss | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/theme-chalk/src/calendar.scss b/packages/theme-chalk/src/calendar.scss index 19176f1464..f33254d381 100644 --- a/packages/theme-chalk/src/calendar.scss +++ b/packages/theme-chalk/src/calendar.scss @@ -31,20 +31,19 @@ font-weight: normal; } + &:not(.is-range) { + td.prev, + td.next { + color: $--color-text-placeholder; + } + } + td { border-bottom: $--calendar-border; border-right: $--calendar-border; vertical-align: top; transition: background-color 0.2s ease; - &::not(.is-range) { - &.prev, - &.next { - color: $--color-text-placeholder; - } - } - - @include when(selected) { background-color: $--calendar-selected-background-color; } From 29734d7c9cafc64279df0ba19c2ef453af62c024 Mon Sep 17 00:00:00 2001 From: ziyoung Date: Mon, 22 Apr 2019 12:03:08 +0800 Subject: [PATCH 13/16] add missing definition file --- types/calendar.d.ts | 7 +++++++ types/element-ui.d.ts | 2 ++ 2 files changed, 9 insertions(+) diff --git a/types/calendar.d.ts b/types/calendar.d.ts index aff6f4b047..ba0f9843a6 100644 --- a/types/calendar.d.ts +++ b/types/calendar.d.ts @@ -1,5 +1,12 @@ import { ElementUIComponent } from './component' +export type DateType = Date | String | Number + /** Calendar Component */ export declare class ElCalendar extends ElementUIComponent { + /** Binding value */ + value: DateType + + /** Specify the display range of the calendar */ + range: DateType[] } diff --git a/types/element-ui.d.ts b/types/element-ui.d.ts index 4af76cacb9..2edc6306f6 100644 --- a/types/element-ui.d.ts +++ b/types/element-ui.d.ts @@ -70,6 +70,8 @@ import { ElTransfer } from './transfer' import { ElTree } from './tree' import { ElUpload } from './upload' import { ElDivider } from './divider' +import { ElCalendar } from './calendar' +import { ElImage } from './image' export interface InstallationOptions { locale: any, From 5194950cc8a9d0c82b702f5e83fc20fc650169ee Mon Sep 17 00:00:00 2001 From: ziyoung Date: Mon, 22 Apr 2019 15:29:06 +0800 Subject: [PATCH 14/16] update demo --- examples/docs/en-US/calendar.md | 9 +++++++-- examples/docs/es/calendar.md | 9 +++++++-- examples/docs/fr-FR/calendar.md | 9 +++++++-- examples/docs/zh-CN/calendar.md | 9 +++++++-- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/examples/docs/en-US/calendar.md b/examples/docs/en-US/calendar.md index 6dc2d14445..dc5bcd13ec 100644 --- a/examples/docs/en-US/calendar.md +++ b/examples/docs/en-US/calendar.md @@ -30,11 +30,16 @@ Display date. + ``` ::: diff --git a/examples/docs/es/calendar.md b/examples/docs/es/calendar.md index 6dc2d14445..dc5bcd13ec 100644 --- a/examples/docs/es/calendar.md +++ b/examples/docs/es/calendar.md @@ -30,11 +30,16 @@ Display date. + ``` ::: diff --git a/examples/docs/fr-FR/calendar.md b/examples/docs/fr-FR/calendar.md index 6dc2d14445..dc5bcd13ec 100644 --- a/examples/docs/fr-FR/calendar.md +++ b/examples/docs/fr-FR/calendar.md @@ -30,11 +30,16 @@ Display date. + ``` ::: diff --git a/examples/docs/zh-CN/calendar.md b/examples/docs/zh-CN/calendar.md index 7a18d24eb2..b56ff768e0 100644 --- a/examples/docs/zh-CN/calendar.md +++ b/examples/docs/zh-CN/calendar.md @@ -30,11 +30,16 @@ + ``` ::: From 152966f3fd605e36b366cfebe908e93689637431 Mon Sep 17 00:00:00 2001 From: ziyoung Date: Mon, 22 Apr 2019 16:55:38 +0800 Subject: [PATCH 15/16] update demo --- examples/demo-styles/calendar.scss | 5 +++++ examples/demo-styles/index.scss | 1 + examples/docs/en-US/calendar.md | 2 +- examples/docs/es/calendar.md | 2 +- examples/docs/fr-FR/calendar.md | 2 +- examples/docs/zh-CN/calendar.md | 2 +- 6 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 examples/demo-styles/calendar.scss diff --git a/examples/demo-styles/calendar.scss b/examples/demo-styles/calendar.scss new file mode 100644 index 0000000000..62198bd1c8 --- /dev/null +++ b/examples/demo-styles/calendar.scss @@ -0,0 +1,5 @@ +.demo-calendar.demo-block { + .is-selected { + color: #1989FA; + } +} diff --git a/examples/demo-styles/index.scss b/examples/demo-styles/index.scss index 026f06251f..ae0f0d8b31 100644 --- a/examples/demo-styles/index.scss +++ b/examples/demo-styles/index.scss @@ -2,6 +2,7 @@ @import "./badge.scss"; @import "./border.scss"; @import "./button.scss"; +@import "./calendar.scss"; @import "./card.scss"; @import "./carousel.scss"; @import "./cascader.scss"; diff --git a/examples/docs/en-US/calendar.md b/examples/docs/en-US/calendar.md index dc5bcd13ec..55c265b44b 100644 --- a/examples/docs/en-US/calendar.md +++ b/examples/docs/en-US/calendar.md @@ -31,7 +31,7 @@ Display date. slot="dateCell" slot-scope="{date, data}">

- {{ data.day }} is {{ data.isSelected ? '✔️' : ''}} + {{ data.day.split('-').slice(1).join('-') }} {{ data.isSelected ? '✔️' : ''}}

diff --git a/examples/docs/es/calendar.md b/examples/docs/es/calendar.md index dc5bcd13ec..55c265b44b 100644 --- a/examples/docs/es/calendar.md +++ b/examples/docs/es/calendar.md @@ -31,7 +31,7 @@ Display date. slot="dateCell" slot-scope="{date, data}">

- {{ data.day }} is {{ data.isSelected ? '✔️' : ''}} + {{ data.day.split('-').slice(1).join('-') }} {{ data.isSelected ? '✔️' : ''}}

diff --git a/examples/docs/fr-FR/calendar.md b/examples/docs/fr-FR/calendar.md index dc5bcd13ec..55c265b44b 100644 --- a/examples/docs/fr-FR/calendar.md +++ b/examples/docs/fr-FR/calendar.md @@ -31,7 +31,7 @@ Display date. slot="dateCell" slot-scope="{date, data}">

- {{ data.day }} is {{ data.isSelected ? '✔️' : ''}} + {{ data.day.split('-').slice(1).join('-') }} {{ data.isSelected ? '✔️' : ''}}

diff --git a/examples/docs/zh-CN/calendar.md b/examples/docs/zh-CN/calendar.md index b56ff768e0..667a3a303a 100644 --- a/examples/docs/zh-CN/calendar.md +++ b/examples/docs/zh-CN/calendar.md @@ -31,7 +31,7 @@ slot="dateCell" slot-scope="{date, data}">

- {{ data.day }} is {{ data.isSelected ? '✔️' : ''}} + {{ data.day.split('-').slice(1).join('-') }} {{ data.isSelected ? '✔️' : ''}}

From 38bd7d8f947e761a34c945314eb55d8afde44670 Mon Sep 17 00:00:00 2001 From: ziyoung Date: Thu, 25 Apr 2019 10:42:02 +0800 Subject: [PATCH 16/16] update --- packages/calendar/src/date-table.vue | 10 +++++----- packages/calendar/src/main.vue | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/calendar/src/date-table.vue b/packages/calendar/src/date-table.vue index 3178cd92c6..e1c8366b8b 100644 --- a/packages/calendar/src/date-table.vue +++ b/packages/calendar/src/date-table.vue @@ -104,17 +104,17 @@ export default { // if range exists, should render days in range. if (this.isInRange) { const [start, end] = this.range; - const currentMonthDays = rangeArr(end.getDate() - start.getDate() + 1).map((_, index) => ({ + const currentMonthRange = rangeArr(end.getDate() - start.getDate() + 1).map((_, index) => ({ text: start.getDate() + index, type: 'current' })); - let remaining = currentMonthDays.length % 7; + let remaining = currentMonthRange.length % 7; remaining = remaining === 0 ? 0 : 7 - remaining; - const nextMonthDays = rangeArr(remaining).map((_, index) => ({ + const nextMonthRange = rangeArr(remaining).map((_, index) => ({ text: index + 1, type: 'next' })); - days = currentMonthDays.concat(nextMonthDays); + days = currentMonthRange.concat(nextMonthRange); } else { const date = this.date; const firstDay = getFirstDayOfMonth(date); @@ -125,7 +125,7 @@ export default { const currentMonthDays = getMonthDays(date).map(day => ({ text: day, type: 'current' - }));; + })); days = [...prevMonthDays, ...currentMonthDays]; const nextMonthDays = rangeArr(42 - days.length).map((_, index) => ({ text: index + 1, diff --git a/packages/calendar/src/main.vue b/packages/calendar/src/main.vue index 69e8512453..fa7112ab70 100644 --- a/packages/calendar/src/main.vue +++ b/packages/calendar/src/main.vue @@ -43,11 +43,11 @@ class="el-calendar__body" key="has-range">