Refactor/time picker refactor to setup (#8191)

* chore: refine code

* chore(build): [time/date-picker] type unsafe

* Remove `time/date-picker` entry from typing list.

* refactor(components): [time-picker] utilities

* Refine typing for utilities.
* Extract common props.

* chore: fix typing

* chore: fix typing

* chore: refine code

Co-authored-by: JeremyWuuuuu <15975785+JeremyWuuuuu@users.noreply.github.com>
This commit is contained in:
Jeremy
2022-06-11 19:27:36 +08:00
committed by GitHub
parent c4d8de24ca
commit 4ef054488a
15 changed files with 241 additions and 155 deletions

View File

@@ -5,7 +5,6 @@
"packages/components/col/",
"packages/components/collapse-transition/",
"packages/components/color-picker/",
"packages/components/date-picker/",
"packages/components/descriptions/",
"packages/components/dialog/",
"packages/components/drawer/",
@@ -39,7 +38,6 @@
"packages/components/table-v2/",
"packages/components/table/",
"packages/components/tabs/",
"packages/components/time-picker/",
"packages/components/time-select/",
"packages/components/timeline-item/",
"packages/components/timeline/",

View File

@@ -356,7 +356,9 @@ const handleFocusInput = (e?: FocusEvent) => {
emit('focus', e)
}
let currentHandleBlurDeferCallback: () => Promise<void> | undefined
let currentHandleBlurDeferCallback:
| (() => Promise<void> | undefined)
| undefined = undefined
// Check if document.activeElement is inside popper or any input before popper close
const handleBlurInput = (e?: FocusEvent) => {

View File

@@ -1,6 +1,7 @@
import { buildProps, definePropType } from '@element-plus/utils'
import { useSizeProp } from '@element-plus/hooks'
import { CircleClose } from '@element-plus/icons-vue'
import { disabledTimeListsProps } from '../props/shared'
import type { Component, ExtractPropTypes } from 'vue'
import type { Options } from '@popperjs/core'
@@ -12,6 +13,18 @@ export type ModelValueType = SingleOrRange<DateModelType>
export type DayOrDays = SingleOrRange<Dayjs>
export type DateOrDates = SingleOrRange<Date>
export type UserInput = SingleOrRange<string | null>
export type GetDisabledHours = (role: string, comparingDate?: Dayjs) => number[]
export type GetDisabledMinutes = (
hour: number,
role: string,
comparingDate?: Dayjs
) => number[]
export type GetDisabledSeconds = (
hour: number,
minute: number,
role: string,
comparingDate?: Dayjs
) => number[]
export const timePickerDefaultProps = buildProps({
id: {
@@ -84,15 +97,7 @@ export const timePickerDefaultProps = buildProps({
type: Boolean,
default: false,
},
disabledHours: {
type: Function,
},
disabledMinutes: {
type: Function,
},
disabledSeconds: {
type: Function,
},
...disabledTimeListsProps,
disabledDate: {
type: Function,
},

View File

@@ -1,14 +1,15 @@
import type { Dayjs } from 'dayjs'
import type {
GetDisabledHoursState,
GetDisabledMinutesState,
GetDisabledSecondsState,
} from '../types'
type UseTimePanelProps = {
getAvailableHours: (role: string, compare?: Dayjs) => number[]
getAvailableMinutes: (hour: number, role: string, compare?: Dayjs) => number[]
getAvailableSeconds: (
hour: number,
minute: number,
role: string,
compare?: Dayjs
) => number[]
getAvailableHours: GetDisabledHoursState
getAvailableMinutes: GetDisabledMinutesState
getAvailableSeconds: GetDisabledSecondsState
}
export const useTimePanel = ({
@@ -30,7 +31,7 @@ export const useTimePanel = ({
let result = date
;(['hour', 'minute', 'second'] as const).forEach((type) => {
if (availableTimeGetters[type]) {
let availableTimeSlots
let availableTimeSlots: number[]
const method = availableTimeGetters[type]
switch (type) {
case 'minute': {

View File

@@ -0,0 +1,116 @@
import { ref, watch } from 'vue'
import { makeList } from '../utils'
import type { Dayjs } from 'dayjs'
import type {
GetDisabledHoursState,
GetDisabledMinutesState,
GetDisabledSecondsState,
} from '../types'
import type {
GetDisabledHours,
GetDisabledMinutes,
GetDisabledSeconds,
} from '../props/shared'
const makeAvailableArr = (disabledList: boolean[]): number[] => {
const trueOrNumber = (isDisabled: boolean, index: number) =>
isDisabled || index
const getNumber = (predicate: number | true): predicate is number =>
predicate !== true
return disabledList.map(trueOrNumber).filter(getNumber)
}
export const getTimeLists = (
disabledHours?: GetDisabledHours,
disabledMinutes?: GetDisabledMinutes,
disabledSeconds?: GetDisabledSeconds
) => {
const getHoursList = (role: string, compare?: Dayjs) => {
return makeList(24, disabledHours && (() => disabledHours?.(role, compare)))
}
const getMinutesList = (hour: number, role: string, compare?: Dayjs) => {
return makeList(
60,
disabledMinutes && (() => disabledMinutes?.(hour, role, compare))
)
}
const getSecondsList = (
hour: number,
minute: number,
role: string,
compare?: Dayjs
) => {
return makeList(
60,
disabledSeconds && (() => disabledSeconds?.(hour, minute, role, compare))
)
}
return {
getHoursList,
getMinutesList,
getSecondsList,
}
}
export const buildAvailableTimeSlotGetter = (
disabledHours: GetDisabledHours,
disabledMinutes: GetDisabledMinutes,
disabledSeconds: GetDisabledSeconds
) => {
const { getHoursList, getMinutesList, getSecondsList } = getTimeLists(
disabledHours,
disabledMinutes,
disabledSeconds
)
const getAvailableHours: GetDisabledHoursState = (role, compare?) => {
return makeAvailableArr(getHoursList(role, compare))
}
const getAvailableMinutes: GetDisabledMinutesState = (
hour,
role,
compare?
) => {
return makeAvailableArr(getMinutesList(hour, role, compare))
}
const getAvailableSeconds: GetDisabledSecondsState = (
hour,
minute,
role,
compare?
) => {
return makeAvailableArr(getSecondsList(hour, minute, role, compare))
}
return {
getAvailableHours,
getAvailableMinutes,
getAvailableSeconds,
}
}
export const useOldValue = (props: {
parsedValue?: string | Dayjs | Dayjs[]
visible: boolean
}) => {
const oldValue = ref(props.parsedValue)
watch(
() => props.visible,
(val) => {
if (!val) {
oldValue.value = props.parsedValue
}
}
)
return oldValue
}

View File

@@ -1,4 +1,5 @@
import { buildProps, definePropType } from '@element-plus/utils'
import { disabledTimeListsProps } from '../props/shared'
import type { ExtractPropTypes } from 'vue'
import type { Dayjs } from 'dayjs'
@@ -22,26 +23,7 @@ export const basicTimeSpinnerProps = buildProps({
type: definePropType<'a' | 'A' | ''>(String),
default: '',
},
disabledHours: {
type: definePropType<(role: string, comparingDate?: Dayjs) => number[]>(
Function
),
},
disabledMinutes: {
type: definePropType<
(hour: number, role: string, comparingDate?: Dayjs) => number[]
>(Function),
},
disabledSeconds: {
type: definePropType<
(
hour: number,
minute: number,
role: string,
comparingDate?: Dayjs
) => number[]
>(Function),
},
...disabledTimeListsProps,
} as const)
export type BasicTimeSpinnerProps = ExtractPropTypes<

View File

@@ -1,22 +1,15 @@
import { buildProps, definePropType } from '@element-plus/utils'
import { timePanelSharedProps } from './shared'
import type { ExtractPropTypes } from 'vue'
import type { Dayjs } from 'dayjs'
export const panelTimePickerProps = buildProps({
visible: Boolean,
actualVisible: {
type: Boolean,
default: undefined,
},
...timePanelSharedProps,
datetimeRole: String,
parsedValue: {
type: definePropType<Dayjs>(Object),
},
format: {
type: String,
default: '',
},
} as const)
export type PanelTimePickerProps = ExtractPropTypes<typeof panelTimePickerProps>

View File

@@ -1,18 +1,14 @@
import { buildProps, definePropType } from '@element-plus/utils'
import { timePanelSharedProps } from './shared'
import type { ExtractPropTypes } from 'vue'
import type { Dayjs } from 'dayjs'
export const panelTimeRangeProps = buildProps({
visible: Boolean,
actualVisible: Boolean,
...timePanelSharedProps,
parsedValue: {
type: definePropType<[Dayjs, Dayjs]>(Array),
},
format: {
type: String,
default: '',
},
} as const)
export type PanelTimeRangeProps = ExtractPropTypes<typeof panelTimeRangeProps>

View File

@@ -0,0 +1,47 @@
import { buildProps, definePropType } from '@element-plus/utils'
import type { ExtractPropTypes } from 'vue'
import type { Dayjs } from 'dayjs'
export type GetDisabledHours = (role: string, comparingDate?: Dayjs) => number[]
export type GetDisabledMinutes = (
hour: number,
role: string,
comparingDate?: Dayjs
) => number[]
export type GetDisabledSeconds = (
hour: number,
minute: number,
role: string,
comparingDate?: Dayjs
) => number[]
export const disabledTimeListsProps = buildProps({
disabledHours: {
type: definePropType<GetDisabledHours>(Function),
},
disabledMinutes: {
type: definePropType<GetDisabledMinutes>(Function),
},
disabledSeconds: {
type: definePropType<GetDisabledSeconds>(Function),
},
} as const)
export type DisabledTimeListsProps = ExtractPropTypes<
typeof disabledTimeListsProps
>
export const timePanelSharedProps = buildProps({
visible: Boolean,
actualVisible: {
type: Boolean,
default: undefined,
},
format: {
type: String,
default: '',
},
} as const)
export type TimePanelSharedProps = ExtractPropTypes<typeof timePanelSharedProps>

View File

@@ -88,7 +88,7 @@ import { useNamespace } from '@element-plus/hooks'
import { timeUnits } from '../constants'
import { buildTimeList } from '../utils'
import { basicTimeSpinnerProps } from '../props/basic-time-spinner'
import { getTimeLists } from './useTimePicker'
import { getTimeLists } from '../composables/use-time-picker'
import type { Ref } from 'vue'
import type { ScrollbarInstance } from '@element-plus/components/scrollbar'

View File

@@ -45,8 +45,11 @@ import { useLocale, useNamespace } from '@element-plus/hooks'
import { isUndefined } from '@element-plus/utils'
import { panelTimePickerProps } from '../props/panel-time-picker'
import { useTimePanel } from '../composables/use-time-panel'
import {
buildAvailableTimeSlotGetter,
useOldValue,
} from '../composables/use-time-picker'
import TimeSpinner from './basic-time-spinner.vue'
import { getAvailableArrs, useOldValue } from './useTimePicker'
import type { Dayjs } from 'dayjs'
@@ -63,7 +66,7 @@ const {
defaultValue,
} = pickerBase.props
const { getAvailableHours, getAvailableMinutes, getAvailableSeconds } =
getAvailableArrs(disabledHours, disabledMinutes, disabledSeconds)
buildAvailableTimeSlotGetter(disabledHours, disabledMinutes, disabledSeconds)
const ns = useNamespace('time')
const { t, lang } = useLocale()

View File

@@ -90,8 +90,11 @@ import { isArray } from '@element-plus/utils'
import { EVENT_CODE } from '@element-plus/constants'
import { panelTimeRangeProps } from '../props/panel-time-range'
import { useTimePanel } from '../composables/use-time-panel'
import {
buildAvailableTimeSlotGetter,
useOldValue,
} from '../composables/use-time-picker'
import TimeSpinner from './basic-time-spinner.vue'
import { getAvailableArrs, useOldValue } from './useTimePicker'
import type { Dayjs } from 'dayjs'
@@ -99,7 +102,7 @@ const props = defineProps(panelTimeRangeProps)
const emit = defineEmits(['pick', 'select-range', 'set-picker-option'])
const makeSelectRange = (start: number, end: number) => {
const result = []
const result: number[] = []
for (let i = start; i <= end; i++) {
result.push(i)
}
@@ -262,7 +265,11 @@ const getRangeAvailableTime = ([start, end]: Array<Dayjs>) => {
}
const { getAvailableHours, getAvailableMinutes, getAvailableSeconds } =
getAvailableArrs(disabledHours_, disabledMinutes_, disabledSeconds_)
buildAvailableTimeSlotGetter(
disabledHours_,
disabledMinutes_,
disabledSeconds_
)
const {
timePickerOptions,

View File

@@ -1,92 +0,0 @@
import { ref, watch } from 'vue'
import type { Dayjs } from 'dayjs'
const makeList = (total, method, methodFunc) => {
const arr = []
const disabledArr = method && methodFunc()
for (let i = 0; i < total; i++) {
arr[i] = disabledArr ? disabledArr.includes(i) : false
}
return arr
}
const makeAvailableArr = (list) => {
return list.map((_, index) => (!_ ? index : _)).filter((_) => _ !== true)
}
export const getTimeLists = (
disabledHours,
disabledMinutes,
disabledSeconds
) => {
const getHoursList = (role, compare?) => {
return makeList(24, disabledHours, () => disabledHours(role, compare))
}
const getMinutesList = (hour, role, compare?) => {
return makeList(60, disabledMinutes, () =>
disabledMinutes(hour, role, compare)
)
}
const getSecondsList = (hour, minute, role, compare?) => {
return makeList(60, disabledSeconds, () =>
disabledSeconds(hour, minute, role, compare)
)
}
return {
getHoursList,
getMinutesList,
getSecondsList,
}
}
export const getAvailableArrs = (
disabledHours,
disabledMinutes,
disabledSeconds
) => {
const { getHoursList, getMinutesList, getSecondsList } = getTimeLists(
disabledHours,
disabledMinutes,
disabledSeconds
)
const getAvailableHours = (role, compare?) => {
return makeAvailableArr(getHoursList(role, compare))
}
const getAvailableMinutes = (hour, role, compare?) => {
return makeAvailableArr(getMinutesList(hour, role, compare))
}
const getAvailableSeconds = (hour, minute, role, compare?) => {
return makeAvailableArr(getSecondsList(hour, minute, role, compare))
}
return {
getAvailableHours,
getAvailableMinutes,
getAvailableSeconds,
}
}
export const useOldValue = (props: {
parsedValue?: string | Dayjs | Dayjs[]
visible: boolean
}) => {
const oldValue = ref(props.parsedValue)
watch(
() => props.visible,
(val) => {
if (!val) {
oldValue.value = props.parsedValue
}
}
)
return oldValue
}

View File

@@ -0,0 +1,19 @@
import type { Dayjs } from 'dayjs'
export type GetDisabledHoursState = (
role: string,
comparingDate?: Dayjs
) => number[]
export type GetDisabledMinutesState = (
hour: number,
role: string,
comparingDate?: Dayjs
) => number[]
export type GetDisabledSecondsState = (
hour: number,
minute: number,
role: string,
comparingDate?: Dayjs
) => number[]

View File

@@ -28,7 +28,7 @@ export const extractTimeFormat = (format: string) => {
.trim()
}
export const dateEquals = function (a: Date | any, b: Date | any) {
export const dateEquals = function (a: Date | unknown, b: Date | unknown) {
const aIsDate = isDate(a)
const bIsDate = isDate(b)
if (aIsDate && bIsDate) {
@@ -79,3 +79,12 @@ export const formatter = function (
if (format === 'x') return +date
return dayjs(date).locale(lang).format(format)
}
export const makeList = (total: number, method?: () => number[]) => {
const arr: boolean[] = []
const disabledArr = method?.()
for (let i = 0; i < total; i++) {
arr.push(disabledArr?.includes(i) ?? false)
}
return arr
}