refactor: date

pull/4499/head
tangjinzhou 2021-06-15 22:29:10 +08:00
parent a020c2f681
commit a8113d7c55
5 changed files with 265 additions and 237 deletions

View File

@ -381,7 +381,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
<PickerPanel<DateType> <PickerPanel<DateType>
{...panelProps} {...panelProps}
generateConfig={generateConfig} generateConfig={generateConfig}
className={classNames({ class={classNames({
[`${prefixCls}-panel-focused`]: !typing, [`${prefixCls}-panel-focused`]: !typing,
})} })}
value={selectedValue} value={selectedValue}
@ -406,8 +406,8 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
const panel = ( const panel = (
<div <div
className={`${prefixCls}-panel-container`} class={`${prefixCls}-panel-container`}
onMouseDown={(e) => { onMousedown={(e) => {
e.preventDefault(); e.preventDefault();
}} }}
> >
@ -417,27 +417,27 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
let suffixNode: React.ReactNode; let suffixNode: React.ReactNode;
if (suffixIcon) { if (suffixIcon) {
suffixNode = <span className={`${prefixCls}-suffix`}>{suffixIcon}</span>; suffixNode = <span class={`${prefixCls}-suffix`}>{suffixIcon}</span>;
} }
let clearNode: React.ReactNode; let clearNode: React.ReactNode;
if (allowClear && mergedValue && !disabled) { if (allowClear && mergedValue && !disabled) {
clearNode = ( clearNode = (
<span <span
onMouseDown={(e) => { onMousedown={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
}} }}
onMouseUp={(e) => { onMouseup={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
triggerChange(null); triggerChange(null);
triggerOpen(false); triggerOpen(false);
}} }}
className={`${prefixCls}-clear`} class={`${prefixCls}-clear`}
role="button" role="button"
> >
{clearIcon || <span className={`${prefixCls}-clear-btn`} />} {clearIcon || <span class={`${prefixCls}-clear-btn`} />}
</span> </span>
); );
} }
@ -486,21 +486,21 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
direction={direction} direction={direction}
> >
<div <div
className={classNames(prefixCls, className, { class={classNames(prefixCls, className, {
[`${prefixCls}-disabled`]: disabled, [`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-focused`]: focused, [`${prefixCls}-focused`]: focused,
[`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-rtl`]: direction === 'rtl',
})} })}
style={style} style={style}
onMouseDown={onMouseDown} onMousedown={onMouseDown}
onMouseUp={onInternalMouseUp} onMouseup={onInternalMouseUp}
onMouseEnter={onMouseEnter} onMouseenter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseleave={onMouseLeave}
onContextMenu={onContextMenu} onContextmenu={onContextMenu}
onClick={onClick} onClick={onClick}
> >
<div <div
className={classNames(`${prefixCls}-input`, { class={classNames(`${prefixCls}-input`, {
[`${prefixCls}-input-placeholder`]: !!hoverValue, [`${prefixCls}-input-placeholder`]: !!hoverValue,
})} })}
ref={inputDivRef} ref={inputDivRef}
@ -509,19 +509,19 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
id={id} id={id}
tabIndex={tabIndex} tabIndex={tabIndex}
disabled={disabled} disabled={disabled}
readOnly={inputReadOnly || typeof formatList[0] === 'function' || !typing} readonly={inputReadOnly || typeof formatList[0] === 'function' || !typing}
value={hoverValue || text} value={hoverValue || text}
onChange={(e) => { onChange={(e) => {
triggerTextChange(e.target.value); triggerTextChange(e.target.value);
}} }}
autoFocus={autoFocus} autofocus={autoFocus}
placeholder={placeholder} placeholder={placeholder}
ref={inputRef} ref={inputRef}
title={text} title={text}
{...inputProps} {...inputProps}
size={getInputSize(picker, formatList[0], generateConfig)} size={getInputSize(picker, formatList[0], generateConfig)}
{...getDataOrAriaProps(props)} {...getDataOrAriaProps(props)}
autoComplete={autoComplete} autocomplete={autoComplete}
/> />
{suffixNode} {suffixNode}
{clearNode} {clearNode}

View File

@ -515,7 +515,7 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
const disabled = disabledDate && disabledDate(now); const disabled = disabledDate && disabledDate(now);
todayNode = ( todayNode = (
<a <a
className={classNames(todayCls, disabled && `${todayCls}-disabled`)} class={classNames(todayCls, disabled && `${todayCls}-disabled`)}
aria-disabled={disabled} aria-disabled={disabled}
onClick={() => { onClick={() => {
if (!disabled) { if (!disabled) {
@ -539,22 +539,22 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
}} }}
> >
<div <div
tabIndex={tabIndex} tabindex={tabIndex}
className={classNames(`${prefixCls}-panel`, className, { class={classNames(`${prefixCls}-panel`, className, {
[`${prefixCls}-panel-has-range`]: rangedValue && rangedValue[0] && rangedValue[1], [`${prefixCls}-panel-has-range`]: rangedValue && rangedValue[0] && rangedValue[1],
[`${prefixCls}-panel-has-range-hover`]: [`${prefixCls}-panel-has-range-hover`]:
hoverRangedValue && hoverRangedValue[0] && hoverRangedValue[1], hoverRangedValue && hoverRangedValue[0] && hoverRangedValue[1],
[`${prefixCls}-panel-rtl`]: direction === 'rtl', [`${prefixCls}-panel-rtl`]: direction === 'rtl',
})} })}
style={style} style={style}
onKeyDown={onInternalKeyDown} onKeydown={onInternalKeyDown}
onBlur={onInternalBlur} onBlur={onInternalBlur}
onMouseDown={onMouseDown} onMousedown={onMouseDown}
ref={panelDivRef} ref={panelDivRef}
> >
{panelNode} {panelNode}
{extraFooter || rangesNode || todayNode ? ( {extraFooter || rangesNode || todayNode ? (
<div className={`${prefixCls}-footer`}> <div class={`${prefixCls}-footer`}>
{extraFooter} {extraFooter}
{rangesNode} {rangesNode}
{todayNode} {todayNode}

View File

@ -1,7 +1,8 @@
import * as React from 'react'; import { CSSProperties } from '@vue/runtime-dom';
import classNames from 'classnames'; import { AlignType } from '../vc-align/interface';
import Trigger from 'rc-trigger'; import Trigger from '../vc-trigger';
import type { AlignType } from 'rc-trigger/lib/interface'; import classNames from '../_util/classNames';
import { VueNode } from '../_util/type';
const BUILT_IN_PLACEMENTS = { const BUILT_IN_PLACEMENTS = {
bottomLeft: { bottomLeft: {
@ -43,9 +44,9 @@ type Placement = 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight';
export type PickerTriggerProps = { export type PickerTriggerProps = {
prefixCls: string; prefixCls: string;
visible: boolean; visible: boolean;
popupElement: React.ReactElement; popupElement: VueNode;
popupStyle?: React.CSSProperties; popupStyle?: CSSProperties;
children: React.ReactElement; children: VueNode;
dropdownClassName?: string; dropdownClassName?: string;
transitionName?: string; transitionName?: string;
getPopupContainer?: (node: HTMLElement) => HTMLElement; getPopupContainer?: (node: HTMLElement) => HTMLElement;
@ -55,7 +56,8 @@ export type PickerTriggerProps = {
direction?: 'ltr' | 'rtl'; direction?: 'ltr' | 'rtl';
}; };
function PickerTrigger({ function PickerTrigger(
{
prefixCls, prefixCls,
popupElement, popupElement,
popupStyle, popupStyle,
@ -64,11 +66,12 @@ function PickerTrigger({
dropdownAlign, dropdownAlign,
transitionName, transitionName,
getPopupContainer, getPopupContainer,
children,
range, range,
popupPlacement, popupPlacement,
direction, direction,
}: PickerTriggerProps) { }: PickerTriggerProps,
{ slots },
) {
const dropdownPrefixCls = `${prefixCls}-dropdown`; const dropdownPrefixCls = `${prefixCls}-dropdown`;
const getPopupPlacement = () => { const getPopupPlacement = () => {
@ -96,7 +99,7 @@ function PickerTrigger({
popupStyle={popupStyle} popupStyle={popupStyle}
getPopupContainer={getPopupContainer} getPopupContainer={getPopupContainer}
> >
{children} {slots.default?.()}
</Trigger> </Trigger>
); );
} }

View File

@ -811,7 +811,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
} }
return false; return false;
}} }}
className={classNames({ class={classNames({
[`${prefixCls}-panel-focused`]: [`${prefixCls}-panel-focused`]:
mergedActivePickerIndex === 0 ? !startTyping : !endTyping, mergedActivePickerIndex === 0 ? !startTyping : !endTyping,
})} })}
@ -938,9 +938,9 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
let mergedNodes: React.ReactNode = ( let mergedNodes: React.ReactNode = (
<> <>
<div className={`${prefixCls}-panels`}>{panels}</div> <div class={`${prefixCls}-panels`}>{panels}</div>
{(extraNode || rangesNode) && ( {(extraNode || rangesNode) && (
<div className={`${prefixCls}-footer`}> <div class={`${prefixCls}-footer`}>
{extraNode} {extraNode}
{rangesNode} {rangesNode}
</div> </div>
@ -954,7 +954,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
return ( return (
<div <div
className={`${prefixCls}-panel-container`} class={`${prefixCls}-panel-container`}
style={{ marginLeft: panelLeft }} style={{ marginLeft: panelLeft }}
ref={panelDivRef} ref={panelDivRef}
onMouseDown={e => { onMouseDown={e => {
@ -968,10 +968,10 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
const rangePanel = ( const rangePanel = (
<div <div
className={classNames(`${prefixCls}-range-wrapper`, `${prefixCls}-${picker}-range-wrapper`)} class={classNames(`${prefixCls}-range-wrapper`, `${prefixCls}-${picker}-range-wrapper`)}
style={{ minWidth: popupMinWidth }} style={{ minWidth: popupMinWidth }}
> >
<div className={`${prefixCls}-range-arrow`} style={arrowPositionStyle} /> <div class={`${prefixCls}-range-arrow`} style={arrowPositionStyle} />
{renderPanels()} {renderPanels()}
</div> </div>
@ -980,7 +980,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
// ============================= Icons ============================= // ============================= Icons =============================
let suffixNode: React.ReactNode; let suffixNode: React.ReactNode;
if (suffixIcon) { if (suffixIcon) {
suffixNode = <span className={`${prefixCls}-suffix`}>{suffixIcon}</span>; suffixNode = <span class={`${prefixCls}-suffix`}>{suffixIcon}</span>;
} }
let clearNode: React.ReactNode; let clearNode: React.ReactNode;
@ -1010,9 +1010,9 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
triggerChange(values, null); triggerChange(values, null);
triggerOpen(false, mergedActivePickerIndex); triggerOpen(false, mergedActivePickerIndex);
}} }}
className={`${prefixCls}-clear`} class={`${prefixCls}-clear`}
> >
{clearIcon || <span className={`${prefixCls}-clear-btn`} />} {clearIcon || <span class={`${prefixCls}-clear-btn`} />}
</span> </span>
); );
} }
@ -1077,7 +1077,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
> >
<div <div
ref={containerRef} ref={containerRef}
className={classNames(prefixCls, `${prefixCls}-range`, className, { class={classNames(prefixCls, `${prefixCls}-range`, className, {
[`${prefixCls}-disabled`]: mergedDisabled[0] && mergedDisabled[1], [`${prefixCls}-disabled`]: mergedDisabled[0] && mergedDisabled[1],
[`${prefixCls}-focused`]: mergedActivePickerIndex === 0 ? startFocused : endFocused, [`${prefixCls}-focused`]: mergedActivePickerIndex === 0 ? startFocused : endFocused,
[`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-rtl`]: direction === 'rtl',
@ -1090,7 +1090,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
{...getDataOrAriaProps(props)} {...getDataOrAriaProps(props)}
> >
<div <div
className={classNames(`${prefixCls}-input`, { class={classNames(`${prefixCls}-input`, {
[`${prefixCls}-input-active`]: mergedActivePickerIndex === 0, [`${prefixCls}-input-active`]: mergedActivePickerIndex === 0,
[`${prefixCls}-input-placeholder`]: !!startHoverValue, [`${prefixCls}-input-placeholder`]: !!startHoverValue,
})} })}
@ -1112,11 +1112,11 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
autoComplete={autoComplete} autoComplete={autoComplete}
/> />
</div> </div>
<div className={`${prefixCls}-range-separator`} ref={separatorRef}> <div class={`${prefixCls}-range-separator`} ref={separatorRef}>
{separator} {separator}
</div> </div>
<div <div
className={classNames(`${prefixCls}-input`, { class={classNames(`${prefixCls}-input`, {
[`${prefixCls}-input-active`]: mergedActivePickerIndex === 1, [`${prefixCls}-input-active`]: mergedActivePickerIndex === 1,
[`${prefixCls}-input-placeholder`]: !!endHoverValue, [`${prefixCls}-input-placeholder`]: !!endHoverValue,
})} })}
@ -1137,7 +1137,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
/> />
</div> </div>
<div <div
className={`${prefixCls}-active-bar`} class={`${prefixCls}-active-bar`}
style={{ style={{
...activeBarPositionStyle, ...activeBarPositionStyle,
width: activeBarWidth, width: activeBarWidth,

View File

@ -8,7 +8,8 @@ import type { SharedTimeProps } from '.';
import { setTime as utilSetTime } from '../../utils/timeUtil'; import { setTime as utilSetTime } from '../../utils/timeUtil';
import { cloneElement } from '../../../_util/vnode'; import { cloneElement } from '../../../_util/vnode';
import { VueNode } from '../../../_util/type'; import { VueNode } from '../../../_util/type';
import { Ref } from '@vue/reactivity'; import { ref, Ref } from '@vue/reactivity';
import { computed, defineComponent, watchEffect } from '@vue/runtime-core';
function shouldUnitsUpdate(prevUnits: Unit[], nextUnits: Unit[]) { function shouldUnitsUpdate(prevUnits: Unit[], nextUnits: Unit[]) {
if (prevUnits.length !== nextUnits.length) return true; if (prevUnits.length !== nextUnits.length) return true;
@ -50,23 +51,128 @@ export type TimeBodyProps<DateType> = {
operationRef: Ref<BodyOperationRef | undefined>; operationRef: Ref<BodyOperationRef | undefined>;
} & SharedTimeProps<DateType>; } & SharedTimeProps<DateType>;
function TimeBody<DateType>(props: TimeBodyProps<DateType>) {
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 { const {
generateConfig,
prefixCls, prefixCls,
operationRef, operationRef,
activeColumnIndex, activeColumnIndex,
value,
showHour, showHour,
showMinute, showMinute,
showSecond, showSecond,
use12Hours, use12Hours,
hourStep = 1,
minuteStep = 1,
secondStep = 1,
disabledHours,
disabledMinutes,
disabledSeconds,
hideDisabledOptions, hideDisabledOptions,
onSelect, onSelect,
} = props; } = props;
@ -80,86 +186,6 @@ function TimeBody<DateType>(props: TimeBodyProps<DateType>) {
const contentPrefixCls = `${prefixCls}-content`; const contentPrefixCls = `${prefixCls}-content`;
const columnPrefixCls = `${prefixCls}-time-panel`; const columnPrefixCls = `${prefixCls}-time-panel`;
let isPM: boolean | undefined;
const originHour = value ? generateConfig.getHour(value) : -1;
let hour = originHour;
const minute = value ? generateConfig.getMinute(value) : -1;
const second = value ? generateConfig.getSecond(value) : -1;
const setTime = (
isNewPM: boolean | undefined,
newHour: number,
newMinute: number,
newSecond: number,
) => {
let newDate = value || generateConfig.getNow();
const mergedHour = Math.max(0, newHour);
const mergedMinute = Math.max(0, newMinute);
const mergedSecond = Math.max(0, newSecond);
newDate = utilSetTime(
generateConfig,
newDate,
!use12Hours || !isNewPM ? mergedHour : mergedHour + 12,
mergedMinute,
mergedSecond,
);
return newDate;
};
// ========================= Unit =========================
const rawHours = generateUnits(0, 23, hourStep, disabledHours && disabledHours());
const memorizedRawHours = useMemo(() => rawHours, rawHours, shouldUnitsUpdate);
// Should additional logic to handle 12 hours
if (use12Hours) {
isPM = hour >= 12; // -1 means should display AM
hour %= 12;
}
const [AMDisabled, PMDisabled] = React.useMemo(() => {
if (!use12Hours) {
return [false, false];
}
const AMPMDisabled = [true, true];
memorizedRawHours.forEach(({ disabled, value: hourValue }) => {
if (disabled) return;
if (hourValue >= 12) {
AMPMDisabled[1] = false;
} else {
AMPMDisabled[0] = false;
}
});
return AMPMDisabled;
}, [use12Hours, memorizedRawHours]);
const hours = React.useMemo(() => {
if (!use12Hours) return memorizedRawHours;
return memorizedRawHours
.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,
};
});
}, [use12Hours, isPM, memorizedRawHours]);
const minutes = generateUnits(0, 59, minuteStep, disabledMinutes && disabledMinutes(originHour));
const seconds = generateUnits(
0,
59,
secondStep,
disabledSeconds && disabledSeconds(originHour, minute),
);
// ====================== Operations ====================== // ====================== Operations ======================
operationRef.value = { operationRef.value = {
onUpDown: diff => { onUpDown: diff => {
@ -206,18 +232,18 @@ function TimeBody<DateType>(props: TimeBodyProps<DateType>) {
} }
// Hour // Hour
addColumnNode(showHour, <TimeUnitColumn key="hour" />, hour, hours, num => { addColumnNode(showHour, <TimeUnitColumn key="hour" />, hour.value, hours.value, num => {
onSelect(setTime(isPM, num, minute, second), 'mouse'); onSelect(setTime(isPM.value, num, minute.value, second.value), 'mouse');
}); });
// Minute // Minute
addColumnNode(showMinute, <TimeUnitColumn key="minute" />, minute, minutes, num => { addColumnNode(showMinute, <TimeUnitColumn key="minute" />, minute.value, minutes.value, num => {
onSelect(setTime(isPM, hour, num, second), 'mouse'); onSelect(setTime(isPM.value, hour.value, num, second.value), 'mouse');
}); });
// Second // Second
addColumnNode(showSecond, <TimeUnitColumn key="second" />, second, seconds, num => { addColumnNode(showSecond, <TimeUnitColumn key="second" />, second.value, seconds.value, num => {
onSelect(setTime(isPM, hour, minute, num), 'mouse'); onSelect(setTime(isPM.value, hour.value, minute.value, num), 'mouse');
}); });
// 12 Hours // 12 Hours
@ -231,19 +257,18 @@ function TimeBody<DateType>(props: TimeBodyProps<DateType>) {
<TimeUnitColumn key="12hours" />, <TimeUnitColumn key="12hours" />,
PMIndex, PMIndex,
[ [
{ label: 'AM', value: 0, disabled: AMDisabled }, { label: 'AM', value: 0, disabled: AMPMDisabled.value[0] },
{ label: 'PM', value: 1, disabled: PMDisabled }, { label: 'PM', value: 1, disabled: AMPMDisabled.value[1] },
], ],
num => { num => {
onSelect(setTime(!!num, hour, minute, second), 'mouse'); onSelect(setTime(!!num, hour.value, minute.value, second.value), 'mouse');
}, },
); );
return <div class={contentPrefixCls}>{columns.map(({ node }) => node)}</div>; return <div class={contentPrefixCls}>{columns.map(({ node }) => node)}</div>;
} }
}
})
TimeBody.displayName ='TimeBody'
TimeBody.inheritAttrs = false;
export default TimeBody; export default TimeBody;