Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(comp:time-picker): inputs should check disabled time #1164

Merged
merged 1 commit into from
Sep 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
6 changes: 3 additions & 3 deletions packages/components/time-picker/docs/Api.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` | - | - |
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)
}