diff --git a/internal/build/src/type-unsafe.json b/internal/build/src/type-unsafe.json index 95094f7025..2bc2d751ad 100644 --- a/internal/build/src/type-unsafe.json +++ b/internal/build/src/type-unsafe.json @@ -35,7 +35,6 @@ "packages/components/table-column/", "packages/components/table-v2/", "packages/components/time-picker/", - "packages/components/time-select/", "packages/components/timeline/", "packages/components/timeline-item/", "packages/components/tooltip/", diff --git a/packages/components/time-select/__tests__/time-select.test.ts b/packages/components/time-select/__tests__/time-select.test.ts index a4e2a466e4..df6330fe6c 100644 --- a/packages/components/time-select/__tests__/time-select.test.ts +++ b/packages/components/time-select/__tests__/time-select.test.ts @@ -52,7 +52,7 @@ describe('TimeSelect', () => { input.trigger('focus') await nextTick() expect(document.querySelector('.selected')).toBeDefined() - expect(document.querySelector('.selected').textContent).toBe('14:30') + expect(document.querySelector('.selected')?.textContent).toBe('14:30') }) it('set minTime', async () => { @@ -73,7 +73,7 @@ describe('TimeSelect', () => { input.trigger('focus') await nextTick() const elm = document.querySelector('.is-disabled') - expect(elm.textContent).toBe('14:30') + expect(elm?.textContent).toBe('14:30') }) it('set value update', async () => { @@ -86,12 +86,10 @@ describe('TimeSelect', () => { expect(input.exists()).toBe(true) expect(input.element.value).toBe('10:00') - // wrapper.setData is not supported until version 2.0.0-beta.8 - // change value directly on `wrapper.vm` - const vm = wrapper.vm as any - vm.value = '10:30' + + wrapper.setData({ value: '10:30' }) await nextTick() - expect(vm.value).toBe('10:30') + expect(wrapper.vm.value).toBe('10:30') expect(input.element.value).toBe('10:30') }) @@ -109,8 +107,8 @@ describe('TimeSelect', () => { .findAllComponents(Option) .find((w) => w.text().trim() === '11:00') - expect(option.exists()).toBe(true) - option.trigger('click') + expect(option?.exists()).toBe(true) + option?.trigger('click') await nextTick() expect(vm.value).toBe('11:00') expect(input.element.value).toBe('11:00') @@ -160,7 +158,7 @@ describe('TimeSelect', () => { await nextTick() const popperEl = document.querySelector('.el-select__popper') - const attr = popperEl.getAttribute('aria-hidden') + const attr = popperEl?.getAttribute('aria-hidden') expect(attr).toEqual('false') }) @@ -176,7 +174,7 @@ describe('TimeSelect', () => { await nextTick() const popperEl = document.querySelector('.el-select__popper') - const attr = popperEl.getAttribute('aria-hidden') + const attr = popperEl?.getAttribute('aria-hidden') expect(attr).toEqual('true') }) @@ -196,7 +194,7 @@ describe('TimeSelect', () => { await input.trigger('click') await nextTick() const option = document.querySelector('.el-select-dropdown__item') - expect(option.textContent).toBe('01:00 PM') + expect(option?.textContent).toBe('01:00 PM') }) describe('form item accessibility integration', () => { diff --git a/packages/components/time-select/src/time-select.ts b/packages/components/time-select/src/time-select.ts new file mode 100644 index 0000000000..de8963eede --- /dev/null +++ b/packages/components/time-select/src/time-select.ts @@ -0,0 +1,55 @@ +import { buildProps, definePropType } from '@element-plus/utils' +import { CircleClose, Clock } from '@element-plus/icons-vue' +import { useSizeProp } from '@element-plus/hooks' +import type TimeSelect from './time-select.vue' +import type { Component, ExtractPropTypes, PropType } from 'vue' + +export const timeSelectProps = buildProps({ + format: { + type: String, + default: 'HH:mm', + }, + modelValue: String, + disabled: Boolean, + editable: { + type: Boolean, + default: true, + }, + effect: { + type: String as PropType<'light' | 'dark' | string>, + default: 'light', + }, + clearable: { + type: Boolean, + default: true, + }, + size: useSizeProp, + placeholder: String, + start: { + type: String, + default: '09:00', + }, + end: { + type: String, + default: '18:00', + }, + step: { + type: String, + default: '00:30', + }, + minTime: String, + maxTime: String, + name: String, + prefixIcon: { + type: definePropType([String, Object]), + default: () => Clock, + }, + clearIcon: { + type: definePropType([String, Object]), + default: () => CircleClose, + }, +} as const) + +export type TimeSelectProps = ExtractPropTypes + +export type TimeSelectInstance = InstanceType diff --git a/packages/components/time-select/src/time-select.vue b/packages/components/time-select/src/time-select.vue index 2652fd9453..1a2533b1be 100644 --- a/packages/components/time-select/src/time-select.vue +++ b/packages/components/time-select/src/time-select.vue @@ -30,204 +30,90 @@ - diff --git a/packages/components/time-select/src/utils.ts b/packages/components/time-select/src/utils.ts new file mode 100644 index 0000000000..c6f7caaa05 --- /dev/null +++ b/packages/components/time-select/src/utils.ts @@ -0,0 +1,62 @@ +interface Time { + hours: number + minutes: number +} + +export const parseTime = (time: string): null | Time => { + const values = (time || '').split(':') + if (values.length >= 2) { + let hours = Number.parseInt(values[0], 10) + const minutes = Number.parseInt(values[1], 10) + const timeUpper = time.toUpperCase() + if (timeUpper.includes('AM') && hours === 12) { + hours = 0 + } else if (timeUpper.includes('PM') && hours !== 12) { + hours += 12 + } + return { + hours, + minutes, + } + } + + return null +} + +export const compareTime = (time1: string, time2: string): number => { + const value1 = parseTime(time1) + if (!value1) return -1 + const value2 = parseTime(time2) + if (!value2) return -1 + const minutes1 = value1.minutes + value1.hours * 60 + const minutes2 = value2.minutes + value2.hours * 60 + if (minutes1 === minutes2) { + return 0 + } + return minutes1 > minutes2 ? 1 : -1 +} + +export const padTime = (time: number | string) => { + return `${time}`.padStart(2, '0') +} +export const formatTime = (time: Time): string => { + return `${padTime(time.hours)}:${padTime(time.minutes)}` +} + +export const nextTime = (time: string, step: string): string => { + const timeValue = parseTime(time) + if (!timeValue) return '' + + const stepValue = parseTime(step) + if (!stepValue) return '' + + const next = { + hours: timeValue.hours, + minutes: timeValue.minutes, + } + next.minutes += stepValue.minutes + next.hours += stepValue.hours + next.hours += Math.floor(next.minutes / 60) + next.minutes = next.minutes % 60 + return formatTime(next) +}