mirror of
https://gitee.com/element-plus/element-plus.git
synced 2024-12-05 04:37:47 +08:00
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:
parent
6b6c5a4a87
commit
5b233da008
@ -49,7 +49,6 @@ export default defineComponent({
|
||||
|
||||
const commonPicker = ref<InstanceType<typeof CommonPicker>>()
|
||||
const refProps = {
|
||||
...props,
|
||||
focus: (focusStartInput = true) => {
|
||||
commonPicker.value?.focus(focusStartInput)
|
||||
},
|
||||
|
@ -23,10 +23,10 @@
|
||||
<template #default>
|
||||
<el-input
|
||||
v-if="!isRangeInput"
|
||||
:id="id"
|
||||
:id="(id as string | undefined)"
|
||||
ref="inputRef"
|
||||
container-role="combobox"
|
||||
:model-value="displayValue"
|
||||
:model-value="(displayValue as string)"
|
||||
:name="name"
|
||||
:size="pickerSize"
|
||||
:disabled="pickerDisabled"
|
||||
@ -39,7 +39,7 @@
|
||||
@input="onUserInput"
|
||||
@focus="handleFocusInput"
|
||||
@blur="handleBlurInput"
|
||||
@keydown="handleKeydownInput"
|
||||
@keydown="handleKeydownInput as any"
|
||||
@change="handleChange"
|
||||
@mousedown="onMouseDownInput"
|
||||
@mouseenter="onMouseEnter"
|
||||
@ -80,7 +80,7 @@
|
||||
pickerSize ? nsRange.bm('editor', pickerSize) : '',
|
||||
$attrs.class,
|
||||
]"
|
||||
:style="$attrs.style"
|
||||
:style="($attrs.style as any)"
|
||||
@click="handleFocusInput"
|
||||
@mousedown="onMouseDownInput"
|
||||
@mouseenter="onMouseEnter"
|
||||
@ -162,17 +162,8 @@
|
||||
</template>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
inject,
|
||||
nextTick,
|
||||
provide,
|
||||
ref,
|
||||
unref,
|
||||
watch,
|
||||
} from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, nextTick, provide, ref, unref, watch } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { isEqual } from 'lodash-unified'
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
@ -181,27 +172,24 @@ import { formContextKey, formItemContextKey } from '@element-plus/tokens'
|
||||
import ElInput from '@element-plus/components/input'
|
||||
import ElIcon from '@element-plus/components/icon'
|
||||
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 { Calendar, Clock } from '@element-plus/icons-vue'
|
||||
import { timePickerDefaultProps } from './props'
|
||||
|
||||
import type { Dayjs } from 'dayjs'
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
import type { FormContext, FormItemContext } from '@element-plus/tokens'
|
||||
import type { Options } from '@popperjs/core'
|
||||
|
||||
interface PickerOptions {
|
||||
isValidValue: (date: Dayjs) => boolean
|
||||
handleKeydownInput: (event: KeyboardEvent) => void
|
||||
parseUserInput: (value: Dayjs) => dayjs.Dayjs
|
||||
formatToString: (value: Dayjs) => string | string[]
|
||||
getRangeAvailableTime: (date: Dayjs) => dayjs.Dayjs
|
||||
getDefaultValue: () => Dayjs
|
||||
panelReady: boolean
|
||||
handleClear: () => void
|
||||
handleFocusPicker?: () => void
|
||||
}
|
||||
import type { FormContext, FormItemContext } from '@element-plus/tokens'
|
||||
import type {
|
||||
DateModelType,
|
||||
DateOrDates,
|
||||
DayOrDays,
|
||||
PickerOptions,
|
||||
SingleOrRange,
|
||||
TimePickerDefaultProps,
|
||||
UserInput,
|
||||
} from './props'
|
||||
|
||||
// Date object and string
|
||||
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 aIsArray = Array.isArray(a)
|
||||
const bIsArray = Array.isArray(b)
|
||||
const aIsArray = isArray(a)
|
||||
const bIsArray = isArray(b)
|
||||
if (aIsArray && bIsArray) {
|
||||
if (a.length !== b.length) {
|
||||
return false
|
||||
@ -231,11 +219,11 @@ const valueEquals = function (a: Array<Date> | any, b: Array<Date> | any) {
|
||||
return false
|
||||
}
|
||||
|
||||
const parser = function (
|
||||
const parseDate = function (
|
||||
date: string | number | Date,
|
||||
format: string,
|
||||
format: string | undefined,
|
||||
lang: string
|
||||
): Dayjs {
|
||||
) {
|
||||
const day =
|
||||
isEmpty(format) || format === 'x'
|
||||
? dayjs(date).locale(lang)
|
||||
@ -244,8 +232,8 @@ const parser = function (
|
||||
}
|
||||
|
||||
const formatter = function (
|
||||
date: string | number | Date,
|
||||
format: string,
|
||||
date: string | number | Date | Dayjs,
|
||||
format: string | undefined,
|
||||
lang: string
|
||||
) {
|
||||
if (isEmpty(format)) return date
|
||||
@ -253,15 +241,12 @@ const formatter = function (
|
||||
return dayjs(date).locale(lang).format(format)
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
defineOptions({
|
||||
name: 'Picker',
|
||||
components: {
|
||||
ElInput,
|
||||
ElTooltip,
|
||||
ElIcon,
|
||||
},
|
||||
props: timePickerDefaultProps,
|
||||
emits: [
|
||||
})
|
||||
|
||||
const props = defineProps(timePickerDefaultProps)
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'change',
|
||||
'focus',
|
||||
@ -270,8 +255,8 @@ export default defineComponent({
|
||||
'panel-change',
|
||||
'visible-change',
|
||||
'keydown',
|
||||
],
|
||||
setup(props, ctx) {
|
||||
])
|
||||
|
||||
const { lang } = useLocale()
|
||||
|
||||
const nsDate = useNamespace('date')
|
||||
@ -286,7 +271,7 @@ export default defineComponent({
|
||||
const inputRef = ref<HTMLElement | ComponentPublicInstance>()
|
||||
const pickerVisible = ref(false)
|
||||
const pickerActualVisible = ref(false)
|
||||
const valueOnOpen = ref(null)
|
||||
const valueOnOpen = ref<TimePickerDefaultProps['modelValue'] | null>(null)
|
||||
|
||||
let hasJustTabExitedInput = false
|
||||
let ignoreFocusEvent = false
|
||||
@ -301,30 +286,34 @@ export default defineComponent({
|
||||
valueOnOpen.value = props.modelValue
|
||||
}
|
||||
})
|
||||
const emitChange = (val, isClear?: boolean) => {
|
||||
const emitChange = (
|
||||
val: TimePickerDefaultProps['modelValue'] | null,
|
||||
isClear?: boolean
|
||||
) => {
|
||||
// determine user real change only
|
||||
if (isClear || !valueEquals(val, valueOnOpen.value)) {
|
||||
ctx.emit('change', val)
|
||||
emit('change', val)
|
||||
props.validateEvent &&
|
||||
elFormItem.validate?.('change').catch((err) => debugWarn(err))
|
||||
}
|
||||
}
|
||||
const emitInput = (val) => {
|
||||
if (!valueEquals(props.modelValue, val)) {
|
||||
let formatValue
|
||||
if (Array.isArray(val)) {
|
||||
formatValue = val.map((_) =>
|
||||
formatter(_, props.valueFormat, lang.value)
|
||||
const emitInput = (input: SingleOrRange<DateModelType | Dayjs> | null) => {
|
||||
if (!valueEquals(props.modelValue, input)) {
|
||||
let formatted
|
||||
if (isArray(input)) {
|
||||
formatted = input.map((item) =>
|
||||
formatter(item, props.valueFormat, lang.value)
|
||||
)
|
||||
} else if (val) {
|
||||
formatValue = formatter(val, props.valueFormat, lang.value)
|
||||
} else if (input) {
|
||||
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) => {
|
||||
ctx.emit('keydown', e)
|
||||
const emitKeydown = (e: KeyboardEvent) => {
|
||||
emit('keydown', e)
|
||||
}
|
||||
|
||||
const refInput = computed<HTMLInputElement[]>(() => {
|
||||
if (inputRef.value) {
|
||||
const _r = isRangeInput.value
|
||||
@ -341,7 +330,7 @@ export default defineComponent({
|
||||
return refInput?.value[1]
|
||||
})
|
||||
|
||||
const setSelectionRange = (start, end, pos) => {
|
||||
const setSelectionRange = (start: number, end: number, pos?: 'min' | 'max') => {
|
||||
const _inputs = refInput.value
|
||||
if (!_inputs.length) return
|
||||
if (!pos || pos === 'min') {
|
||||
@ -365,7 +354,7 @@ export default defineComponent({
|
||||
}
|
||||
pickerVisible.value = visible
|
||||
let result
|
||||
if (Array.isArray(date)) {
|
||||
if (isArray(date)) {
|
||||
result = date.map((_) => _.toDate())
|
||||
} else {
|
||||
// clear btn emit null
|
||||
@ -380,7 +369,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const onShow = () => {
|
||||
ctx.emit('visible-change', true)
|
||||
emit('visible-change', true)
|
||||
}
|
||||
|
||||
const onKeydownPopperContent = (event: KeyboardEvent) => {
|
||||
@ -392,7 +381,7 @@ export default defineComponent({
|
||||
const onHide = () => {
|
||||
pickerActualVisible.value = false
|
||||
ignoreFocusEvent = false
|
||||
ctx.emit('visible-change', false)
|
||||
emit('visible-change', false)
|
||||
}
|
||||
|
||||
const focus = (focusStartInput = true, isIgnoreFocusEvent = false) => {
|
||||
@ -406,7 +395,7 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
const handleFocusInput = (e) => {
|
||||
const handleFocusInput = (e?: FocusEvent) => {
|
||||
if (
|
||||
props.readonly ||
|
||||
pickerDisabled.value ||
|
||||
@ -416,20 +405,19 @@ export default defineComponent({
|
||||
return
|
||||
}
|
||||
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
|
||||
const handleBlurInput = (e) => {
|
||||
const handleBlurInput = (e?: FocusEvent) => {
|
||||
const handleBlurDefer = async () => {
|
||||
setTimeout(() => {
|
||||
if (currentHandleBlurDeferCallback === handleBlurDefer) {
|
||||
if (
|
||||
!(
|
||||
refPopper.value?.isFocusInsideContent() &&
|
||||
!hasJustTabExitedInput
|
||||
refPopper.value?.isFocusInsideContent() && !hasJustTabExitedInput
|
||||
) &&
|
||||
refInput.value.filter((input) => {
|
||||
return input.contains(document.activeElement)
|
||||
@ -437,7 +425,7 @@ export default defineComponent({
|
||||
) {
|
||||
handleChange()
|
||||
pickerVisible.value = false
|
||||
ctx.emit('blur', e)
|
||||
emit('blur', e)
|
||||
props.validateEvent &&
|
||||
elFormItem.validate?.('blur').catch((err) => debugWarn(err))
|
||||
}
|
||||
@ -454,43 +442,44 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const parsedValue = computed(() => {
|
||||
let result
|
||||
let dayOrDays: DayOrDays
|
||||
if (valueIsEmpty.value) {
|
||||
if (pickerOptions.value.getDefaultValue) {
|
||||
result = pickerOptions.value.getDefaultValue()
|
||||
dayOrDays = pickerOptions.value.getDefaultValue()
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(props.modelValue)) {
|
||||
result = props.modelValue.map((_) =>
|
||||
parser(_, props.valueFormat, lang.value)
|
||||
)
|
||||
if (isArray(props.modelValue)) {
|
||||
dayOrDays = props.modelValue.map((d) =>
|
||||
parseDate(d, props.valueFormat, lang.value)
|
||||
) as [Dayjs, Dayjs]
|
||||
} else {
|
||||
result = parser(props.modelValue, props.valueFormat, lang.value)
|
||||
dayOrDays = parseDate(props.modelValue, props.valueFormat, lang.value)!
|
||||
}
|
||||
}
|
||||
|
||||
if (pickerOptions.value.getRangeAvailableTime) {
|
||||
const availableResult =
|
||||
pickerOptions.value.getRangeAvailableTime(result)
|
||||
if (!isEqual(availableResult, result)) {
|
||||
result = availableResult
|
||||
const availableResult = pickerOptions.value.getRangeAvailableTime(
|
||||
dayOrDays!
|
||||
)
|
||||
if (!isEqual(availableResult, dayOrDays!)) {
|
||||
dayOrDays = availableResult
|
||||
emitInput(
|
||||
Array.isArray(result)
|
||||
? result.map((_) => _.toDate())
|
||||
: result.toDate()
|
||||
(isArray(dayOrDays)
|
||||
? dayOrDays.map((_) => _.toDate())
|
||||
: dayOrDays.toDate()) as SingleOrRange<Date>
|
||||
)
|
||||
}
|
||||
}
|
||||
if (Array.isArray(result) && result.some((_) => !_)) {
|
||||
result = []
|
||||
if (isArray(dayOrDays!) && dayOrDays.some((day) => !day)) {
|
||||
dayOrDays = [] as unknown as DayOrDays
|
||||
}
|
||||
return result
|
||||
return dayOrDays!
|
||||
})
|
||||
|
||||
const displayValue = computed(() => {
|
||||
if (!pickerOptions.value.panelReady) return
|
||||
const displayValue = computed<UserInput>(() => {
|
||||
if (!pickerOptions.value.panelReady) return ''
|
||||
const formattedValue = formatDayjsToString(parsedValue.value)
|
||||
if (Array.isArray(userInput.value)) {
|
||||
if (isArray(userInput.value)) {
|
||||
return [
|
||||
userInput.value[0] || (formattedValue && formattedValue[0]) || '',
|
||||
userInput.value[1] || (formattedValue && formattedValue[1]) || '',
|
||||
@ -498,8 +487,8 @@ export default defineComponent({
|
||||
} else if (userInput.value !== null) {
|
||||
return userInput.value
|
||||
}
|
||||
if (!isTimePicker.value && valueIsEmpty.value) return
|
||||
if (!pickerVisible.value && valueIsEmpty.value) return
|
||||
if (!isTimePicker.value && valueIsEmpty.value) return ''
|
||||
if (!pickerVisible.value && valueIsEmpty.value) return ''
|
||||
if (formattedValue) {
|
||||
return isDatesPicker.value
|
||||
? (formattedValue as Array<string>).join(', ')
|
||||
@ -534,8 +523,7 @@ export default defineComponent({
|
||||
}
|
||||
const valueIsEmpty = computed(() => {
|
||||
return (
|
||||
!props.modelValue ||
|
||||
(Array.isArray(props.modelValue) && !props.modelValue.length)
|
||||
!props.modelValue || (isArray(props.modelValue) && !props.modelValue.length)
|
||||
)
|
||||
})
|
||||
const onMouseDownInput = async (event: MouseEvent) => {
|
||||
@ -569,10 +557,6 @@ export default defineComponent({
|
||||
|
||||
const pickerSize = useSize()
|
||||
|
||||
const popperPaneRef = computed(() => {
|
||||
return refPopper.value?.popperRef?.contentRef
|
||||
})
|
||||
|
||||
const popperEl = computed(() => unref(refPopper)?.popperRef?.contentRef)
|
||||
const actualInputRef = computed(() => {
|
||||
if (unref(isRangeInput)) {
|
||||
@ -596,7 +580,7 @@ export default defineComponent({
|
||||
pickerVisible.value = false
|
||||
})
|
||||
|
||||
const userInput = ref(null)
|
||||
const userInput = ref<UserInput>(null)
|
||||
|
||||
const handleChange = () => {
|
||||
if (userInput.value) {
|
||||
@ -604,9 +588,9 @@ export default defineComponent({
|
||||
if (value) {
|
||||
if (isValidValue(value)) {
|
||||
emitInput(
|
||||
Array.isArray(value)
|
||||
(isArray(value)
|
||||
? value.map((_) => _.toDate())
|
||||
: value.toDate()
|
||||
: value.toDate()) as DateOrDates
|
||||
)
|
||||
userInput.value = null
|
||||
}
|
||||
@ -619,22 +603,22 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
const parseUserInputToDayjs = (value) => {
|
||||
const parseUserInputToDayjs = (value: UserInput) => {
|
||||
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
|
||||
return pickerOptions.value.formatToString(value)
|
||||
return pickerOptions.value.formatToString!(value)
|
||||
}
|
||||
|
||||
const isValidValue = (value) => {
|
||||
return pickerOptions.value.isValidValue(value)
|
||||
const isValidValue = (value: DayOrDays) => {
|
||||
return pickerOptions.value.isValidValue!(value)
|
||||
}
|
||||
|
||||
const handleKeydownInput = async (event) => {
|
||||
const code = event.code
|
||||
const handleKeydownInput = async (event: KeyboardEvent) => {
|
||||
const { code } = event
|
||||
emitKeydown(event)
|
||||
if (code === EVENT_CODE.esc) {
|
||||
if (pickerVisible.value === true) {
|
||||
@ -669,7 +653,7 @@ export default defineComponent({
|
||||
if (
|
||||
userInput.value === null ||
|
||||
userInput.value === '' ||
|
||||
isValidValue(parseUserInputToDayjs(displayValue.value))
|
||||
isValidValue(parseUserInputToDayjs(displayValue.value) as DayOrDays)
|
||||
) {
|
||||
handleChange()
|
||||
pickerVisible.value = false
|
||||
@ -687,7 +671,7 @@ export default defineComponent({
|
||||
pickerOptions.value.handleKeydownInput(event)
|
||||
}
|
||||
}
|
||||
const onUserInput = (e) => {
|
||||
const onUserInput = (e: string) => {
|
||||
userInput.value = e
|
||||
// Temporary fix when the picker is dismissed and the input box
|
||||
// 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) {
|
||||
userInput.value = [event.target.value, userInput.value[1]]
|
||||
userInput.value = [target.value, userInput.value[1]]
|
||||
} 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) {
|
||||
userInput.value = [userInput.value[0], event.target.value]
|
||||
userInput.value = [userInput.value[0], target.value]
|
||||
} else {
|
||||
userInput.value = [null, event.target.value]
|
||||
userInput.value = [null, target.value]
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
userInput.value = [formatDayjsToString(value), displayValue.value[1]]
|
||||
const newValue = [value, parsedValue.value && parsedValue.value[1]]
|
||||
userInput.value = [
|
||||
formatDayjsToString(value) as string,
|
||||
displayValue.value?.[1] || null,
|
||||
]
|
||||
const newValue = [value, parsedVal && (parsedVal[1] || null)] as DayOrDays
|
||||
if (isValidValue(newValue)) {
|
||||
emitInput(newValue)
|
||||
userInput.value = null
|
||||
@ -725,10 +716,15 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
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()) {
|
||||
userInput.value = [displayValue.value[0], formatDayjsToString(value)]
|
||||
const newValue = [parsedValue.value && parsedValue.value[0], value]
|
||||
userInput.value = [
|
||||
unref(displayValue)?.[0] || null,
|
||||
formatDayjsToString(value) as string,
|
||||
]
|
||||
const newValue = [parsedVal && parsedVal[0], value] as DayOrDays
|
||||
if (isValidValue(newValue)) {
|
||||
emitInput(newValue)
|
||||
userInput.value = null
|
||||
@ -744,64 +740,38 @@ export default defineComponent({
|
||||
pickerOptions.value.panelReady = true
|
||||
}
|
||||
|
||||
const onCalendarChange = (e) => {
|
||||
ctx.emit('calendar-change', e)
|
||||
const onCalendarChange = (e: [Date, false | Date]) => {
|
||||
emit('calendar-change', e)
|
||||
}
|
||||
|
||||
const onPanelChange = (value, mode, view) => {
|
||||
ctx.emit('panel-change', value, mode, view)
|
||||
const onPanelChange = (
|
||||
value: [Dayjs, Dayjs],
|
||||
mode: 'month' | 'year',
|
||||
view: unknown
|
||||
) => {
|
||||
emit('panel-change', value, mode, view)
|
||||
}
|
||||
|
||||
provide('EP_PICKER_BASE', {
|
||||
props,
|
||||
})
|
||||
|
||||
return {
|
||||
nsDate,
|
||||
nsInput,
|
||||
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,
|
||||
defineExpose({
|
||||
/**
|
||||
* @description focus input box.
|
||||
*/
|
||||
focus,
|
||||
onShow,
|
||||
onBeforeShow,
|
||||
onHide,
|
||||
onKeydownPopperContent,
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description emit focus event
|
||||
*/
|
||||
handleFocusInput,
|
||||
/**
|
||||
* @description emit blur event
|
||||
*/
|
||||
handleBlurInput,
|
||||
/**
|
||||
* @description pick item manually
|
||||
*/
|
||||
onPick,
|
||||
})
|
||||
</script>
|
||||
|
@ -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 type { Component, PropType } from 'vue'
|
||||
import type { Component, ExtractPropTypes } from 'vue'
|
||||
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: {
|
||||
type: [Array, String],
|
||||
type: definePropType<SingleOrRange<string>>([Array, String]),
|
||||
},
|
||||
name: {
|
||||
type: [Array, String],
|
||||
type: definePropType<SingleOrRange<string>>([Array, String]),
|
||||
default: '',
|
||||
},
|
||||
popperClass: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
},
|
||||
valueFormat: {
|
||||
type: String as PropType<string>,
|
||||
},
|
||||
format: String,
|
||||
valueFormat: String,
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
@ -32,7 +36,7 @@ export const timePickerDefaultProps = {
|
||||
default: true,
|
||||
},
|
||||
clearIcon: {
|
||||
type: [String, Object] as PropType<string | Component>,
|
||||
type: definePropType<string | Component>([String, Object]),
|
||||
default: CircleClose,
|
||||
},
|
||||
editable: {
|
||||
@ -40,13 +44,10 @@ export const timePickerDefaultProps = {
|
||||
default: true,
|
||||
},
|
||||
prefixIcon: {
|
||||
type: [String, Object] as PropType<string | Component>,
|
||||
type: definePropType<string | Component>([String, Object]),
|
||||
default: '',
|
||||
},
|
||||
size: {
|
||||
type: String as PropType<ComponentSize>,
|
||||
validator: isValidComponentSize,
|
||||
},
|
||||
size: useSizeProp,
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@ -60,13 +61,11 @@ export const timePickerDefaultProps = {
|
||||
default: '',
|
||||
},
|
||||
popperOptions: {
|
||||
type: Object as PropType<Partial<Options>>,
|
||||
type: definePropType<Partial<Options>>(Object),
|
||||
default: () => ({}),
|
||||
},
|
||||
modelValue: {
|
||||
type: [Date, Array, String, Number] as PropType<
|
||||
number | string | Date | (number | string | Date)[]
|
||||
>,
|
||||
type: definePropType<ModelValueType>([Date, Array, String, Number]),
|
||||
default: '',
|
||||
},
|
||||
rangeSeparator: {
|
||||
@ -76,10 +75,10 @@ export const timePickerDefaultProps = {
|
||||
startPlaceholder: String,
|
||||
endPlaceholder: String,
|
||||
defaultValue: {
|
||||
type: [Date, Array] as PropType<Date | Date[]>,
|
||||
type: definePropType<SingleOrRange<Date>>([Date, Array]),
|
||||
},
|
||||
defaultTime: {
|
||||
type: [Date, Array] as PropType<Date | Date[]>,
|
||||
type: definePropType<SingleOrRange<Date>>([Date, Array]),
|
||||
},
|
||||
isRange: {
|
||||
type: Boolean,
|
||||
@ -113,7 +112,7 @@ export const timePickerDefaultProps = {
|
||||
default: undefined,
|
||||
},
|
||||
tabindex: {
|
||||
type: [String, Number],
|
||||
type: definePropType<string | number>([String, Number]),
|
||||
default: 0,
|
||||
},
|
||||
validateEvent: {
|
||||
@ -121,4 +120,20 @@ export const timePickerDefaultProps = {
|
||||
default: true,
|
||||
},
|
||||
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
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ const getRangeAvailableTime = ([start, end]: Array<Dayjs>) => {
|
||||
return [
|
||||
getAvailableTime(start, 'start', true, end),
|
||||
getAvailableTime(end, 'end', false, start),
|
||||
]
|
||||
] as const
|
||||
}
|
||||
|
||||
const { getAvailableHours, getAvailableMinutes, getAvailableSeconds } =
|
||||
|
@ -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),
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
63
packages/components/time-picker/src/time-picker.tsx
Normal file
63
packages/components/time-picker/src/time-picker.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
Loading…
Reference in New Issue
Block a user