Skip to content

Commit

Permalink
fix(comp:time-picker): inputs should check disabled time
Browse files Browse the repository at this point in the history
  • Loading branch information
sallerli1 committed Sep 26, 2022
1 parent 7f656fc commit c5c6f1d
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 56 deletions.
1 change: 1 addition & 0 deletions packages/components/_private/time-panel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import TimePanel from './src/TimePanel'
const ɵTimePanel = TimePanel as unknown as TimePanelComponent

export { ɵTimePanel }
export { normalizeAmPm as ɵNormalizeAmPm, calculateViewHour as ɵCalculateViewHour } from './src/utils'

export type {
TimePanelInstance as ɵTimePanelInstance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export function useOptions(
const viewHours = computed(
() => selectedValue.value && calculateViewHour(get(selectedValue.value, 'hour'), props.use12Hours),
)
const ampm = computed(() => selectedValue.value && normalizeAmPm(get(selectedValue.value, 'hour'), props.use12Hours))

const getAmPm = (value: Date | undefined) => value && normalizeAmPm(get(value, 'hour'), props.use12Hours)
const ampm = computed(() => getAmPm(selectedValue.value))

function getOptions(type: TimePanelColumnType): TimePanelCell[] {
const getHourOptions = () => {
Expand Down Expand Up @@ -78,23 +80,13 @@ export function useOptions(
}
}

const getHourValue = (value: Date) => {
const hour = ampm.value === 'pm' ? get(value, 'hour') % 12 : get(value, 'hour')

if (ampm.value) {
return hour === 0 ? 12 : hour
}

return hour
}

function getColumnValue(value: Date, type: TimePanelColumnType) {
switch (type) {
case 'AM/PM':
return ampm.value
return getAmPm(value)
case 'hour':
default:
return getHourValue(value)
return calculateViewHour(get(value, 'hour'), props.use12Hours)
case 'minute':
return get(value, 'minute')
case 'second':
Expand All @@ -112,9 +104,11 @@ export function useOptions(
cell.value,
)

!cell.disabled && setSelectedValue(newValue)
setActiveValue(newValue)
callEmit(props.onChange, newValue)
if (!cell.disabled) {
setSelectedValue(newValue)
callEmit(props.onChange, newValue)
}
}

return (cell: TimePanelCell) => onActiveChange(type, cell)
Expand Down Expand Up @@ -161,7 +155,7 @@ function generateNumericOptions(

function generateAmPmOptions(disabledOption: string, hideDisabledOptions: boolean): TimePanelCell[] {
disabledOption = disabledOption.toLowerCase()
return ['am', 'pm']
return (['am', 'pm'] as const)
.map(item => ({
disabled: disabledOption === item,
value: item,
Expand Down
14 changes: 6 additions & 8 deletions packages/components/_private/time-panel/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,20 @@ import type { DefineComponent, HTMLAttributes, PropType } from 'vue'

import { MaybeArray } from '@idux/cdk/utils'

export type AmPm = 'am' | 'pm'

export const baseTimePanelProps = {
disabledHours: {
type: Function as PropType<(selectedAmPm: string | undefined) => number[]>,
type: Function as PropType<(selectedAmPm: AmPm | undefined) => number[]>,
default: () => [],
},
disabledMinutes: {
type: Function as PropType<(selectedHour: number | undefined, selectedAmPm: string | undefined) => number[]>,
type: Function as PropType<(selectedHour: number | undefined, selectedAmPm: AmPm | undefined) => number[]>,
default: () => [],
},
disabledSeconds: {
type: Function as PropType<
(
selectedHour: number | undefined,
selectedMinute: number | undefined,
selectedAmPm: string | undefined,
) => number[]
(selectedHour: number | undefined, selectedMinute: number | undefined, selectedAmPm: AmPm | undefined) => number[]
>,
default: () => [],
},
Expand Down Expand Up @@ -89,7 +87,7 @@ export type TimePanelInstance = InstanceType<DefineComponent<TimePanelProps>>

// private
export interface TimePanelCell {
value: number | string
value: number | 'am' | 'pm'
disabled: boolean
}

Expand Down
12 changes: 6 additions & 6 deletions packages/components/_private/time-panel/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import type { TimePanelColumnType } from './types'
import type { DateConfig } from '@idux/components/config'

export function normalizeAmPm(hour: number, is12Hours = false): 'am' | 'pm' | '' {
export function normalizeAmPm(hour: number, is12Hours = false): 'am' | 'pm' | undefined {
if (!is12Hours) {
return ''
return
}

return hour >= 12 ? 'pm' : 'am'
Expand All @@ -26,9 +26,9 @@ export function calculateViewHour(hour: number, is12Hours: boolean): number {
return hour
}

export function getHourValue(hour: number, ampm?: string): number {
export function getHourValue(hour: number, ampm?: 'am' | 'pm' | ''): number {
if (ampm) {
ampm = ampm.toLowerCase()
ampm = ampm.toLowerCase() as 'am' | 'pm' | ''
if (ampm === 'am') {
hour >= 12 && (hour -= 12)
}
Expand All @@ -45,7 +45,7 @@ export function calculateValue(
dateNow: Date,
type: TimePanelColumnType,
is12Hours: boolean,
value: number | string,
value: number | 'am' | 'pm' | '',
): Date {
const { get, set } = dateConfig
const selectNumber = Number(value)
Expand All @@ -59,6 +59,6 @@ export function calculateValue(
case 'second':
return set(newDate, selectNumber, 'second')
case 'AM/PM':
return set(newDate, getHourValue(get(newDate, 'hour'), value.toString()), 'hour')
return set(newDate, getHourValue(get(newDate, 'hour'), value.toString() as 'am' | 'pm' | ''), 'hour')
}
}
11 changes: 8 additions & 3 deletions packages/components/time-picker/demo/Disable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<IxTimePicker
v-model:value="time"
format="hh:mm:ss a"
allow-input
:hide-disabled-options="hideDisabledOptions"
:disabled-hours="disabledHours"
:disabled-minutes="disabledMinutes"
Expand Down Expand Up @@ -34,15 +35,19 @@ const disabled = ref(true)
const readonly = ref(true)
const hideDisabledOptions = ref(false)
function disabledHours(selectedAmPm: string) {
function disabledHours(selectedAmPm: string | undefined) {
return selectedAmPm === 'am' ? [1, 2, 3] : []
}
function disabledMinutes(selectedHour: number, selectedAmPm: string) {
function disabledMinutes(selectedHour: number | undefined, selectedAmPm: string | undefined) {
return selectedAmPm === 'pm' && selectedHour === 12 ? [1, 2, 3] : []
}
function disabledSeconds(selectedHour: number, selectedMinute: number, selectedAmPm: string) {
function disabledSeconds(
selectedHour: number | undefined,
selectedMinute: number | undefined,
selectedAmPm: string | undefined,
) {
return selectedAmPm === 'pm' && selectedHour === 12 && selectedMinute === 0 ? [1, 2, 3] : []
}
</script>
Expand Down
34 changes: 14 additions & 20 deletions packages/components/time-picker/src/composables/usePanelProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { TimePickerProps, TimeRangePickerProps } from '../types'
import type { ɵBaseTimePanelProps } from '@idux/components/_private/time-panel'

import { type ComputedRef, computed } from 'vue'

import { TimePickerContext, TimeRangePickerContext } from '../tokens'
import { checkAmPmCapital, checkHourEnabled, checkMinuteEnabled, checkSecondEnabled, checkUse12Hours } from '../utils'

export interface CommonPanelProps extends ɵBaseTimePanelProps {
hourEnabled: boolean
Expand All @@ -19,21 +20,14 @@ export interface CommonPanelProps extends ɵBaseTimePanelProps {
amPmCapital: boolean
}

export function usePanelProps(context: TimePickerContext | TimeRangePickerContext): ComputedRef<CommonPanelProps> {
export function usePanelProps(
props: TimePickerProps | TimeRangePickerProps,
formatRef: ComputedRef<string>,
): ComputedRef<CommonPanelProps> {
return computed(() => {
const { props, config } = context
const {
disabledHours,
disabledMinutes,
disabledSeconds,
format,
hideDisabledOptions,
hourStep,
minuteStep,
secondStep,
} = props

const _format = format ?? config.format
const { disabledHours, disabledMinutes, disabledSeconds, hideDisabledOptions, hourStep, minuteStep, secondStep } =
props
const format = formatRef.value

return {
disabledHours,
Expand All @@ -43,11 +37,11 @@ export function usePanelProps(context: TimePickerContext | TimeRangePickerContex
hourStep,
minuteStep,
secondStep,
hourEnabled: /[hH]/.test(_format),
minuteEnabled: /m/.test(_format),
secondEnabled: /s/.test(_format),
use12Hours: /[aA]/.test(_format),
amPmCapital: /A/.test(_format),
hourEnabled: checkHourEnabled(format),
minuteEnabled: checkMinuteEnabled(format),
secondEnabled: checkSecondEnabled(format),
use12Hours: checkUse12Hours(format),
amPmCapital: checkAmPmCapital(format),
}
})
}
29 changes: 27 additions & 2 deletions packages/components/time-picker/src/composables/usePickerState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import { type ComputedRef, toRaw } from 'vue'
import { isArray } from 'lodash-es'

import { type FormAccessor, useAccessorAndControl } from '@idux/cdk/forms'
import { callEmit, useState } from '@idux/cdk/utils'
import { callEmit, convertArray, useState } from '@idux/cdk/utils'
import { ɵCalculateViewHour, ɵNormalizeAmPm } from '@idux/components/_private/time-panel'
import { type DateConfig } from '@idux/components/config'
import { useFormItemRegister } from '@idux/components/form'

import { type TimePickerProps, type TimeRangePickerProps } from '../types'
import { convertToDate, sortRangeValue } from '../utils'
import { checkUse12Hours, convertToDate, sortRangeValue } from '../utils'

type StateValueType<T extends TimePickerProps | TimeRangePickerProps> = T extends TimePickerProps
? Date | undefined
Expand All @@ -40,8 +41,32 @@ export function usePickerState<T extends TimePickerProps | TimeRangePickerProps>

const [isFocused, setFocused] = useState(false)

const checkValueDisabled = (value: StateValueType<T>) => {
const { get } = dateConfig
const use12Hours = checkUse12Hours(formatRef.value)

return convertArray(value).some(v => {
const _hour = get(v, 'hour')
const amPm = ɵNormalizeAmPm(_hour, use12Hours)
const hour = ɵCalculateViewHour(_hour, !!use12Hours)
const minute = get(v, 'minute')
const second = get(v, 'second')

return (
props.disabledHours(amPm).includes(hour) ||
props.disabledMinutes(hour, amPm).includes(minute) ||
props.disabledSeconds(hour, minute, amPm).includes(second)
)
})
}

function handleChange(value: StateValueType<T>) {
const newValue = (isArray(value) ? sortRangeValue(value) : value) as StateValueType<T>

if (checkValueDisabled(newValue)) {
return
}

let oldValue = toRaw(accessor.value) as StateValueType<T>
oldValue = (
isArray(oldValue)
Expand Down
2 changes: 1 addition & 1 deletion packages/components/time-picker/src/content/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export default defineComponent({
onUpdated(setInputRef)

const inputProps = useInputProps(context)
const panelProps = usePanelProps(context)
const panelProps = usePanelProps(props, formatRef)

const { activeValue, setActiveValue } = useActiveValue(props, dateConfig, formatRef, panelValue)

Expand Down
16 changes: 16 additions & 0 deletions packages/components/time-picker/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,19 @@ export function sortRangeValue(values: (Date | undefined)[]): (Date | undefined)
return v1.valueOf() - v2.valueOf()
})
}

export function checkHourEnabled(format: string): boolean {
return /[hH]/.test(format)
}
export function checkMinuteEnabled(format: string): boolean {
return /m/.test(format)
}
export function checkSecondEnabled(format: string): boolean {
return /s/.test(format)
}
export function checkUse12Hours(format: string): boolean {
return /[aA]/.test(format)
}
export function checkAmPmCapital(format: string): boolean {
return /A/.test(format)
}

0 comments on commit c5c6f1d

Please sign in to comment.