diff --git a/docs/en-US/component/date-picker.md b/docs/en-US/component/date-picker.md index cb63856a8c..af284c0939 100644 --- a/docs/en-US/component/date-picker.md +++ b/docs/en-US/component/date-picker.md @@ -158,7 +158,7 @@ Note, date time locale (month name, first day of the week ...) are also configur | placeholder | placeholder in non-range mode | ^[string] | '' | | start-placeholder | placeholder for the start date in range mode | ^[string] | — | | end-placeholder | placeholder for the end date in range mode | ^[string] | — | -| type | type of the picker | ^[enum]`'year' \| 'month' \| 'date' \| 'dates' \| 'datetime' \| 'week' \| 'datetimerange' \| 'daterange' \| 'monthrange'` | date | +| type | type of the picker | ^[enum]`'year' \| 'years' \|'month' \| 'date' \| 'dates' \| 'datetime' \| 'week' \| 'datetimerange' \| 'daterange' \| 'monthrange'` | date | | format | format of the displayed value in the input box | ^[string] see [date formats](/en-US/component/date-picker#date-formats) | YYYY-MM-DD | | popper-class | custom class name for DatePicker's dropdown | ^[string] | — | | popper-options | Customized popper option see more at [popper.js](https://popper.js.org/docs/v2/) | ^[object]`Partial` | {} | diff --git a/packages/components/date-picker/src/date-picker-com/basic-year-table.vue b/packages/components/date-picker/src/date-picker-com/basic-year-table.vue index d89fb8f971..90ea9213f4 100644 --- a/packages/components/date-picker/src/date-picker-com/basic-year-table.vue +++ b/packages/components/date-picker/src/date-picker-com/basic-year-table.vue @@ -21,7 +21,9 @@ @keydown.space.prevent.stop="handleYearTableClick" @keydown.enter.prevent.stop="handleYearTableClick" > - {{ startYear + i * 4 + j }} +
+ {{ startYear + i * 4 + j }} +
@@ -82,7 +84,8 @@ const isSelectedCell = (year: number) => { (year === startYear.value && props.date.year() < startYear.value && props.date.year() > startYear.value + 9) || - castArray(props.date).findIndex((date) => date.year() === year) >= 0 + castArray(props.date).findIndex((date) => date.year() === year) >= 0 || + castArray(props.parsedValue).findIndex((date) => date?.year() === year) >= 0 ) } @@ -92,7 +95,18 @@ const handleYearTableClick = (event: MouseEvent | KeyboardEvent) => { if (target && target.textContent) { if (hasClass(target, 'disabled')) return const year = target.textContent || target.innerText - emit('pick', Number(year)) + if (props.selectionMode === 'years') { + if (event.type === 'keydown') { + emit('pick', castArray(props.parsedValue), false) + return + } + const newValue = hasClass(target, 'current') + ? castArray(props.parsedValue).filter((d) => d?.year() !== Number(year)) + : castArray(props.parsedValue).concat([dayjs(year)]) + emit('pick', newValue) + } else { + emit('pick', Number(year)) + } } } diff --git a/packages/components/date-picker/src/date-picker-com/panel-date-pick.vue b/packages/components/date-picker/src/date-picker-com/panel-date-pick.vue index 8cf25cbeee..4d5d9ca600 100644 --- a/packages/components/date-picker/src/date-picker-com/panel-date-pick.vue +++ b/packages/components/date-picker/src/date-picker-com/panel-date-pick.vue @@ -142,6 +142,7 @@ -
+
{ const selectionMode = computed(() => { const { type } = props - if (['week', 'month', 'year', 'dates'].includes(type)) return type + if (['week', 'month', 'year', 'years', 'dates'].includes(type)) return type return 'date' as DatePickType }) @@ -421,12 +420,17 @@ const handleMonthPick = async (month: number) => { handlePanelChange('month') } -const handleYearPick = async (year: number) => { +const handleYearPick = async ( + year: number | YearsPickerEmits, + keepOpen?: boolean +) => { if (selectionMode.value === 'year') { - innerDate.value = innerDate.value.startOf('year').year(year) + innerDate.value = innerDate.value.startOf('year').year(year as number) emit(innerDate.value, false) + } else if (selectionMode.value === 'years') { + emit(year as YearsPickerEmits, keepOpen ?? true) } else { - innerDate.value = innerDate.value.year(year) + innerDate.value = innerDate.value.year(year as number) currentView.value = 'month' if (['month', 'year', 'date', 'week'].includes(selectionMode.value)) { emit(innerDate.value, true) @@ -448,7 +452,11 @@ const showTime = computed( ) const footerVisible = computed(() => { - return showTime.value || selectionMode.value === 'dates' + const showDateFooter = showTime.value || selectionMode.value === 'dates' + const showYearFooter = selectionMode.value === 'years' + const isDateView = currentView.value === 'date' + const isYearView = currentView.value === 'year' + return (showDateFooter && isDateView) || (showYearFooter && isYearView) }) const disabledConfirm = computed(() => { @@ -460,7 +468,7 @@ const disabledConfirm = computed(() => { return disabledDate(props.parsedValue.toDate()) }) const onConfirm = () => { - if (selectionMode.value === 'dates') { + if (selectionMode.value === 'dates' || selectionMode.value === 'years') { emit(props.parsedValue as Dayjs[]) } else { // deal with the scenario where: user opens the date time picker, then confirm without doing anything @@ -585,10 +593,9 @@ const isValidValue = (date: unknown) => { } const formatToString = (value: Dayjs | Dayjs[]) => { - if (selectionMode.value === 'dates') { - return (value as Dayjs[]).map((_) => _.format(props.format)) - } - return (value as Dayjs).format(props.format) + return Array.isArray(value) + ? (value as Dayjs[]).map((_) => _.format(props.format)) + : (value as Dayjs).format(props.format) } const parseUserInput = (value: Dayjs) => { @@ -730,6 +737,9 @@ watch( if (['month', 'year'].includes(val)) { currentView.value = val return + } else if (val === 'years') { + currentView.value = 'year' + return } currentView.value = 'date' }, @@ -757,7 +767,8 @@ watch( () => props.parsedValue, (val) => { if (val) { - if (selectionMode.value === 'dates') return + if (selectionMode.value === 'dates' || selectionMode.value === 'years') + return if (Array.isArray(val)) return innerDate.value = val } else { diff --git a/packages/components/date-picker/src/date-picker.type.ts b/packages/components/date-picker/src/date-picker.type.ts index efae9ef62e..373c71ddab 100644 --- a/packages/components/date-picker/src/date-picker.type.ts +++ b/packages/components/date-picker/src/date-picker.type.ts @@ -2,6 +2,7 @@ import type { Dayjs } from 'dayjs' export declare type IDatePickerType = | 'year' + | 'years' | 'month' | 'date' | 'dates' diff --git a/packages/components/date-picker/src/props/basic-date-table.ts b/packages/components/date-picker/src/props/basic-date-table.ts index de39e40a47..d01fb95f79 100644 --- a/packages/components/date-picker/src/props/basic-date-table.ts +++ b/packages/components/date-picker/src/props/basic-date-table.ts @@ -21,6 +21,7 @@ export type BasicDateTableEmits = typeof basicDateTableEmits export type RangePickerEmits = { minDate: Dayjs; maxDate: null } export type DatePickerEmits = Dayjs export type DatesPickerEmits = Dayjs[] +export type YearsPickerEmits = Dayjs[] export type WeekPickerEmits = { year: number week: number diff --git a/packages/components/date-picker/src/props/basic-year-table.ts b/packages/components/date-picker/src/props/basic-year-table.ts index a9b752a8ca..2861e0841e 100644 --- a/packages/components/date-picker/src/props/basic-year-table.ts +++ b/packages/components/date-picker/src/props/basic-year-table.ts @@ -1,5 +1,5 @@ import { buildProps } from '@element-plus/utils' -import { datePickerSharedProps } from './shared' +import { datePickerSharedProps, selectionModeWithDefault } from './shared' import type { ExtractPropTypes } from 'vue' @@ -9,6 +9,7 @@ export const basicYearTableProps = buildProps({ date, disabledDate, parsedValue, -}) + selectionMode: selectionModeWithDefault('year'), +} as const) export type BasicYearTableProps = ExtractPropTypes diff --git a/packages/components/date-picker/src/props/shared.ts b/packages/components/date-picker/src/props/shared.ts index 11b8243059..22a849171e 100644 --- a/packages/components/date-picker/src/props/shared.ts +++ b/packages/components/date-picker/src/props/shared.ts @@ -5,7 +5,15 @@ import type { ExtractPropTypes } from 'vue' import type { Dayjs } from 'dayjs' import type { DatePickType } from '@element-plus/constants' -const selectionModes = ['date', 'dates', 'year', 'month', 'week', 'range'] +const selectionModes = [ + 'date', + 'dates', + 'year', + 'years', + 'month', + 'week', + 'range', +] export type RangeState = { endDate: null | Dayjs diff --git a/packages/components/time-picker/src/common/picker.vue b/packages/components/time-picker/src/common/picker.vue index b653de1287..09be004938 100644 --- a/packages/components/time-picker/src/common/picker.vue +++ b/packages/components/time-picker/src/common/picker.vue @@ -33,7 +33,13 @@ :placeholder="placeholder" :class="[nsDate.b('editor'), nsDate.bm('editor', type), $attrs.class]" :style="$attrs.style" - :readonly="!editable || readonly || isDatesPicker || type === 'week'" + :readonly=" + !editable || + readonly || + isDatesPicker || + isYearsPicker || + type === 'week' + " :label="label" :tabindex="tabindex" :validate-event="false" @@ -470,7 +476,7 @@ const displayValue = computed(() => { if (!isTimePicker.value && valueIsEmpty.value) return '' if (!pickerVisible.value && valueIsEmpty.value) return '' if (formattedValue) { - return isDatesPicker.value + return isDatesPicker.value || isYearsPicker.value ? (formattedValue as Array).join(', ') : formattedValue } @@ -483,6 +489,8 @@ const isTimePicker = computed(() => props.type.startsWith('time')) const isDatesPicker = computed(() => props.type === 'dates') +const isYearsPicker = computed(() => props.type === 'years') + const triggerIcon = computed( () => props.prefixIcon || (isTimeLikePicker.value ? Clock : Calendar) ) diff --git a/packages/components/time-picker/src/constants.ts b/packages/components/time-picker/src/constants.ts index 8f84c61ad1..3244993790 100644 --- a/packages/components/time-picker/src/constants.ts +++ b/packages/components/time-picker/src/constants.ts @@ -7,6 +7,7 @@ export const DEFAULT_FORMATS_DATEPICKER = { dates: DEFAULT_FORMATS_DATE, week: 'gggg[w]ww', year: 'YYYY', + years: 'YYYY', month: 'YYYY-MM', datetime: `${DEFAULT_FORMATS_DATE} ${DEFAULT_FORMATS_TIME}`, monthrange: 'YYYY-MM', diff --git a/packages/constants/date.ts b/packages/constants/date.ts index b6976ce1fa..0373851137 100644 --- a/packages/constants/date.ts +++ b/packages/constants/date.ts @@ -1,5 +1,6 @@ export const datePickTypes = [ 'year', + 'years', 'month', 'date', 'dates', diff --git a/packages/theme-chalk/src/date-picker/date-table.scss b/packages/theme-chalk/src/date-picker/date-table.scss index 4ca42868af..50e4a03409 100644 --- a/packages/theme-chalk/src/date-picker/date-table.scss +++ b/packages/theme-chalk/src/date-picker/date-table.scss @@ -130,11 +130,7 @@ &.selected .#{$namespace}-date-table-cell { margin-left: 5px; margin-right: 5px; - background-color: getCssVar('datepicker-inrange-bg-color'); border-radius: 15px; - &:hover { - background-color: getCssVar('datepicker-inrange-hover-bg-color'); - } } &.selected .#{$namespace}-date-table-cell__text { diff --git a/packages/theme-chalk/src/date-picker/month-table.scss b/packages/theme-chalk/src/date-picker/month-table.scss index eb7f35e622..ced74450cf 100644 --- a/packages/theme-chalk/src/date-picker/month-table.scss +++ b/packages/theme-chalk/src/date-picker/month-table.scss @@ -7,9 +7,11 @@ border-collapse: collapse; td { + width: 68px; text-align: center; padding: 8px 0; cursor: pointer; + position: relative; & div { height: 48px; padding: 6px 0; @@ -37,13 +39,16 @@ } .cell { - width: 60px; + width: 54px; height: 36px; display: block; line-height: 36px; color: getCssVar('datepicker-text-color'); margin: 0 auto; border-radius: 18px; + position: absolute; + left: 50%; + transform: translateX(-50%); &:hover { color: getCssVar('datepicker-hover-text-color'); } @@ -67,23 +72,33 @@ } &.start-date div { + margin-left: 3px; border-top-left-radius: 24px; border-bottom-left-radius: 24px; } &.end-date div { + margin-right: 3px; border-top-right-radius: 24px; border-bottom-right-radius: 24px; } + &.current:not(.disabled) div { + border-radius: 24px; + margin-left: 3px; + margin-right: 3px; + } + &.current:not(.disabled) .cell { - color: getCssVar('datepicker-active-color'); + color: $color-white; + background-color: getCssVar('datepicker-active-color'); } &:focus-visible { outline: none; .cell { outline: 2px solid getCssVar('datepicker-active-color'); + outline-offset: 1px; } } } diff --git a/packages/theme-chalk/src/date-picker/year-table.scss b/packages/theme-chalk/src/date-picker/year-table.scss index 520ba17fa7..e37a38f8bc 100644 --- a/packages/theme-chalk/src/date-picker/year-table.scss +++ b/packages/theme-chalk/src/date-picker/year-table.scss @@ -11,9 +11,17 @@ } td { + width: 68px; text-align: center; - padding: 20px 3px; + padding: 8px 0px; cursor: pointer; + position: relative; + + & div { + height: 48px; + padding: 6px 0; + box-sizing: border-box; + } &.today { .cell { @@ -33,27 +41,38 @@ } .cell { - width: 48px; + width: 54px; height: 36px; display: block; line-height: 36px; color: getCssVar('datepicker-text-color'); border-radius: 18px; margin: 0 auto; + position: absolute; + left: 50%; + transform: translateX(-50%); &:hover { color: getCssVar('datepicker-hover-text-color'); } } + &.current:not(.disabled) div { + border-radius: 24px; + margin-left: 3px; + margin-right: 3px; + } + &.current:not(.disabled) .cell { - color: getCssVar('datepicker-active-color'); + color: $color-white; + background-color: getCssVar('datepicker-active-color'); } &:focus-visible { outline: none; .cell { outline: 2px solid getCssVar('datepicker-active-color'); + outline-offset: 1px; } } }