From c92fbc4a5814e22c669445cafbe026bec1ae2bd5 Mon Sep 17 00:00:00 2001 From: sallerli1 Date: Mon, 26 Sep 2022 20:08:54 +0800 Subject: [PATCH] fix(comp:time-picker): inputs should check disabled time --- .../components/_private/time-panel/index.ts | 1 + .../time-panel/src/composables/useOptions.ts | 26 ++++++-------- .../_private/time-panel/src/types.ts | 14 ++++---- .../_private/time-panel/src/utils.ts | 12 +++---- .../components/time-picker/demo/Disable.vue | 11 ++++-- .../components/time-picker/docs/Api.zh.md | 6 ++-- .../src/composables/usePanelProps.ts | 34 ++++++++----------- .../src/composables/usePickerState.ts | 29 ++++++++++++++-- .../time-picker/src/content/Content.tsx | 2 +- packages/components/time-picker/src/utils.ts | 16 +++++++++ 10 files changed, 92 insertions(+), 59 deletions(-) diff --git a/packages/components/_private/time-panel/index.ts b/packages/components/_private/time-panel/index.ts index 4d1fe378c..954d39409 100644 --- a/packages/components/_private/time-panel/index.ts +++ b/packages/components/_private/time-panel/index.ts @@ -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, diff --git a/packages/components/_private/time-panel/src/composables/useOptions.ts b/packages/components/_private/time-panel/src/composables/useOptions.ts index b05b34bb7..9d9b23d27 100644 --- a/packages/components/_private/time-panel/src/composables/useOptions.ts +++ b/packages/components/_private/time-panel/src/composables/useOptions.ts @@ -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 = () => { @@ -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': @@ -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) @@ -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, diff --git a/packages/components/_private/time-panel/src/types.ts b/packages/components/_private/time-panel/src/types.ts index ac91dd02d..74de285cc 100644 --- a/packages/components/_private/time-panel/src/types.ts +++ b/packages/components/_private/time-panel/src/types.ts @@ -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: () => [], }, @@ -89,7 +87,7 @@ export type TimePanelInstance = InstanceType> // private export interface TimePanelCell { - value: number | string + value: number | 'am' | 'pm' disabled: boolean } diff --git a/packages/components/_private/time-panel/src/utils.ts b/packages/components/_private/time-panel/src/utils.ts index 02c1fb745..d69cb5ba7 100644 --- a/packages/components/_private/time-panel/src/utils.ts +++ b/packages/components/_private/time-panel/src/utils.ts @@ -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' @@ -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) } @@ -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) @@ -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') } } diff --git a/packages/components/time-picker/demo/Disable.vue b/packages/components/time-picker/demo/Disable.vue index 0d0a052ff..0365d661d 100644 --- a/packages/components/time-picker/demo/Disable.vue +++ b/packages/components/time-picker/demo/Disable.vue @@ -4,6 +4,7 @@ diff --git a/packages/components/time-picker/docs/Api.zh.md b/packages/components/time-picker/docs/Api.zh.md index aab97b9b8..f4093cd9a 100644 --- a/packages/components/time-picker/docs/Api.zh.md +++ b/packages/components/time-picker/docs/Api.zh.md @@ -20,9 +20,9 @@ | `clearIcon` | 清除按钮图标 |`string \| #clearIcon` | `close-circle` | ✅ | - | | `clearText` | hover到clearIcon上,显示的title |`string` | clear | ✅ | - | | `size` | 尺寸大小 | `lg \| md \| sm` | `md` | ✅ | - | - | `disabledHours` | 禁用部分小时选项 | `(selectedAmPm: string | undefined) => number[]` | ``() => []`` | - | - | - | `disabledMinutes` | 禁用部分分钟选项 | `(selectedHour: number | undefined, selectedAmPm: string | undefined) => number[]` | `() => []` | - | - | - | `disabledSeconds` | 禁用部分秒选项 | `(selectedHour: number | undefined, selectedMinute: number | undefined, selectedAmPm: string | undefined)=>number[]` | `() => []` | - | - | + | `disabledHours` | 禁用部分小时选项 | `(selectedAmPm: 'am' \| 'pm' \| undefined) => number[]` | ``() => []`` | - | - | + | `disabledMinutes` | 禁用部分分钟选项 | `(selectedHour: number | undefined, selectedAmPm: 'am' \| 'pm' \| undefined) => number[]` | `() => []` | - | - | + | `disabledSeconds` | 禁用部分秒选项 | `(selectedHour: number | undefined, selectedMinute: number \| undefined, selectedAmPm: 'am' \| 'pm' | undefined)=>number[]` | `() => []` | - | - | | `hideDisabledOptions` | 隐藏禁止选择的options |`boolean` |`false` | - | - | | `hourStep` | 小时选项的间隔 | `number` | `1` | - | - | | `minuteStep` | 分钟选项的间隔 | `number` | `1` | - | - | diff --git a/packages/components/time-picker/src/composables/usePanelProps.ts b/packages/components/time-picker/src/composables/usePanelProps.ts index edc9e9600..19c1d5ada 100644 --- a/packages/components/time-picker/src/composables/usePanelProps.ts +++ b/packages/components/time-picker/src/composables/usePanelProps.ts @@ -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 @@ -19,21 +20,14 @@ export interface CommonPanelProps extends ɵBaseTimePanelProps { amPmCapital: boolean } -export function usePanelProps(context: TimePickerContext | TimeRangePickerContext): ComputedRef { +export function usePanelProps( + props: TimePickerProps | TimeRangePickerProps, + formatRef: ComputedRef, +): ComputedRef { 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, @@ -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), } }) } diff --git a/packages/components/time-picker/src/composables/usePickerState.ts b/packages/components/time-picker/src/composables/usePickerState.ts index a7a4f08a5..36f085ee5 100644 --- a/packages/components/time-picker/src/composables/usePickerState.ts +++ b/packages/components/time-picker/src/composables/usePickerState.ts @@ -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 ? Date | undefined @@ -40,8 +41,32 @@ export function usePickerState const [isFocused, setFocused] = useState(false) + const checkValueDisabled = (value: StateValueType) => { + 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) { const newValue = (isArray(value) ? sortRangeValue(value) : value) as StateValueType + + if (checkValueDisabled(newValue)) { + return + } + let oldValue = toRaw(accessor.value) as StateValueType oldValue = ( isArray(oldValue) diff --git a/packages/components/time-picker/src/content/Content.tsx b/packages/components/time-picker/src/content/Content.tsx index 026035ea4..ae5aef63f 100644 --- a/packages/components/time-picker/src/content/Content.tsx +++ b/packages/components/time-picker/src/content/Content.tsx @@ -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) diff --git a/packages/components/time-picker/src/utils.ts b/packages/components/time-picker/src/utils.ts index 0d8d5de9c..bf2b6c31d 100644 --- a/packages/components/time-picker/src/utils.ts +++ b/packages/components/time-picker/src/utils.ts @@ -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) +}