refactor(date-picker): clean codes

This commit is contained in:
07akioni 2021-10-07 01:47:35 +08:00
parent 7769a20c2d
commit ea92f5c0b1
9 changed files with 115 additions and 116 deletions

View File

@ -20,6 +20,7 @@ import { clickoutside } from 'vdirs'
import { format, getTime, isValid, getYear, getMonth } from 'date-fns'
import { useIsMounted, useMergedState } from 'vooks'
import { happensIn } from 'seemly'
import type { Size as TimePickerSize } from '../../time-picker/src/interface'
import { InputInst, InputProps, NInput } from '../../input'
import { NBaseIcon } from '../../_internal'
import { useFormItem, useTheme, useConfig, useLocale } from '../../_mixins'
@ -29,11 +30,22 @@ import { warn, call, useAdjustedTo, createKey } from '../../_utils'
import type { MaybeArray, ExtractPublicPropTypes } from '../../_utils'
import { datePickerLight } from '../styles'
import { strictParse } from './utils'
// import { getDerivedTimeFromKeyboardEvent } from './utils'
import {
uniCalendarValidation,
dualCalendarValidation
} from './validation-utils'
import { MONTH_ITEM_HEIGHT, START_YEAR } from './config'
import type {
OnUpdateValue,
OnUpdateValueImpl,
Value,
PanelRef,
IsDateDisabled,
IsTimeDisabled,
Shortcuts,
FirstDayOfWeek
} from './interface'
import { datePickerInjectionKey } from './interface'
import DatetimePanel from './panel/datetime'
import DatetimerangePanel from './panel/datetimerange'
import DatePanel from './panel/date'
@ -41,18 +53,6 @@ import DaterangePanel from './panel/daterange'
import MonthPanel from './panel/month'
import style from './styles/index.cssr'
import { DatePickerTheme } from '../styles/light'
import {
OnUpdateValue,
OnUpdateValueImpl,
Value,
PanelRef,
IsDateDisabled,
IsTimeDisabled,
datePickerInjectionKey,
Shortcuts,
FirstDayOfWeek
} from './interface'
import { Size as TimePickerSize } from '../../time-picker/src/interface'
const DATE_FORMAT = {
date: 'yyyy-MM-dd',
@ -352,7 +352,7 @@ export default defineComponent({
? getMonth(Date.now())
: getMonth(mergedValue as number)
: getMonth(value)
monthScrollRef.scrollTo({ top: monthIndex * 40 })
monthScrollRef.scrollTo({ top: monthIndex * MONTH_ITEM_HEIGHT })
}
if (yearScrollRef) {
const yearIndex =
@ -360,8 +360,8 @@ export default defineComponent({
? mergedValue === null
? getYear(Date.now())
: getYear(mergedValue as number)
: getYear(value)) - 1900
yearScrollRef.scrollTo({ top: yearIndex * 40 })
: getYear(value)) - START_YEAR
yearScrollRef.scrollTo({ top: yearIndex * MONTH_ITEM_HEIGHT })
}
}
// --- Panel update value
@ -649,8 +649,8 @@ export default defineComponent({
itemSize,
itemCellWidth,
itemCellHeight,
itemMonthCellWidth,
itemMonthCellHeight,
scrollItemWidth,
scrollItemHeight,
calendarTitlePadding,
calendarTitleHeight,
calendarDaysHeight,
@ -661,6 +661,7 @@ export default defineComponent({
calendarTitleGridTempateColumns,
iconColor,
iconColorDisabled,
scrollItemBorderRadius,
[createKey('calendarLeftPadding', type)]: calendarLeftPadding,
[createKey('calendarRightPadding', type)]: calendarRightPadding
}
@ -704,8 +705,6 @@ export default defineComponent({
'--item-size': itemSize,
'--item-cell-width': itemCellWidth,
'--item-cell-height': itemCellHeight,
'--item-month-cell-width': itemMonthCellWidth,
'--item-month-cell-height': itemMonthCellHeight,
'--item-text-color': itemTextColor,
'--item-color-included': itemColorIncluded,
'--item-color-disabled': itemColorDisabled,
@ -714,6 +713,11 @@ export default defineComponent({
'--item-text-color-disabled': itemTextColorDisabled,
'--item-text-color-active': itemTextColorActive,
// scroll item
'--scroll-item-width': scrollItemWidth,
'--scroll-item-height': scrollItemHeight,
'--scroll-item-border-radius': scrollItemBorderRadius,
// panel arrow
'--arrow-size': arrowSize,
'--arrow-color': arrowColor,

View File

@ -0,0 +1,3 @@
export const START_YEAR = 1901
// TODO: we need to remove it to make height customizable
export const MONTH_ITEM_HEIGHT = 40

View File

@ -43,8 +43,9 @@ export type OnClose = (disableUpdateOnClose: boolean) => void
export interface PanelRef {
$el: HTMLElement
monthScrollRef: ScrollbarInst | null
yearScrollRef: VirtualListInst | null
// Only exists when type is month
monthScrollRef?: ScrollbarInst | null
yearScrollRef?: VirtualListInst | null
}
// 0 is Monday

View File

@ -1,9 +1,10 @@
import { h, defineComponent, renderSlot, VNode } from 'vue'
import { h, defineComponent, VNode } from 'vue'
import { VirtualList } from 'vueuc'
import { NButton, NxButton } from '../../../button'
import { NBaseFocusDetector, NScrollbar } from '../../../_internal'
import { useCalendar } from './use-calendar'
import type { MonthItem, YearItem } from '../utils'
import { MONTH_ITEM_HEIGHT } from '../config'
/**
* Month Panel
@ -31,8 +32,8 @@ export default defineComponent({
{
[`${mergedClsPrefix}-date-panel-month-calendar__picker-col-item--current`]:
item.type === 'month'
? item.inCurrentMonth
: item.inCurrentYear,
? item.isCurrentMonth
: item.isCurrentYear,
[`${mergedClsPrefix}-date-panel-month-calendar__picker-col-item--selected`]:
item.selected,
[`${mergedClsPrefix}-date-panel-month-calendar__picker-col-item--disabled`]:
@ -41,14 +42,17 @@ export default defineComponent({
]}
onClick={() => handleDateClick(item)}
>
{item.type === 'month' ? item.formattedText : item.dateObject.year}
{item.type === 'month'
? item.dateObject.month + 1
: item.dateObject.year}
</div>
)
}
return { ...useCalendarRef, renderItem }
},
render () {
const { mergedClsPrefix, mergedTheme, shortcuts, renderItem } = this
const { mergedClsPrefix, mergedTheme, shortcuts, actions, renderItem } =
this
return (
<div
ref="selfRef"
@ -65,23 +69,28 @@ export default defineComponent({
themeOverrides={mergedTheme.peerOverrides.Scrollbar}
container={this.virtualListContainer}
content={this.virtualListContent}
horizontalRailStyle={{ zIndex: 3 }}
verticalRailStyle={{ zIndex: 3 }}
horizontalRailStyle={{ zIndex: 1 }}
verticalRailStyle={{ zIndex: 1 }}
>
{{
default: () => (
<VirtualList
ref="yearScrollRef"
items={this.yearArray.map((yearItem, i) =>
renderItem(yearItem, i, mergedClsPrefix)
)}
itemSize={40}
items={this.yearArray}
itemSize={MONTH_ITEM_HEIGHT}
showScrollbar={false}
keyField="ts"
onScroll={this.handleVirtualListScroll}
>
{{
default: ({ item }: { item: VNode }) => {
return item
default: ({
item,
index
}: {
item: YearItem
index: number
}) => {
return renderItem(item, index, mergedClsPrefix)
}
}}
</VirtualList>
@ -98,7 +107,7 @@ export default defineComponent({
>
{{
default: () => [
...this.monthArray.map((monthItem, i) =>
this.monthArray.map((monthItem, i) =>
renderItem(monthItem, i, mergedClsPrefix)
),
<div
@ -111,10 +120,12 @@ export default defineComponent({
</div>
{this.datePickerSlots.footer ? (
<div class={`${mergedClsPrefix}-date-panel-footer`}>
{renderSlot(this.datePickerSlots, 'footer')}
{{
default: this.datePickerSlots.footer
}}
</div>
) : null}
{this.actions?.length || shortcuts ? (
{actions?.length || shortcuts ? (
<div class={`${mergedClsPrefix}-date-panel-actions`}>
<div class={`${mergedClsPrefix}-date-panel-actions__prefix`}>
{shortcuts &&
@ -142,7 +153,7 @@ export default defineComponent({
})}
</div>
<div class={`${mergedClsPrefix}-date-panel-actions__suffix`}>
{this.actions?.includes('clear') ? (
{actions?.includes('clear') ? (
<NButton
theme={mergedTheme.peers.Button}
themeOverrides={mergedTheme.peerOverrides.Button}
@ -152,7 +163,7 @@ export default defineComponent({
{{ default: () => this.locale.clear }}
</NButton>
) : null}
{this.actions?.includes('now') ? (
{actions?.includes('now') ? (
<NButton
theme={mergedTheme.peers.Button}
themeOverrides={mergedTheme.peerOverrides.Button}
@ -162,7 +173,7 @@ export default defineComponent({
{{ default: () => this.locale.now }}
</NButton>
) : null}
{this.actions?.includes('confirm') ? (
{actions?.includes('confirm') ? (
<NButton
theme={mergedTheme.peers.Button}
themeOverrides={mergedTheme.peerOverrides.Button}

View File

@ -71,6 +71,7 @@ function useCalendar (
: props.value
)
const yearScrollRef = ref<VirtualListInst | null>(null)
const monthScrollRef = ref<ScrollbarInst | null>(null)
const scrollbarInstRef = ref<ScrollbarInst | null>(null)
const nowRef = ref(Date.now())
const dateArrayRef = computed(() => {
@ -82,16 +83,7 @@ function useCalendar (
)
})
const monthArrayRef = computed(() => {
return monthArray(calendarValueRef.value, props.value, nowRef.value).map(
(item) => {
item.formattedText = format(
item.ts,
localeRef.value.monthFormat,
panelCommon.dateFnsOptions.value
)
return item
}
)
return monthArray(calendarValueRef.value, props.value, nowRef.value)
})
const yearArrayRef = computed(() => {
return yearArray(calendarValueRef.value, props.value, nowRef.value)
@ -223,7 +215,7 @@ function useCalendar (
newValue = Date.now()
}
newValue = getTime(set(newValue, dateItem.dateObject))
panelCommon.doUpdateValue(getTime(sanitizeValue(newValue)), type === 'date')
panelCommon.doUpdateValue(sanitizeValue(newValue), type === 'date')
if (type === 'date') {
panelCommon.doClose()
} else if (type === 'month') {
@ -271,14 +263,17 @@ function useCalendar (
function prevMonth (): void {
calendarValueRef.value = getTime(addMonths(calendarValueRef.value, -1))
}
// For month type
function virtualListContainer (): HTMLElement {
const { value } = yearScrollRef
return value?.listElRef as HTMLElement
}
// For month type
function virtualListContent (): HTMLElement {
const { value } = yearScrollRef
return value?.itemsElRef as HTMLElement
}
// For month type
function handleVirtualListScroll (e: Event): void {
scrollbarInstRef.value?.sync()
}
@ -313,7 +308,7 @@ function useCalendar (
timePickerSize: panelCommon.timePickerSize,
dateInputValue: dateInputValueRef,
datePickerSlots,
monthScrollRef: ref(null),
monthScrollRef,
yearScrollRef,
scrollbarInstRef
}

View File

@ -40,8 +40,6 @@ import fadeInScaleUpTransition from '../../../_styles/transitions/fade-in-scale-
// --item-size
// --item-cell-width
// --item-cell-height
// --item-month-cell-width
// --item-month-cell-height
// --item-text-color
// --item-color-included
// --item-color-disabled
@ -51,6 +49,11 @@ import fadeInScaleUpTransition from '../../../_styles/transitions/fade-in-scale-
// --item-text-color-disabled
// --item-text-color-active
// scroll item
// --scroll-item-width
// --scroll-item-height
// --scroll-item-border-radius
// panel arrow
// --arrow-size
// --arrow-color
@ -96,24 +99,25 @@ export default c([
gridArea: 'left-calendar'
}, [
cE('picker-col', `
min-width: var(--item-month-cell-width);
height: calc(var(--item-month-cell-height) * 7);
min-width: var(--scroll-item-width);
height: calc(var(--scroll-item-height) * 6);
user-select: none;
`, [
c('&:first-child', `
min-width: calc(var(--item-month-cell-width) + 4px);
min-width: calc(var(--scroll-item-width) + 4px);
`, [
cE('picker-col-item', [
c('&::before', 'left: 4px;')
])
]),
cE('padding', `
height: calc(var(--item-month-cell-height) * 6)
height: calc(var(--scroll-item-height) * 5)
`)
]),
cE('picker-col-item', `
z-index: 0;
cursor: pointer;
height: var(--item-month-cell-height);
height: var(--scroll-item-height);
box-sizing: border-box;
padding-top: 4px;
display: flex;
@ -134,7 +138,7 @@ export default c([
right: 4px;
top: 4px;
bottom: 0;
border-radius: var(--panel-border-radius);
border-radius: var(--scroll-item-border-radius);
transition:
background-color .3s var(--bezier);
`),
@ -152,11 +156,7 @@ export default c([
background-color: var(--item-color-disabled);
cursor: not-allowed;
`)
]),
cM('end', {
padding: 'var(--calendar-right-padding)',
gridArea: 'right-calendar'
})
])
]),
cM('date', {
gridTemplateAreas: `

View File

@ -14,8 +14,10 @@ import {
getDay,
parse,
format,
Locale
Locale,
startOfYear
} from 'date-fns'
import { START_YEAR } from './config'
function getDerivedTimeFromKeyboardEvent (
prevValue: number | null,
@ -36,41 +38,27 @@ function getDerivedTimeFromKeyboardEvent (
return now
}
const matcherMap = {
date: isSameDay,
month: isSameMonth,
year: isSameYear
} as const
function matchDate (
sourceTime: number[] | number,
patternTime: number | Date
patternTime: number | Date,
type: 'date' | 'month' | 'year' = 'date'
): boolean {
const matcher = matcherMap[type]
if (Array.isArray(sourceTime)) {
return sourceTime.some((time) => isSameDay(time, patternTime))
return sourceTime.some((time) => matcher(time, patternTime))
} else {
return isSameDay(sourceTime, patternTime)
}
}
function matchMonth (
sourceTime: number[] | number,
patternTime: number | Date
): boolean {
if (Array.isArray(sourceTime)) {
return sourceTime.some((time) => isSameMonth(time, patternTime))
} else {
return isSameMonth(sourceTime, patternTime)
}
}
function matchYear (
sourceTime: number[] | number,
patternTime: number | Date
): boolean {
if (Array.isArray(sourceTime)) {
return sourceTime.some((time) => isSameYear(time, patternTime))
} else {
return isSameYear(sourceTime, patternTime)
return matcher(sourceTime, patternTime)
}
}
export interface DateItem {
type: 'day'
type: 'date'
dateObject: {
date: number
month: number
@ -91,10 +79,9 @@ export interface MonthItem {
month: number
year: number
}
inCurrentMonth: boolean
isCurrentMonth: boolean
selected: boolean
ts: number
formattedText?: string
}
export interface YearItem {
@ -102,7 +89,7 @@ export interface YearItem {
dateObject: {
year: number
}
inCurrentYear: boolean
isCurrentYear: boolean
selected: boolean
ts: number
}
@ -124,7 +111,7 @@ function dateItem (
if (matchDate(valueTs[1], time)) endOfSpan = true
}
return {
type: 'day',
type: 'date',
dateObject: {
date: getDate(time),
month: getMonth(time),
@ -151,8 +138,8 @@ function monthItem (
month: getMonth(monthTs),
year: getYear(monthTs)
},
inCurrentMonth: isSameMonth(currentTs, monthTs),
selected: valueTs !== null && matchMonth(valueTs, monthTs),
isCurrentMonth: isSameMonth(currentTs, monthTs),
selected: valueTs !== null && matchDate(valueTs, monthTs, 'month'),
ts: getTime(monthTs)
}
}
@ -167,8 +154,8 @@ function yearItem (
dateObject: {
year: getYear(yearTs)
},
inCurrentYear: isSameYear(currentTs, yearTs),
selected: valueTs !== null && matchYear(valueTs, yearTs),
isCurrentYear: isSameYear(currentTs, yearTs),
selected: valueTs !== null && matchDate(valueTs, yearTs, 'year'),
ts: getTime(yearTs)
}
}
@ -229,15 +216,10 @@ function monthArray (
currentTs: number
): MonthItem[] {
const calendarMonths = []
const cachedMonth = getMonth(monthTs)
const yearStart = startOfYear(monthTs)
for (let i = 0; i < 12; i++) {
calendarMonths.push(
monthItem(
getTime(addMonths(monthTs, i - cachedMonth)),
valueTs,
currentTs
)
monthItem(getTime(addMonths(yearStart, i)), valueTs, currentTs)
)
}
return calendarMonths
@ -249,11 +231,13 @@ function yearArray (
currentTs: number
): YearItem[] {
const calendarYears = []
const cachedYear = getYear(yearTs)
for (let i = 1900; i < 2100; i++) {
const time1900 = new Date(START_YEAR, 0, 1)
// 1900 is not a round time, so we use 1911 as start...
// new Date(1900, 0, 1)
// 1899-12-31T15:54:17.000Z
for (let i = 0; i < 200; i++) {
calendarYears.push(
yearItem(getTime(addYears(yearTs, i - cachedYear)), valueTs, currentTs)
yearItem(getTime(addYears(time1900, i)), valueTs, currentTs)
)
}
return calendarYears

View File

@ -2,8 +2,8 @@ export default {
itemSize: '24px',
itemCellWidth: '38px',
itemCellHeight: '32px',
itemMonthCellWidth: '80px',
itemMonthCellHeight: '40px',
scrollItemWidth: '80px',
scrollItemHeight: '40px',
panelExtraFooterPadding: '8px 12px',
panelActionPadding: '8px 12px',
calendarTitlePadding: '0',
@ -17,10 +17,10 @@ export default {
calendarLeftPaddingDatetime: '4px 12px',
calendarLeftPaddingDaterange: '6px 12px 4px 12px',
calendarLeftPaddingDatetimerange: '4px 12px',
calendarLeftPaddingMonth: '4px 4px',
calendarLeftPaddingMonth: '0',
calendarRightPaddingDate: '6px 12px 4px 12px',
calendarRightPaddingDatetime: '4px 12px',
calendarRightPaddingDaterange: '6px 12px 4px 12px',
calendarRightPaddingDatetimerange: '4px 12px',
calendarRightPaddingMonth: '4px 12px'
calendarRightPaddingMonth: '0'
}

View File

@ -51,6 +51,7 @@ export const self = (vars: ThemeCommonVars) => {
panelBoxShadow: boxShadow2,
panelBorderRadius: borderRadius,
calendarTitleFontWeight: fontWeightStrong,
scrollItemBorderRadius: borderRadius,
iconColor,
iconColorDisabled
}