import type { GenerateConfig } from '../../generate'; import type { Locale, OnSelect } from '../../interface'; import type { Unit } from './TimeUnitColumn'; import TimeUnitColumn from './TimeUnitColumn'; import { leftPad } from '../../utils/miscUtil'; import type { SharedTimeProps } from '.'; import { setTime as utilSetTime } from '../../utils/timeUtil'; import { cloneElement } from '../../../_util/vnode'; import { VueNode } from '../../../_util/type'; import { ref, Ref } from '@vue/reactivity'; import { computed, defineComponent, watchEffect } from '@vue/runtime-core'; function shouldUnitsUpdate(prevUnits: Unit[], nextUnits: Unit[]) { if (prevUnits.length !== nextUnits.length) return true; // if any unit's disabled status is different, the units should be re-evaluted for (let i = 0; i < prevUnits.length; i += 1) { if (prevUnits[i].disabled !== nextUnits[i].disabled) return true; } return false; } function generateUnits( start: number, end: number, step: number, disabledUnits: number[] | undefined, ) { const units: Unit[] = []; for (let i = start; i <= end; i += step) { units.push({ label: leftPad(i, 2), value: i, disabled: (disabledUnits || []).includes(i), }); } return units; } export type BodyOperationRef = { onUpDown: (diff: number) => void; }; export type TimeBodyProps = { prefixCls: string; locale: Locale; generateConfig: GenerateConfig; value?: DateType | null; onSelect: OnSelect; activeColumnIndex: number; operationRef: Ref; } & SharedTimeProps; const TimeBody = defineComponent({ name: 'TimeBody', inheritAttrs: false, props: [ 'generateConfig', 'prefixCls', 'operationRef', 'activeColumnIndex', 'value', 'showHour', 'showMinute', 'showSecond', 'use12Hours', 'hourStep', 'minuteStep', 'secondStep', 'disabledHours', 'disabledMinutes', 'disabledSeconds', 'hideDisabledOptions', 'onSelect', ], setup(props) { const originHour = computed(() => props.value ? props.generateConfig.getHour(props.value) : -1); const isPM = computed(()=> { if (props.use12Hours) { return originHour.value >= 12; // -1 means should display AM } else { return false } }) let hour = computed(()=> { // Should additional logic to handle 12 hours if (props.use12Hours) { return originHour.value % 12 } else { return originHour.value } }); const minute = computed(()=> props.value ? props.generateConfig.getMinute(props.value) : -1); const second = computed(()=> props.value ? props.generateConfig.getSecond(props.value) : -1); const setTime = ( isNewPM: boolean | undefined, newHour: number, newMinute: number, newSecond: number, ) => { let newDate = props.value || props.generateConfig.getNow(); const mergedHour = Math.max(0, newHour); const mergedMinute = Math.max(0, newMinute); const mergedSecond = Math.max(0, newSecond); newDate = utilSetTime( props.generateConfig, newDate, !props.use12Hours || !isNewPM ? mergedHour : mergedHour + 12, mergedMinute, mergedSecond, ); return newDate; }; // ========================= Unit ========================= const rawHours = computed(()=> generateUnits(0, 23, props.hourStep ?? 1, props.disabledHours && props.disabledHours())); // const memorizedRawHours = useMemo(() => rawHours, rawHours, shouldUnitsUpdate); const AMPMDisabled = computed(() => { if (!props.use12Hours) { return [false, false]; } const AMPMDisabled = [true, true]; rawHours.value.forEach(({ disabled, value: hourValue }) => { if (disabled) return; if (hourValue >= 12) { AMPMDisabled[1] = false; } else { AMPMDisabled[0] = false; } }); return AMPMDisabled; }); const hours = computed(() => { if (!props.use12Hours) return rawHours.value; return rawHours.value .filter(isPM ? hourMeta => hourMeta.value >= 12 : hourMeta => hourMeta.value < 12) .map(hourMeta => { const hourValue = hourMeta.value % 12; const hourLabel = hourValue === 0 ? '12' : leftPad(hourValue, 2); return { ...hourMeta, label: hourLabel, value: hourValue, }; }); }); const minutes = computed(()=> generateUnits(0, 59, props.minuteStep ?? 1, props.disabledMinutes && props.disabledMinutes(originHour.value))); const seconds = computed(()=> generateUnits( 0, 59, props.secondStep ?? 1, props.disabledSeconds && props.disabledSeconds(originHour.value, minute), )); return ()=> { const { prefixCls, operationRef, activeColumnIndex, showHour, showMinute, showSecond, use12Hours, hideDisabledOptions, onSelect, } = props; const columns: { node: VueNode; value: number; units: Unit[]; onSelect: (diff: number) => void; }[] = []; const contentPrefixCls = `${prefixCls}-content`; const columnPrefixCls = `${prefixCls}-time-panel`; // ====================== Operations ====================== operationRef.value = { onUpDown: diff => { const column = columns[activeColumnIndex]; if (column) { const valueIndex = column.units.findIndex(unit => unit.value === column.value); const unitLen = column.units.length; for (let i = 1; i < unitLen; i += 1) { const nextUnit = column.units[(valueIndex + diff * i + unitLen) % unitLen]; if (nextUnit.disabled !== true) { column.onSelect(nextUnit.value); break; } } } }, }; // ======================== Render ======================== function addColumnNode( condition: boolean | undefined, node: VueNode, columnValue: number, units: Unit[], onColumnSelect: (diff: number) => void, ) { if (condition !== false) { columns.push({ node: cloneElement(node, { prefixCls: columnPrefixCls, value: columnValue, active: activeColumnIndex === columns.length, onSelect: onColumnSelect, units, hideDisabledOptions, }), onSelect: onColumnSelect, value: columnValue, units, }); } } // Hour addColumnNode(showHour, , hour.value, hours.value, num => { onSelect(setTime(isPM.value, num, minute.value, second.value), 'mouse'); }); // Minute addColumnNode(showMinute, , minute.value, minutes.value, num => { onSelect(setTime(isPM.value, hour.value, num, second.value), 'mouse'); }); // Second addColumnNode(showSecond, , second.value, seconds.value, num => { onSelect(setTime(isPM.value, hour.value, minute.value, num), 'mouse'); }); // 12 Hours let PMIndex = -1; if (typeof isPM === 'boolean') { PMIndex = isPM ? 1 : 0; } addColumnNode( use12Hours === true, , PMIndex, [ { label: 'AM', value: 0, disabled: AMPMDisabled.value[0] }, { label: 'PM', value: 1, disabled: AMPMDisabled.value[1] }, ], num => { onSelect(setTime(!!num, hour.value, minute.value, second.value), 'mouse'); }, ); return
{columns.map(({ node }) => node)}
; } } }) export default TimeBody;