Skip to content

Commit 132cecc

Browse files
committed
feat(ui): optimize date-picker
- Adjust popup window closing logic - Show animation only click cell of time-picker - Adjust format of `A` when dayjs's locale is `zh-cn`
1 parent e10d922 commit 132cecc

7 files changed

Lines changed: 61 additions & 33 deletions

File tree

packages/ui/src/components/_date-input/DateInput.tsx

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export interface DDateInputProps extends Omit<React.HTMLAttributes<HTMLDivElemen
4242
dFormControl: DFormControl | undefined;
4343
dModel: Date | null | [Date, Date] | undefined;
4444
dFormat: string;
45-
dVisible: boolean | undefined;
45+
dVisible: boolean;
4646
dPlacement: 'top' | 'top-left' | 'top-right' | 'bottom' | 'bottom-left' | 'bottom-right';
4747
dOrder: (date: [Date, Date]) => boolean;
4848
dPlaceholder: [string, string];
@@ -55,7 +55,7 @@ export interface DDateInputProps extends Omit<React.HTMLAttributes<HTMLDivElemen
5555
| [DCloneHTMLElement<React.InputHTMLAttributes<HTMLInputElement>>?, DCloneHTMLElement<React.InputHTMLAttributes<HTMLInputElement>>?]
5656
| undefined;
5757
onModelChange: ((date: any) => void) | undefined;
58-
onVisibleChange: ((visible: boolean) => void) | undefined;
58+
onVisibleChange: (visible: boolean) => void;
5959
onUpdatePanel: ((date: Date) => void) | undefined;
6060
afterVisibleChange: ((visible: boolean) => void) | undefined;
6161
onClear: (() => void) | undefined;
@@ -129,8 +129,6 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
129129
const [t] = useTranslation();
130130
const forceUpdate = useForceUpdate();
131131

132-
const [visible, changeVisible] = useDValue<boolean>(false, dVisible, onVisibleChange);
133-
134132
const formControlInject = useFormControl(dFormControl);
135133
const [_value, _changeValue] = useDValue<Date | null | [Date, Date]>(
136134
null,
@@ -159,7 +157,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
159157
});
160158
} else {
161159
dataRef.current.clearTid = async.setTimeout(() => {
162-
changeVisible(false);
160+
onVisibleChange(false);
163161
setIsFocus([false, false]);
164162
}, 20);
165163
}
@@ -202,17 +200,17 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
202200
forceUpdate();
203201
};
204202

205-
const clearable = dClearable && !isNull(_value) && !visible && !dDisabled;
203+
const clearable = dClearable && !isNull(_value) && !dVisible && !dDisabled;
206204

207-
const maxZIndex = useMaxIndex(visible);
205+
const maxZIndex = useMaxIndex(dVisible);
208206

209207
const [popupPositionStyle, setPopupPositionStyle] = useState<React.CSSProperties>({
210208
top: '-200vh',
211209
left: '-200vw',
212210
});
213211
const [transformOrigin, setTransformOrigin] = useState<string>();
214212
const updatePosition = useEventCallback(() => {
215-
if (visible && boxRef.current && popupRef.current) {
213+
if (dVisible && boxRef.current && popupRef.current) {
216214
const height = popupRef.current.offsetHeight;
217215
const maxWidth = window.innerWidth - WINDOW_SPACE * 2;
218216
const width = Math.min(popupRef.current.scrollWidth, maxWidth);
@@ -233,12 +231,12 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
233231
}
234232
});
235233

236-
const globalScroll = useGlobalScroll(updatePosition, !visible);
237-
useEvent(dPageScrollRef, 'scroll', updatePosition, { passive: true }, !visible || globalScroll);
234+
const globalScroll = useGlobalScroll(updatePosition, !dVisible);
235+
useEvent(dPageScrollRef, 'scroll', updatePosition, { passive: true }, !dVisible || globalScroll);
238236

239-
useResize(boxRef, updatePosition, !visible);
240-
useResize(popupRef, updatePosition, !visible);
241-
useResize(dContentResizeRef, updatePosition, !visible);
237+
useResize(boxRef, updatePosition, !dVisible);
238+
useResize(popupRef, updatePosition, !dVisible);
239+
useResize(dContentResizeRef, updatePosition, !dVisible);
242240

243241
useEffect(() => {
244242
if (boxRef.current && indicatorRef.current) {
@@ -290,10 +288,10 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
290288

291289
const getInputNode = (isLeft: boolean) => (
292290
<DComboboxKeyboard
293-
dVisible={visible}
291+
dVisible={dVisible}
294292
dEditable
295293
dHasSub={false}
296-
onVisibleChange={changeVisible}
294+
onVisibleChange={onVisibleChange}
297295
onFocusChange={() => {
298296
// Only for popup open/close
299297
}}
@@ -331,10 +329,10 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
331329
if (isNull(isLeft ? valueRight : valueLeft)) {
332330
dataRef.current.focusAnother = true;
333331
} else {
334-
changeVisible(false);
332+
onVisibleChange(false);
335333
}
336334
} else {
337-
changeVisible(false);
335+
onVisibleChange(false);
338336
}
339337
} else {
340338
dataRef.current.inputValue[index] = isNull(value) ? '' : dayjs(value).format(dFormat);
@@ -378,7 +376,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
378376
renderBaseDesign(
379377
<div
380378
{...restProps}
381-
{...{ [ESC_CLOSABLE_DATA]: visible }}
379+
{...{ [ESC_CLOSABLE_DATA]: dVisible }}
382380
ref={boxRef}
383381
className={getClassName(restProps.className, prefix, {
384382
[`${prefix}--${dSize}`]: dSize,
@@ -398,7 +396,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
398396
onClick={(e) => {
399397
restProps.onClick?.(e);
400398

401-
changeVisible(true);
399+
onVisibleChange(true);
402400
if (!hasFocus) {
403401
inputLeftRef.current?.focus({ preventScroll: true });
404402
}
@@ -439,7 +437,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
439437
{containerRef.current &&
440438
ReactDOM.createPortal(
441439
<DTransition
442-
dIn={visible}
440+
dIn={dVisible}
443441
dDuring={TTANSITION_DURING_POPUP}
444442
onEnter={updatePosition}
445443
afterEnter={() => {

packages/ui/src/components/date-picker/DatePicker.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import React, { useRef } from 'react';
1010
import { CalendarOutlined } from '@react-devui/icons';
1111
import { getClassName } from '@react-devui/utils';
1212

13-
import { useGeneralContext } from '../../hooks';
13+
import { useDValue, useGeneralContext } from '../../hooks';
1414
import { registerComponentMate } from '../../utils';
1515
import { DDateInput } from '../_date-input';
1616
import { getCols, orderDate } from '../_date-input/utils';
@@ -90,6 +90,8 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
9090

9191
const [t] = useTranslation();
9292

93+
const [visible, changeVisible] = useDValue<boolean>(false, dVisible, onVisibleChange);
94+
9395
const size = dSize ?? gSize;
9496
const disabled = (dDisabled || gDisabled || dFormControl?.control.disabled) ?? false;
9597

@@ -118,7 +120,7 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
118120
dFormControl={dFormControl}
119121
dModel={dModel}
120122
dFormat={format}
121-
dVisible={dVisible}
123+
dVisible={visible}
122124
dPlacement={dPlacement}
123125
dOrder={(date) => orderDate(date, dOrder, dShowTime ? undefined : 'date')}
124126
dPlaceholder={[placeholderLeft, placeholderRight]}
@@ -129,7 +131,7 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
129131
dDisabled={disabled}
130132
dInputRender={dInputRender}
131133
onModelChange={onModelChange}
132-
onVisibleChange={onVisibleChange}
134+
onVisibleChange={changeVisible}
133135
onUpdatePanel={(date) => {
134136
updatePanelRef.current?.(date);
135137
updateTimePickerPanelRef.current?.(date);
@@ -148,7 +150,13 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
148150
dDateCurrentSelected={date[index]}
149151
dDateAnotherSelected={date[isFocus[0] ? 1 : 0]}
150152
dConfigDate={dConfigDate ? (...args) => dConfigDate(...args, position, date) : undefined}
151-
onDateChange={changeDate}
153+
onDateChange={(date) => {
154+
changeDate(date);
155+
156+
if (!dShowTime) {
157+
changeVisible(false);
158+
}
159+
}}
152160
></DPanel>
153161
{dShowTime &&
154162
React.cloneElement<DTimePickerPanelPrivateProps>(
@@ -176,6 +184,8 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
176184
changeDate(d);
177185
updatePanelRef.current?.(d[index]);
178186
updateTimePickerPanelRef.current?.(d[index]);
187+
188+
changeVisible(false);
179189
};
180190

181191
return (
@@ -197,6 +207,8 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
197207
changeDate(now);
198208
updatePanelRef.current?.(now);
199209
updateTimePickerPanelRef.current?.(now);
210+
211+
changeVisible(false);
200212
}}
201213
dType="link"
202214
>

packages/ui/src/components/date-picker/Panel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface DPanelProps {
1414
dDateCurrentSelected: Date | null;
1515
dDateAnotherSelected: Date | null;
1616
dConfigDate: ((date: Date) => { disabled?: boolean }) | undefined;
17-
onDateChange: (time: Date) => void;
17+
onDateChange: (date: Date) => void;
1818
}
1919

2020
function Panel(props: DPanelProps, ref: React.ForwardedRef<(date: Date) => void>): JSX.Element | null {

packages/ui/src/components/root/Root.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ export function DRoot(props: DRootProps): JSX.Element | null {
7373

7474
case 'zh-CN':
7575
dayjs.locale('zh-cn');
76+
dayjs.updateLocale('zh-cn', {
77+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
78+
meridiem: (hour: number, minute: number, isLowercase: number) => {
79+
return hour > 12 ? 'PM' : 'AM';
80+
},
81+
});
7682
break;
7783

7884
default:

packages/ui/src/components/time-picker/Panel.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function Panel(props: DPanelProps, ref: React.ForwardedRef<(date: Date) => void>
6262
return unit.join(':');
6363
})();
6464

65-
const updateView = useEventCallback((t: Date, unit?: 'hour' | 'minute' | 'second') => {
65+
const updateView = useEventCallback((t: Date, unit?: 'hour' | 'minute' | 'second', behavior: 'smooth' | 'instant' = 'smooth') => {
6666
if (unit === 'hour' || isUndefined(unit)) {
6767
let hour = t.getHours();
6868
if (d12Hour) {
@@ -78,7 +78,7 @@ function Panel(props: DPanelProps, ref: React.ForwardedRef<(date: Date) => void>
7878

7979
dataRef.current.clearHTid = scrollTo(ulHRef.current, {
8080
top: Array.prototype.indexOf.call(ulHRef.current.children, ulHRef.current.querySelector(`[data-h="${hour}"]`)) * 28,
81-
behavior: 'smooth',
81+
behavior,
8282
});
8383
}
8484
}
@@ -89,7 +89,7 @@ function Panel(props: DPanelProps, ref: React.ForwardedRef<(date: Date) => void>
8989
dataRef.current.clearMTid?.();
9090
dataRef.current.clearMTid = scrollTo(ulMRef.current, {
9191
top: Array.prototype.indexOf.call(ulMRef.current.children, ulMRef.current.querySelector(`[data-m="${minute}"]`)) * 28,
92-
behavior: 'smooth',
92+
behavior,
9393
});
9494
}
9595
}
@@ -100,13 +100,19 @@ function Panel(props: DPanelProps, ref: React.ForwardedRef<(date: Date) => void>
100100
dataRef.current.clearSTid?.();
101101
dataRef.current.clearSTid = scrollTo(ulSRef.current, {
102102
top: Array.prototype.indexOf.call(ulSRef.current.children, ulSRef.current.querySelector(`[data-s="${second}"]`)) * 28,
103-
behavior: 'smooth',
103+
behavior,
104104
});
105105
}
106106
}
107107
});
108108

109-
useImperativeHandle(ref, () => updateView, [updateView]);
109+
useImperativeHandle(
110+
ref,
111+
() => (t) => {
112+
updateView(t, undefined, 'instant');
113+
},
114+
[updateView]
115+
);
110116

111117
return (
112118
<div className={`${dPrefix}time-picker__panel`}>

packages/ui/src/components/time-picker/TimePicker.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import React, { useRef } from 'react';
77
import { ClockCircleOutlined } from '@react-devui/icons';
88
import { getClassName } from '@react-devui/utils';
99

10-
import { useGeneralContext } from '../../hooks';
10+
import { useDValue, useGeneralContext } from '../../hooks';
1111
import { registerComponentMate } from '../../utils';
1212
import { DDateInput } from '../_date-input';
1313
import { getCols, orderTime } from '../_date-input/utils';
@@ -91,6 +91,8 @@ function TimePicker(props: DTimePickerProps, ref: React.ForwardedRef<DTimePicker
9191

9292
const [t] = useTranslation();
9393

94+
const [visible, changeVisible] = useDValue<boolean>(false, dVisible, onVisibleChange);
95+
9496
const size = dSize ?? gSize;
9597
const disabled = (dDisabled || gDisabled || dFormControl?.control.disabled) ?? false;
9698

@@ -109,7 +111,7 @@ function TimePicker(props: DTimePickerProps, ref: React.ForwardedRef<DTimePicker
109111
dFormControl={dFormControl}
110112
dModel={dModel}
111113
dFormat={format}
112-
dVisible={dVisible}
114+
dVisible={visible}
113115
dPlacement={dPlacement}
114116
dOrder={(date) => orderTime(date, dOrder)}
115117
dPlaceholder={[placeholderLeft, placeholderRight]}
@@ -120,7 +122,7 @@ function TimePicker(props: DTimePickerProps, ref: React.ForwardedRef<DTimePicker
120122
dDisabled={disabled}
121123
dInputRender={dInputRender}
122124
onModelChange={onModelChange}
123-
onVisibleChange={onVisibleChange}
125+
onVisibleChange={changeVisible}
124126
onUpdatePanel={(date) => {
125127
updatePanelRef.current?.(date);
126128
}}
@@ -144,6 +146,8 @@ function TimePicker(props: DTimePickerProps, ref: React.ForwardedRef<DTimePicker
144146
const now = new Date();
145147
changeDate(now);
146148
updatePanelRef.current?.(now);
149+
150+
changeVisible(false);
147151
}}
148152
dType="link"
149153
>

packages/ui/src/dayjs.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import 'dayjs/locale/zh-cn';
33
import customParseFormat from 'dayjs/plugin/customParseFormat';
44
import isBetween from 'dayjs/plugin/isBetween';
55
import localeData from 'dayjs/plugin/localeData';
6+
import updateLocale from 'dayjs/plugin/updateLocale';
67

78
dayjs.extend(customParseFormat);
89
dayjs.extend(isBetween);
910
dayjs.extend(localeData);
11+
dayjs.extend(updateLocale);
1012

1113
export default dayjs;

0 commit comments

Comments
 (0)