feat(components): [el-time-select] support custom format (e.g. 12-hour) (#5309)

This commit is contained in:
Alan Wang
2022-01-12 11:11:43 +08:00
committed by GitHub
parent 951f07e91e
commit 2cf215e3c4
5 changed files with 124 additions and 27 deletions

View File

@@ -7,6 +7,8 @@ lang: en-US
Use Time Select for time input.
The available time range is 00:00 to 23:59
## Fixed time picker
Provide a list of fixed time for users to choose.
@@ -17,9 +19,27 @@ time-select/basic
:::
## Time Formats
Use `format` to control format of time(hours and minutes).
Check the list [here](https://day.js.org/docs/en/display/format#list-of-all-available-formats) of all available formats of Day.js.
:::warning
Pay attention to capitalization
:::
:::demo
time-select/time-formats
:::
## Fixed time range
If start time is picked at first, then the end time will change accordingly.
If start( end ) time is picked at first, then the status of end( start ) time's options will change accordingly.
:::demo
@@ -29,22 +49,23 @@ time-select/time-range
## Attributes
| Attribute | Description | Type | Accepted Values | Default |
| --------------------- | -------------------------------------------------------- | ------------------ | ----------------------- | ----------- |
| model-value / v-model | binding value | string | — | — |
| disabled | whether TimeSelect is disabled | boolean | — | false |
| editable | whether the input is editable | boolean | — | true |
| clearable | whether to show clear button | boolean | — | true |
| size | size of Input | string | large / default / small | default |
| placeholder | placeholder in non-range mode | string | — | — |
| name | same as `name` in native input | string | — | — |
| prefix-icon | Custom prefix icon component | string / Component | — | Clock |
| clear-icon | Custom clear icon component | string / Component | — | CircleClose |
| start | start time | string | — | 09:00 |
| end | end time | string | — | 18:00 |
| step | time step | string | — | 00:30 |
| min-time | minimum time, any time before this time will be disabled | string | — | 00:00 |
| max-time | maximum time, any time after this time will be disabled | string | — | — |
| Attribute | Description | Type | Accepted Values | Default |
| --------------------- | -------------------------------------------------------- | ------------------ | -------------------------------------------------------------------------------------- | ----------- |
| model-value / v-model | binding value | string | — | — |
| disabled | whether TimeSelect is disabled | boolean | — | false |
| editable | whether the input is editable | boolean | — | true |
| clearable | whether to show clear button | boolean | — | true |
| size | size of Input | string | large / default / small | default |
| placeholder | placeholder in non-range mode | string | — | — |
| name | same as `name` in native input | string | — | — |
| prefix-icon | Custom prefix icon component | string / Component | — | Clock |
| clear-icon | Custom clear icon component | string / Component | — | CircleClose |
| start | start time | string | — | 09:00 |
| end | end time | string | — | 18:00 |
| step | time step | string | — | 00:30 |
| min-time | minimum time, any time before this time will be disabled | string | — | 00:00 |
| max-time | maximum time, any time after this time will be disabled | string | — | — |
| format | set format of time | string | see [formats](https://day.js.org/docs/en/display/format#list-of-all-available-formats) | HH:mm |
## Events

View File

@@ -0,0 +1,17 @@
<template>
<el-time-select
v-model="value"
start="00:00"
step="00:30"
end="23:59"
placeholder="Select time"
format="hh:mm A"
>
</el-time-select>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const value = ref('')
</script>

View File

@@ -2,6 +2,7 @@
<div class="demo-time-range">
<el-time-select
v-model="startTime"
:max-time="endTime"
class="mr-4"
placeholder="Start time"
start="08:30"

View File

@@ -1,8 +1,11 @@
import { nextTick } from 'vue'
import { mount } from '@vue/test-utils'
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import Select from '@element-plus/components/select'
import { sleep } from '@element-plus/test-utils'
import TimeSelect from '../src/time-select.vue'
dayjs.extend(customParseFormat)
const { Option } = Select
@@ -170,4 +173,23 @@ describe('TimeSelect', () => {
const attr = popperEl.getAttribute('aria-hidden')
expect(attr).toEqual('true')
})
it('set format', async () => {
const wrapper = _mount(
`<el-time-select
v-model="value"
start="13:00"
step="00:30"
end="13:30"
format="hh:mm A"
>
</el-time-select>`,
() => ({ value: '' })
)
const input = wrapper.find('.el-input__inner')
await input.trigger('click')
await nextTick()
const option = document.querySelector('.el-select-dropdown__item')
expect(option.textContent).toBe('01:00 PM')
})
})

View File

@@ -31,12 +31,15 @@
<script lang="ts">
import { defineComponent, computed, ref } from 'vue'
import dayjs from 'dayjs'
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 type { PropType, Component } from 'vue'
import type { ComponentSize } from '@element-plus/utils/types'
dayjs.extend(customParseFormat)
const { Option: ElOption } = ElSelect
@@ -48,8 +51,14 @@ interface Time {
const parseTime = (time: string): null | Time => {
const values = (time || '').split(':')
if (values.length >= 2) {
const hours = parseInt(values[0], 10)
let hours = parseInt(values[0], 10)
const minutes = 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,
@@ -67,10 +76,11 @@ const compareTime = (time1: string, time2: string): number => {
}
return minutes1 > minutes2 ? 1 : -1
}
const padTime = (time: number | string) => {
return `${time}`.padStart(2, '0')
}
const formatTime = (time: Time): string => {
return `${time.hours < 10 ? `0${time.hours}` : time.hours}:${
time.minutes < 10 ? `0${time.minutes}` : time.minutes
}`
return `${padTime(time.hours)}:${padTime(time.minutes)}`
}
const nextTime = (time: string, step: string): string => {
const timeValue = parseTime(time)
@@ -94,6 +104,10 @@ export default defineComponent({
event: 'change',
},
props: {
format: {
type: String,
default: 'HH:mm',
},
modelValue: String,
disabled: {
type: Boolean,
@@ -155,18 +169,40 @@ export default defineComponent({
// computed
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 = props.start
while (compareTime(current, props.end) <= 0) {
let current = start.value
let currentTime
while (compareTime(current, end.value) <= 0) {
currentTime = dayjs(current, 'HH:mm').format(props.format)
result.push({
value: current,
value: currentTime,
disabled:
compareTime(current, props.minTime || '-1:-1') <= 0 ||
compareTime(current, props.maxTime || '100:100') >= 0,
compareTime(current, minTime.value || '-1:-1') <= 0 ||
compareTime(current, maxTime.value || '100:100') >= 0,
})
current = nextTime(current, props.step)
current = nextTime(current, step.value)
}
}
return result