refactor(components): [time-picker] time-picker (#8160)

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

* Refactor `time-picker` to tsx.

* chore: refactor  to script setup

* chore: fix typing issue in `picker`

* chore: fix API exposing

Co-authored-by: JeremyWuuuuu <15975785+JeremyWuuuuu@users.noreply.github.com>
This commit is contained in:
Jeremy 2022-06-08 09:17:23 +08:00 committed by GitHub
parent 6b6c5a4a87
commit 5b233da008
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 616 additions and 624 deletions

View File

@ -49,7 +49,6 @@ export default defineComponent({
const commonPicker = ref<InstanceType<typeof CommonPicker>>() const commonPicker = ref<InstanceType<typeof CommonPicker>>()
const refProps = { const refProps = {
...props,
focus: (focusStartInput = true) => { focus: (focusStartInput = true) => {
commonPicker.value?.focus(focusStartInput) commonPicker.value?.focus(focusStartInput)
}, },

View File

@ -23,10 +23,10 @@
<template #default> <template #default>
<el-input <el-input
v-if="!isRangeInput" v-if="!isRangeInput"
:id="id" :id="(id as string | undefined)"
ref="inputRef" ref="inputRef"
container-role="combobox" container-role="combobox"
:model-value="displayValue" :model-value="(displayValue as string)"
:name="name" :name="name"
:size="pickerSize" :size="pickerSize"
:disabled="pickerDisabled" :disabled="pickerDisabled"
@ -39,7 +39,7 @@
@input="onUserInput" @input="onUserInput"
@focus="handleFocusInput" @focus="handleFocusInput"
@blur="handleBlurInput" @blur="handleBlurInput"
@keydown="handleKeydownInput" @keydown="handleKeydownInput as any"
@change="handleChange" @change="handleChange"
@mousedown="onMouseDownInput" @mousedown="onMouseDownInput"
@mouseenter="onMouseEnter" @mouseenter="onMouseEnter"
@ -80,7 +80,7 @@
pickerSize ? nsRange.bm('editor', pickerSize) : '', pickerSize ? nsRange.bm('editor', pickerSize) : '',
$attrs.class, $attrs.class,
]" ]"
:style="$attrs.style" :style="($attrs.style as any)"
@click="handleFocusInput" @click="handleFocusInput"
@mousedown="onMouseDownInput" @mousedown="onMouseDownInput"
@mouseenter="onMouseEnter" @mouseenter="onMouseEnter"
@ -162,17 +162,8 @@
</template> </template>
</el-tooltip> </el-tooltip>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { import { computed, inject, nextTick, provide, ref, unref, watch } from 'vue'
computed,
defineComponent,
inject,
nextTick,
provide,
ref,
unref,
watch,
} from 'vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { isEqual } from 'lodash-unified' import { isEqual } from 'lodash-unified'
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from '@vueuse/core'
@ -181,27 +172,24 @@ import { formContextKey, formItemContextKey } from '@element-plus/tokens'
import ElInput from '@element-plus/components/input' import ElInput from '@element-plus/components/input'
import ElIcon from '@element-plus/components/icon' import ElIcon from '@element-plus/components/icon'
import ElTooltip from '@element-plus/components/tooltip' import ElTooltip from '@element-plus/components/tooltip'
import { debugWarn, isEmpty } from '@element-plus/utils' import { debugWarn, isArray, isEmpty } from '@element-plus/utils'
import { EVENT_CODE } from '@element-plus/constants' import { EVENT_CODE } from '@element-plus/constants'
import { Calendar, Clock } from '@element-plus/icons-vue' import { Calendar, Clock } from '@element-plus/icons-vue'
import { timePickerDefaultProps } from './props' import { timePickerDefaultProps } from './props'
import type { Dayjs } from 'dayjs' import type { Dayjs } from 'dayjs'
import type { ComponentPublicInstance } from 'vue' import type { ComponentPublicInstance } from 'vue'
import type { FormContext, FormItemContext } from '@element-plus/tokens'
import type { Options } from '@popperjs/core' import type { Options } from '@popperjs/core'
import type { FormContext, FormItemContext } from '@element-plus/tokens'
interface PickerOptions { import type {
isValidValue: (date: Dayjs) => boolean DateModelType,
handleKeydownInput: (event: KeyboardEvent) => void DateOrDates,
parseUserInput: (value: Dayjs) => dayjs.Dayjs DayOrDays,
formatToString: (value: Dayjs) => string | string[] PickerOptions,
getRangeAvailableTime: (date: Dayjs) => dayjs.Dayjs SingleOrRange,
getDefaultValue: () => Dayjs TimePickerDefaultProps,
panelReady: boolean UserInput,
handleClear: () => void } from './props'
handleFocusPicker?: () => void
}
// Date object and string // Date object and string
const dateEquals = function (a: Date | any, b: Date | any) { const dateEquals = function (a: Date | any, b: Date | any) {
@ -217,8 +205,8 @@ const dateEquals = function (a: Date | any, b: Date | any) {
} }
const valueEquals = function (a: Array<Date> | any, b: Array<Date> | any) { const valueEquals = function (a: Array<Date> | any, b: Array<Date> | any) {
const aIsArray = Array.isArray(a) const aIsArray = isArray(a)
const bIsArray = Array.isArray(b) const bIsArray = isArray(b)
if (aIsArray && bIsArray) { if (aIsArray && bIsArray) {
if (a.length !== b.length) { if (a.length !== b.length) {
return false return false
@ -231,11 +219,11 @@ const valueEquals = function (a: Array<Date> | any, b: Array<Date> | any) {
return false return false
} }
const parser = function ( const parseDate = function (
date: string | number | Date, date: string | number | Date,
format: string, format: string | undefined,
lang: string lang: string
): Dayjs { ) {
const day = const day =
isEmpty(format) || format === 'x' isEmpty(format) || format === 'x'
? dayjs(date).locale(lang) ? dayjs(date).locale(lang)
@ -244,8 +232,8 @@ const parser = function (
} }
const formatter = function ( const formatter = function (
date: string | number | Date, date: string | number | Date | Dayjs,
format: string, format: string | undefined,
lang: string lang: string
) { ) {
if (isEmpty(format)) return date if (isEmpty(format)) return date
@ -253,15 +241,12 @@ const formatter = function (
return dayjs(date).locale(lang).format(format) return dayjs(date).locale(lang).format(format)
} }
export default defineComponent({ defineOptions({
name: 'Picker', name: 'Picker',
components: { })
ElInput,
ElTooltip, const props = defineProps(timePickerDefaultProps)
ElIcon, const emit = defineEmits([
},
props: timePickerDefaultProps,
emits: [
'update:modelValue', 'update:modelValue',
'change', 'change',
'focus', 'focus',
@ -270,8 +255,8 @@ export default defineComponent({
'panel-change', 'panel-change',
'visible-change', 'visible-change',
'keydown', 'keydown',
], ])
setup(props, ctx) {
const { lang } = useLocale() const { lang } = useLocale()
const nsDate = useNamespace('date') const nsDate = useNamespace('date')
@ -286,7 +271,7 @@ export default defineComponent({
const inputRef = ref<HTMLElement | ComponentPublicInstance>() const inputRef = ref<HTMLElement | ComponentPublicInstance>()
const pickerVisible = ref(false) const pickerVisible = ref(false)
const pickerActualVisible = ref(false) const pickerActualVisible = ref(false)
const valueOnOpen = ref(null) const valueOnOpen = ref<TimePickerDefaultProps['modelValue'] | null>(null)
let hasJustTabExitedInput = false let hasJustTabExitedInput = false
let ignoreFocusEvent = false let ignoreFocusEvent = false
@ -301,30 +286,34 @@ export default defineComponent({
valueOnOpen.value = props.modelValue valueOnOpen.value = props.modelValue
} }
}) })
const emitChange = (val, isClear?: boolean) => { const emitChange = (
val: TimePickerDefaultProps['modelValue'] | null,
isClear?: boolean
) => {
// determine user real change only // determine user real change only
if (isClear || !valueEquals(val, valueOnOpen.value)) { if (isClear || !valueEquals(val, valueOnOpen.value)) {
ctx.emit('change', val) emit('change', val)
props.validateEvent && props.validateEvent &&
elFormItem.validate?.('change').catch((err) => debugWarn(err)) elFormItem.validate?.('change').catch((err) => debugWarn(err))
} }
} }
const emitInput = (val) => { const emitInput = (input: SingleOrRange<DateModelType | Dayjs> | null) => {
if (!valueEquals(props.modelValue, val)) { if (!valueEquals(props.modelValue, input)) {
let formatValue let formatted
if (Array.isArray(val)) { if (isArray(input)) {
formatValue = val.map((_) => formatted = input.map((item) =>
formatter(_, props.valueFormat, lang.value) formatter(item, props.valueFormat, lang.value)
) )
} else if (val) { } else if (input) {
formatValue = formatter(val, props.valueFormat, lang.value) formatted = formatter(input, props.valueFormat, lang.value)
} }
ctx.emit('update:modelValue', val ? formatValue : val, lang.value) emit('update:modelValue', input ? formatted : input, lang.value)
} }
} }
const emitKeydown = (e) => { const emitKeydown = (e: KeyboardEvent) => {
ctx.emit('keydown', e) emit('keydown', e)
} }
const refInput = computed<HTMLInputElement[]>(() => { const refInput = computed<HTMLInputElement[]>(() => {
if (inputRef.value) { if (inputRef.value) {
const _r = isRangeInput.value const _r = isRangeInput.value
@ -341,7 +330,7 @@ export default defineComponent({
return refInput?.value[1] return refInput?.value[1]
}) })
const setSelectionRange = (start, end, pos) => { const setSelectionRange = (start: number, end: number, pos?: 'min' | 'max') => {
const _inputs = refInput.value const _inputs = refInput.value
if (!_inputs.length) return if (!_inputs.length) return
if (!pos || pos === 'min') { if (!pos || pos === 'min') {
@ -365,7 +354,7 @@ export default defineComponent({
} }
pickerVisible.value = visible pickerVisible.value = visible
let result let result
if (Array.isArray(date)) { if (isArray(date)) {
result = date.map((_) => _.toDate()) result = date.map((_) => _.toDate())
} else { } else {
// clear btn emit null // clear btn emit null
@ -380,7 +369,7 @@ export default defineComponent({
} }
const onShow = () => { const onShow = () => {
ctx.emit('visible-change', true) emit('visible-change', true)
} }
const onKeydownPopperContent = (event: KeyboardEvent) => { const onKeydownPopperContent = (event: KeyboardEvent) => {
@ -392,7 +381,7 @@ export default defineComponent({
const onHide = () => { const onHide = () => {
pickerActualVisible.value = false pickerActualVisible.value = false
ignoreFocusEvent = false ignoreFocusEvent = false
ctx.emit('visible-change', false) emit('visible-change', false)
} }
const focus = (focusStartInput = true, isIgnoreFocusEvent = false) => { const focus = (focusStartInput = true, isIgnoreFocusEvent = false) => {
@ -406,7 +395,7 @@ export default defineComponent({
} }
} }
const handleFocusInput = (e) => { const handleFocusInput = (e?: FocusEvent) => {
if ( if (
props.readonly || props.readonly ||
pickerDisabled.value || pickerDisabled.value ||
@ -416,20 +405,19 @@ export default defineComponent({
return return
} }
pickerVisible.value = true pickerVisible.value = true
ctx.emit('focus', e) emit('focus', e)
} }
let currentHandleBlurDeferCallback: () => void | undefined let currentHandleBlurDeferCallback: () => Promise<void> | undefined
// Check if document.activeElement is inside popper or any input before popper close // Check if document.activeElement is inside popper or any input before popper close
const handleBlurInput = (e) => { const handleBlurInput = (e?: FocusEvent) => {
const handleBlurDefer = async () => { const handleBlurDefer = async () => {
setTimeout(() => { setTimeout(() => {
if (currentHandleBlurDeferCallback === handleBlurDefer) { if (currentHandleBlurDeferCallback === handleBlurDefer) {
if ( if (
!( !(
refPopper.value?.isFocusInsideContent() && refPopper.value?.isFocusInsideContent() && !hasJustTabExitedInput
!hasJustTabExitedInput
) && ) &&
refInput.value.filter((input) => { refInput.value.filter((input) => {
return input.contains(document.activeElement) return input.contains(document.activeElement)
@ -437,7 +425,7 @@ export default defineComponent({
) { ) {
handleChange() handleChange()
pickerVisible.value = false pickerVisible.value = false
ctx.emit('blur', e) emit('blur', e)
props.validateEvent && props.validateEvent &&
elFormItem.validate?.('blur').catch((err) => debugWarn(err)) elFormItem.validate?.('blur').catch((err) => debugWarn(err))
} }
@ -454,43 +442,44 @@ export default defineComponent({
}) })
const parsedValue = computed(() => { const parsedValue = computed(() => {
let result let dayOrDays: DayOrDays
if (valueIsEmpty.value) { if (valueIsEmpty.value) {
if (pickerOptions.value.getDefaultValue) { if (pickerOptions.value.getDefaultValue) {
result = pickerOptions.value.getDefaultValue() dayOrDays = pickerOptions.value.getDefaultValue()
} }
} else { } else {
if (Array.isArray(props.modelValue)) { if (isArray(props.modelValue)) {
result = props.modelValue.map((_) => dayOrDays = props.modelValue.map((d) =>
parser(_, props.valueFormat, lang.value) parseDate(d, props.valueFormat, lang.value)
) ) as [Dayjs, Dayjs]
} else { } else {
result = parser(props.modelValue, props.valueFormat, lang.value) dayOrDays = parseDate(props.modelValue, props.valueFormat, lang.value)!
} }
} }
if (pickerOptions.value.getRangeAvailableTime) { if (pickerOptions.value.getRangeAvailableTime) {
const availableResult = const availableResult = pickerOptions.value.getRangeAvailableTime(
pickerOptions.value.getRangeAvailableTime(result) dayOrDays!
if (!isEqual(availableResult, result)) { )
result = availableResult if (!isEqual(availableResult, dayOrDays!)) {
dayOrDays = availableResult
emitInput( emitInput(
Array.isArray(result) (isArray(dayOrDays)
? result.map((_) => _.toDate()) ? dayOrDays.map((_) => _.toDate())
: result.toDate() : dayOrDays.toDate()) as SingleOrRange<Date>
) )
} }
} }
if (Array.isArray(result) && result.some((_) => !_)) { if (isArray(dayOrDays!) && dayOrDays.some((day) => !day)) {
result = [] dayOrDays = [] as unknown as DayOrDays
} }
return result return dayOrDays!
}) })
const displayValue = computed(() => { const displayValue = computed<UserInput>(() => {
if (!pickerOptions.value.panelReady) return if (!pickerOptions.value.panelReady) return ''
const formattedValue = formatDayjsToString(parsedValue.value) const formattedValue = formatDayjsToString(parsedValue.value)
if (Array.isArray(userInput.value)) { if (isArray(userInput.value)) {
return [ return [
userInput.value[0] || (formattedValue && formattedValue[0]) || '', userInput.value[0] || (formattedValue && formattedValue[0]) || '',
userInput.value[1] || (formattedValue && formattedValue[1]) || '', userInput.value[1] || (formattedValue && formattedValue[1]) || '',
@ -498,8 +487,8 @@ export default defineComponent({
} else if (userInput.value !== null) { } else if (userInput.value !== null) {
return userInput.value return userInput.value
} }
if (!isTimePicker.value && valueIsEmpty.value) return if (!isTimePicker.value && valueIsEmpty.value) return ''
if (!pickerVisible.value && valueIsEmpty.value) return if (!pickerVisible.value && valueIsEmpty.value) return ''
if (formattedValue) { if (formattedValue) {
return isDatesPicker.value return isDatesPicker.value
? (formattedValue as Array<string>).join(', ') ? (formattedValue as Array<string>).join(', ')
@ -534,8 +523,7 @@ export default defineComponent({
} }
const valueIsEmpty = computed(() => { const valueIsEmpty = computed(() => {
return ( return (
!props.modelValue || !props.modelValue || (isArray(props.modelValue) && !props.modelValue.length)
(Array.isArray(props.modelValue) && !props.modelValue.length)
) )
}) })
const onMouseDownInput = async (event: MouseEvent) => { const onMouseDownInput = async (event: MouseEvent) => {
@ -569,10 +557,6 @@ export default defineComponent({
const pickerSize = useSize() const pickerSize = useSize()
const popperPaneRef = computed(() => {
return refPopper.value?.popperRef?.contentRef
})
const popperEl = computed(() => unref(refPopper)?.popperRef?.contentRef) const popperEl = computed(() => unref(refPopper)?.popperRef?.contentRef)
const actualInputRef = computed(() => { const actualInputRef = computed(() => {
if (unref(isRangeInput)) { if (unref(isRangeInput)) {
@ -596,7 +580,7 @@ export default defineComponent({
pickerVisible.value = false pickerVisible.value = false
}) })
const userInput = ref(null) const userInput = ref<UserInput>(null)
const handleChange = () => { const handleChange = () => {
if (userInput.value) { if (userInput.value) {
@ -604,9 +588,9 @@ export default defineComponent({
if (value) { if (value) {
if (isValidValue(value)) { if (isValidValue(value)) {
emitInput( emitInput(
Array.isArray(value) (isArray(value)
? value.map((_) => _.toDate()) ? value.map((_) => _.toDate())
: value.toDate() : value.toDate()) as DateOrDates
) )
userInput.value = null userInput.value = null
} }
@ -619,22 +603,22 @@ export default defineComponent({
} }
} }
const parseUserInputToDayjs = (value) => { const parseUserInputToDayjs = (value: UserInput) => {
if (!value) return null if (!value) return null
return pickerOptions.value.parseUserInput(value) return pickerOptions.value.parseUserInput!(value)
} }
const formatDayjsToString = (value) => { const formatDayjsToString = (value: DayOrDays) => {
if (!value) return null if (!value) return null
return pickerOptions.value.formatToString(value) return pickerOptions.value.formatToString!(value)
} }
const isValidValue = (value) => { const isValidValue = (value: DayOrDays) => {
return pickerOptions.value.isValidValue(value) return pickerOptions.value.isValidValue!(value)
} }
const handleKeydownInput = async (event) => { const handleKeydownInput = async (event: KeyboardEvent) => {
const code = event.code const { code } = event
emitKeydown(event) emitKeydown(event)
if (code === EVENT_CODE.esc) { if (code === EVENT_CODE.esc) {
if (pickerVisible.value === true) { if (pickerVisible.value === true) {
@ -669,7 +653,7 @@ export default defineComponent({
if ( if (
userInput.value === null || userInput.value === null ||
userInput.value === '' || userInput.value === '' ||
isValidValue(parseUserInputToDayjs(displayValue.value)) isValidValue(parseUserInputToDayjs(displayValue.value) as DayOrDays)
) { ) {
handleChange() handleChange()
pickerVisible.value = false pickerVisible.value = false
@ -687,7 +671,7 @@ export default defineComponent({
pickerOptions.value.handleKeydownInput(event) pickerOptions.value.handleKeydownInput(event)
} }
} }
const onUserInput = (e) => { const onUserInput = (e: string) => {
userInput.value = e userInput.value = e
// Temporary fix when the picker is dismissed and the input box // Temporary fix when the picker is dismissed and the input box
// is focused, just mimic the behavior of antdesign. // is focused, just mimic the behavior of antdesign.
@ -696,27 +680,34 @@ export default defineComponent({
} }
} }
const handleStartInput = (event) => { const handleStartInput = (event: Event) => {
const target = event.target as HTMLInputElement
if (userInput.value) { if (userInput.value) {
userInput.value = [event.target.value, userInput.value[1]] userInput.value = [target.value, userInput.value[1]]
} else { } else {
userInput.value = [event.target.value, null] userInput.value = [target.value, null]
} }
} }
const handleEndInput = (event) => { const handleEndInput = (event: Event) => {
const target = event.target as HTMLInputElement
if (userInput.value) { if (userInput.value) {
userInput.value = [userInput.value[0], event.target.value] userInput.value = [userInput.value[0], target.value]
} else { } else {
userInput.value = [null, event.target.value] userInput.value = [null, target.value]
} }
} }
const handleStartChange = () => { const handleStartChange = () => {
const value = parseUserInputToDayjs(userInput.value && userInput.value[0]) const values = userInput.value as string[]
const value = parseUserInputToDayjs(values && values[0]) as Dayjs
const parsedVal = unref(parsedValue) as [Dayjs, Dayjs]
if (value && value.isValid()) { if (value && value.isValid()) {
userInput.value = [formatDayjsToString(value), displayValue.value[1]] userInput.value = [
const newValue = [value, parsedValue.value && parsedValue.value[1]] formatDayjsToString(value) as string,
displayValue.value?.[1] || null,
]
const newValue = [value, parsedVal && (parsedVal[1] || null)] as DayOrDays
if (isValidValue(newValue)) { if (isValidValue(newValue)) {
emitInput(newValue) emitInput(newValue)
userInput.value = null userInput.value = null
@ -725,10 +716,15 @@ export default defineComponent({
} }
const handleEndChange = () => { const handleEndChange = () => {
const value = parseUserInputToDayjs(userInput.value && userInput.value[1]) const values = unref(userInput) as string[]
const value = parseUserInputToDayjs(values && values[1]) as Dayjs
const parsedVal = unref(parsedValue) as [Dayjs, Dayjs]
if (value && value.isValid()) { if (value && value.isValid()) {
userInput.value = [displayValue.value[0], formatDayjsToString(value)] userInput.value = [
const newValue = [parsedValue.value && parsedValue.value[0], value] unref(displayValue)?.[0] || null,
formatDayjsToString(value) as string,
]
const newValue = [parsedVal && parsedVal[0], value] as DayOrDays
if (isValidValue(newValue)) { if (isValidValue(newValue)) {
emitInput(newValue) emitInput(newValue)
userInput.value = null userInput.value = null
@ -744,64 +740,38 @@ export default defineComponent({
pickerOptions.value.panelReady = true pickerOptions.value.panelReady = true
} }
const onCalendarChange = (e) => { const onCalendarChange = (e: [Date, false | Date]) => {
ctx.emit('calendar-change', e) emit('calendar-change', e)
} }
const onPanelChange = (value, mode, view) => { const onPanelChange = (
ctx.emit('panel-change', value, mode, view) value: [Dayjs, Dayjs],
mode: 'month' | 'year',
view: unknown
) => {
emit('panel-change', value, mode, view)
} }
provide('EP_PICKER_BASE', { provide('EP_PICKER_BASE', {
props, props,
}) })
return { defineExpose({
nsDate, /**
nsInput, * @description focus input box.
nsRange, */
// injected popper options
elPopperOptions,
isDatesPicker,
handleEndChange,
handleStartChange,
handleStartInput,
handleEndInput,
onUserInput,
handleChange,
handleKeydownInput,
popperPaneRef,
onClickOutside,
pickerSize,
isRangeInput,
onMouseDownInput,
onMouseLeave,
onMouseEnter,
onTouchStartInput,
onClearIconClick,
showClose,
triggerIcon,
onPick,
handleFocusInput,
handleBlurInput,
pickerVisible,
pickerActualVisible,
displayValue,
parsedValue,
setSelectionRange,
refPopper,
inputRef,
pickerDisabled,
onSetPickerOption,
onCalendarChange,
onPanelChange,
focus, focus,
onShow, /**
onBeforeShow, * @description emit focus event
onHide, */
onKeydownPopperContent, handleFocusInput,
} /**
}, * @description emit blur event
*/
handleBlurInput,
/**
* @description pick item manually
*/
onPick,
}) })
</script> </script>

View File

@ -1,28 +1,32 @@
import { isValidComponentSize } from '@element-plus/utils' import { buildProps, definePropType } from '@element-plus/utils'
import { useSizeProp } from '@element-plus/hooks'
import { CircleClose } from '@element-plus/icons-vue' import { CircleClose } from '@element-plus/icons-vue'
import type { Component, PropType } from 'vue' import type { Component, ExtractPropTypes } from 'vue'
import type { Options } from '@popperjs/core' import type { Options } from '@popperjs/core'
import type { ComponentSize } from '@element-plus/constants' import type { Dayjs } from 'dayjs'
export const timePickerDefaultProps = { export type SingleOrRange<T> = T | [T, T]
export type DateModelType = number | string | Date
export type ModelValueType = SingleOrRange<DateModelType>
export type DayOrDays = SingleOrRange<Dayjs>
export type DateOrDates = SingleOrRange<Date>
export type UserInput = SingleOrRange<string | null>
export const timePickerDefaultProps = buildProps({
id: { id: {
type: [Array, String], type: definePropType<SingleOrRange<string>>([Array, String]),
}, },
name: { name: {
type: [Array, String], type: definePropType<SingleOrRange<string>>([Array, String]),
default: '', default: '',
}, },
popperClass: { popperClass: {
type: String, type: String,
default: '', default: '',
}, },
format: { format: String,
type: String, valueFormat: String,
},
valueFormat: {
type: String as PropType<string>,
},
type: { type: {
type: String, type: String,
default: '', default: '',
@ -32,7 +36,7 @@ export const timePickerDefaultProps = {
default: true, default: true,
}, },
clearIcon: { clearIcon: {
type: [String, Object] as PropType<string | Component>, type: definePropType<string | Component>([String, Object]),
default: CircleClose, default: CircleClose,
}, },
editable: { editable: {
@ -40,13 +44,10 @@ export const timePickerDefaultProps = {
default: true, default: true,
}, },
prefixIcon: { prefixIcon: {
type: [String, Object] as PropType<string | Component>, type: definePropType<string | Component>([String, Object]),
default: '', default: '',
}, },
size: { size: useSizeProp,
type: String as PropType<ComponentSize>,
validator: isValidComponentSize,
},
readonly: { readonly: {
type: Boolean, type: Boolean,
default: false, default: false,
@ -60,13 +61,11 @@ export const timePickerDefaultProps = {
default: '', default: '',
}, },
popperOptions: { popperOptions: {
type: Object as PropType<Partial<Options>>, type: definePropType<Partial<Options>>(Object),
default: () => ({}), default: () => ({}),
}, },
modelValue: { modelValue: {
type: [Date, Array, String, Number] as PropType< type: definePropType<ModelValueType>([Date, Array, String, Number]),
number | string | Date | (number | string | Date)[]
>,
default: '', default: '',
}, },
rangeSeparator: { rangeSeparator: {
@ -76,10 +75,10 @@ export const timePickerDefaultProps = {
startPlaceholder: String, startPlaceholder: String,
endPlaceholder: String, endPlaceholder: String,
defaultValue: { defaultValue: {
type: [Date, Array] as PropType<Date | Date[]>, type: definePropType<SingleOrRange<Date>>([Date, Array]),
}, },
defaultTime: { defaultTime: {
type: [Date, Array] as PropType<Date | Date[]>, type: definePropType<SingleOrRange<Date>>([Date, Array]),
}, },
isRange: { isRange: {
type: Boolean, type: Boolean,
@ -113,7 +112,7 @@ export const timePickerDefaultProps = {
default: undefined, default: undefined,
}, },
tabindex: { tabindex: {
type: [String, Number], type: definePropType<string | number>([String, Number]),
default: 0, default: 0,
}, },
validateEvent: { validateEvent: {
@ -121,4 +120,20 @@ export const timePickerDefaultProps = {
default: true, default: true,
}, },
unlinkPanels: Boolean, unlinkPanels: Boolean,
} as const)
export type TimePickerDefaultProps = ExtractPropTypes<
typeof timePickerDefaultProps
>
export interface PickerOptions {
isValidValue: (date: DayOrDays) => boolean
handleKeydownInput: (event: KeyboardEvent) => void
parseUserInput: (value: UserInput) => DayOrDays
formatToString: (value: DayOrDays) => UserInput
getRangeAvailableTime: (date: DayOrDays) => DayOrDays
getDefaultValue: () => DayOrDays
panelReady: boolean
handleClear: () => void
handleFocusPicker?: () => void
} }

View File

@ -258,7 +258,7 @@ const getRangeAvailableTime = ([start, end]: Array<Dayjs>) => {
return [ return [
getAvailableTime(start, 'start', true, end), getAvailableTime(start, 'start', true, end),
getAvailableTime(end, 'end', false, start), getAvailableTime(end, 'end', false, start),
] ] as const
} }
const { getAvailableHours, getAvailableMinutes, getAvailableSeconds } = const { getAvailableHours, getAvailableMinutes, getAvailableSeconds } =

View File

@ -1,55 +0,0 @@
import { defineComponent, h, provide, ref } from 'vue'
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
import { DEFAULT_FORMATS_TIME } from './common/constant'
import Picker from './common/picker.vue'
import TimePickPanel from './time-picker-com/panel-time-pick.vue'
import TimeRangePanel from './time-picker-com/panel-time-range.vue'
import { timePickerDefaultProps } from './common/props'
dayjs.extend(customParseFormat)
export default defineComponent({
name: 'ElTimePicker',
install: null,
props: {
...timePickerDefaultProps,
isRange: {
type: Boolean,
default: false,
},
},
emits: ['update:modelValue'],
setup(props, ctx) {
const commonPicker = ref(null)
const type = props.isRange ? 'timerange' : 'time'
const panel = props.isRange ? TimeRangePanel : TimePickPanel
const refProps = {
...props,
focus: () => {
commonPicker.value?.handleFocusInput()
},
blur: () => {
commonPicker.value?.handleBlurInput()
},
}
provide('ElPopperOptions', props.popperOptions)
ctx.expose(refProps)
return () => {
const format = props.format ?? DEFAULT_FORMATS_TIME
return h(
Picker,
{
...props, // allow format to be overwrite
format,
type,
ref: commonPicker,
'onUpdate:modelValue': (value) =>
ctx.emit('update:modelValue', value),
},
{
default: (scopedProps) => h(panel, scopedProps),
}
)
}
},
})

View File

@ -0,0 +1,63 @@
import { defineComponent, provide, ref } from 'vue'
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
import { DEFAULT_FORMATS_TIME } from './common/constant'
import Picker from './common/picker.vue'
import TimePickPanel from './time-picker-com/panel-time-pick.vue'
import TimeRangePanel from './time-picker-com/panel-time-range.vue'
import { timePickerDefaultProps } from './common/props'
dayjs.extend(customParseFormat)
export default defineComponent({
name: 'ElTimePicker',
install: null,
props: {
...timePickerDefaultProps,
isRange: {
type: Boolean,
default: false,
},
},
emits: ['update:modelValue'],
setup(props, ctx) {
const commonPicker = ref<InstanceType<typeof Picker>>()
const [type, Panel] = props.isRange
? ['timerange', TimeRangePanel]
: ['time', TimePickPanel]
const modelUpdater = (value: any) => ctx.emit('update:modelValue', value)
provide('ElPopperOptions', props.popperOptions)
ctx.expose({
/**
* @description focus on the input element
*/
focus: (e: FocusEvent | undefined) => {
commonPicker.value?.handleFocusInput(e)
},
/**
* @description blur from the input element
*/
blur: (e: FocusEvent | undefined) => {
commonPicker.value?.handleBlurInput(e)
},
})
return () => {
const format = props.format ?? DEFAULT_FORMATS_TIME
return (
<Picker
{...props}
ref={commonPicker}
type={type}
format={format}
onUpdate:modelValue={modelUpdater}
>
{{
default: (props: any) => <Panel {...props} />,
}}
</Picker>
)
}
},
})