refactor(components): [time-select] switch to script-setup syntax (#7833)

* refactor(components): [time-select] switch to script-setup syntax

* fix(components): [time-select] fix typing

* fix(components): [time-select] fix props reviews

* fix(components): [time-select] fix props and build

Co-authored-by: metanas <matanas@pre-history.com>
This commit is contained in:
Anas Boudih 2022-05-25 16:06:12 +01:00 committed by GitHub
parent 66a6d0dc70
commit 1069e9ff34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 195 additions and 195 deletions

View File

@ -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/",

View File

@ -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', () => {

View File

@ -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 | Component>([String, Object]),
default: () => Clock,
},
clearIcon: {
type: definePropType<string | Component>([String, Object]),
default: () => CircleClose,
},
} as const)
export type TimeSelectProps = ExtractPropTypes<typeof timeSelectProps>
export type TimeSelectInstance = InstanceType<typeof TimeSelect>

View File

@ -30,204 +30,90 @@
</el-select>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from 'vue'
<script lang="ts" setup>
import { computed, ref } from 'vue'
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import ElSelect from '@element-plus/components/select'
import ElIcon from '@element-plus/components/icon'
import { CircleClose, Clock } from '@element-plus/icons-vue'
import { useNamespace } from '@element-plus/hooks'
import { timeSelectProps } from './time-select'
import { compareTime, formatTime, nextTime, parseTime } from './utils'
import { componentSizes } from '@element-plus/constants'
import type { Component, PropType } from 'vue'
dayjs.extend(customParseFormat)
const { Option: ElOption } = ElSelect
interface Time {
hours: number
minutes: number
}
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
}
const compareTime = (time1: string, time2: string): number => {
const value1 = parseTime(time1)
const value2 = parseTime(time2)
const minutes1 = value1.minutes + value1.hours * 60
const minutes2 = value2.minutes + value2.hours * 60
if (minutes1 === minutes2) {
return 0
}
return minutes1 > minutes2 ? 1 : -1
}
const padTime = (time: number | string) => {
return `${time}`.padStart(2, '0')
}
const formatTime = (time: Time): string => {
return `${padTime(time.hours)}:${padTime(time.minutes)}`
}
const nextTime = (time: string, step: string): string => {
const timeValue = parseTime(time)
const stepValue = parseTime(step)
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)
}
export default defineComponent({
defineOptions({
name: 'ElTimeSelect',
components: { ElSelect, ElOption, ElIcon },
model: {
prop: 'value',
event: 'change',
},
props: {
format: {
type: String,
default: 'HH:mm',
},
modelValue: String,
disabled: {
type: Boolean,
default: false,
},
editable: {
type: Boolean,
default: true,
},
effect: {
type: String as PropType<'light' | 'dark' | string>,
default: 'light',
},
clearable: {
type: Boolean,
default: true,
},
size: {
type: String as PropType<ComponentSize>,
values: componentSizes,
default: '',
},
placeholder: {
type: String,
default: '',
},
start: {
type: String,
default: '09:00',
},
end: {
type: String,
default: '18:00',
},
step: {
type: String,
default: '00:30',
},
minTime: {
type: String,
default: '',
},
maxTime: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
prefixIcon: {
type: [String, Object] as PropType<string | Component>,
default: Clock,
},
clearIcon: {
type: [String, Object] as PropType<string | Component>,
default: CircleClose,
},
},
emits: ['change', 'blur', 'focus', 'update:modelValue'],
setup(props) {
const nsInput = useNamespace('input')
const select = ref(null)
})
const value = computed(() => props.modelValue)
const start = computed(() => {
const time = parseTime(props.start)
return formatTime(time)
})
const end = computed(() => {
const time = parseTime(props.end)
return formatTime(time)
})
const step = computed(() => {
const time = parseTime(props.step)
return formatTime(time)
})
const minTime = computed(() => {
const time = parseTime(props.minTime)
return time ? formatTime(time) : null
})
const maxTime = computed(() => {
const time = parseTime(props.maxTime)
return time ? formatTime(time) : null
})
const items = computed(() => {
const result = []
if (props.start && props.end && props.step) {
let current = start.value
let currentTime
while (compareTime(current, end.value) <= 0) {
currentTime = dayjs(current, 'HH:mm').format(props.format)
result.push({
value: currentTime,
disabled:
compareTime(current, minTime.value || '-1:-1') <= 0 ||
compareTime(current, maxTime.value || '100:100') >= 0,
})
current = nextTime(current, step.value)
}
}
return result
})
const blur = () => {
select.value?.blur?.()
}
const focus = () => {
select.value?.focus?.()
}
defineEmits(['change', 'blur', 'focus', 'update:modelValue'])
return {
nsInput,
select,
value,
items,
blur,
focus,
const props = defineProps(timeSelectProps)
const nsInput = useNamespace('input')
const select = ref<typeof ElSelect>()
const value = computed(() => props.modelValue)
const start = computed(() => {
const time = parseTime(props.start)
return time ? formatTime(time) : null
})
const end = computed(() => {
const time = parseTime(props.end)
return time ? formatTime(time) : null
})
const step = computed(() => {
const time = parseTime(props.step)
return time ? formatTime(time) : null
})
const minTime = computed(() => {
const time = parseTime(props.minTime || '')
return time ? formatTime(time) : null
})
const maxTime = computed(() => {
const time = parseTime(props.maxTime || '')
return time ? formatTime(time) : null
})
const items = computed(() => {
const result: { value: string; disabled: boolean }[] = []
if (props.start && props.end && props.step) {
let current = start.value
let currentTime: string
while (current && end.value && compareTime(current, end.value) <= 0) {
currentTime = dayjs(current, 'HH:mm').format(props.format)
result.push({
value: currentTime,
disabled:
compareTime(current, minTime.value || '-1:-1') <= 0 ||
compareTime(current, maxTime.value || '100:100') >= 0,
})
current = nextTime(current, step.value!)
}
},
}
return result
})
const blur = () => {
select.value?.blur?.()
}
const focus = () => {
select.value?.focus?.()
}
defineExpose({
blur,
focus,
})
</script>

View File

@ -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)
}