Skip to content

Commit

Permalink
feat(ui): optimize date-picker
Browse files Browse the repository at this point in the history
- Adjust popup window closing logic
- Show animation only click cell of time-picker
- Adjust format of `A` when dayjs's locale is `zh-cn`
  • Loading branch information
xiejay97 committed Feb 24, 2023
1 parent e10d922 commit 132cecc
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 33 deletions.
38 changes: 18 additions & 20 deletions packages/ui/src/components/_date-input/DateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export interface DDateInputProps extends Omit<React.HTMLAttributes<HTMLDivElemen
dFormControl: DFormControl | undefined;
dModel: Date | null | [Date, Date] | undefined;
dFormat: string;
dVisible: boolean | undefined;
dVisible: boolean;
dPlacement: 'top' | 'top-left' | 'top-right' | 'bottom' | 'bottom-left' | 'bottom-right';
dOrder: (date: [Date, Date]) => boolean;
dPlaceholder: [string, string];
Expand All @@ -55,7 +55,7 @@ export interface DDateInputProps extends Omit<React.HTMLAttributes<HTMLDivElemen
| [DCloneHTMLElement<React.InputHTMLAttributes<HTMLInputElement>>?, DCloneHTMLElement<React.InputHTMLAttributes<HTMLInputElement>>?]
| undefined;
onModelChange: ((date: any) => void) | undefined;
onVisibleChange: ((visible: boolean) => void) | undefined;
onVisibleChange: (visible: boolean) => void;
onUpdatePanel: ((date: Date) => void) | undefined;
afterVisibleChange: ((visible: boolean) => void) | undefined;
onClear: (() => void) | undefined;
Expand Down Expand Up @@ -129,8 +129,6 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
const [t] = useTranslation();
const forceUpdate = useForceUpdate();

const [visible, changeVisible] = useDValue<boolean>(false, dVisible, onVisibleChange);

const formControlInject = useFormControl(dFormControl);
const [_value, _changeValue] = useDValue<Date | null | [Date, Date]>(
null,
Expand Down Expand Up @@ -159,7 +157,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
});
} else {
dataRef.current.clearTid = async.setTimeout(() => {
changeVisible(false);
onVisibleChange(false);
setIsFocus([false, false]);
}, 20);
}
Expand Down Expand Up @@ -202,17 +200,17 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
forceUpdate();
};

const clearable = dClearable && !isNull(_value) && !visible && !dDisabled;
const clearable = dClearable && !isNull(_value) && !dVisible && !dDisabled;

const maxZIndex = useMaxIndex(visible);
const maxZIndex = useMaxIndex(dVisible);

const [popupPositionStyle, setPopupPositionStyle] = useState<React.CSSProperties>({
top: '-200vh',
left: '-200vw',
});
const [transformOrigin, setTransformOrigin] = useState<string>();
const updatePosition = useEventCallback(() => {
if (visible && boxRef.current && popupRef.current) {
if (dVisible && boxRef.current && popupRef.current) {
const height = popupRef.current.offsetHeight;
const maxWidth = window.innerWidth - WINDOW_SPACE * 2;
const width = Math.min(popupRef.current.scrollWidth, maxWidth);
Expand All @@ -233,12 +231,12 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
}
});

const globalScroll = useGlobalScroll(updatePosition, !visible);
useEvent(dPageScrollRef, 'scroll', updatePosition, { passive: true }, !visible || globalScroll);
const globalScroll = useGlobalScroll(updatePosition, !dVisible);
useEvent(dPageScrollRef, 'scroll', updatePosition, { passive: true }, !dVisible || globalScroll);

useResize(boxRef, updatePosition, !visible);
useResize(popupRef, updatePosition, !visible);
useResize(dContentResizeRef, updatePosition, !visible);
useResize(boxRef, updatePosition, !dVisible);
useResize(popupRef, updatePosition, !dVisible);
useResize(dContentResizeRef, updatePosition, !dVisible);

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

const getInputNode = (isLeft: boolean) => (
<DComboboxKeyboard
dVisible={visible}
dVisible={dVisible}
dEditable
dHasSub={false}
onVisibleChange={changeVisible}
onVisibleChange={onVisibleChange}
onFocusChange={() => {
// Only for popup open/close
}}
Expand Down Expand Up @@ -331,10 +329,10 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
if (isNull(isLeft ? valueRight : valueLeft)) {
dataRef.current.focusAnother = true;
} else {
changeVisible(false);
onVisibleChange(false);
}
} else {
changeVisible(false);
onVisibleChange(false);
}
} else {
dataRef.current.inputValue[index] = isNull(value) ? '' : dayjs(value).format(dFormat);
Expand Down Expand Up @@ -378,7 +376,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
renderBaseDesign(
<div
{...restProps}
{...{ [ESC_CLOSABLE_DATA]: visible }}
{...{ [ESC_CLOSABLE_DATA]: dVisible }}
ref={boxRef}
className={getClassName(restProps.className, prefix, {
[`${prefix}--${dSize}`]: dSize,
Expand All @@ -398,7 +396,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
onClick={(e) => {
restProps.onClick?.(e);

changeVisible(true);
onVisibleChange(true);
if (!hasFocus) {
inputLeftRef.current?.focus({ preventScroll: true });
}
Expand Down Expand Up @@ -439,7 +437,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
{containerRef.current &&
ReactDOM.createPortal(
<DTransition
dIn={visible}
dIn={dVisible}
dDuring={TTANSITION_DURING_POPUP}
onEnter={updatePosition}
afterEnter={() => {
Expand Down
20 changes: 16 additions & 4 deletions packages/ui/src/components/date-picker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import React, { useRef } from 'react';
import { CalendarOutlined } from '@react-devui/icons';
import { getClassName } from '@react-devui/utils';

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

const [t] = useTranslation();

const [visible, changeVisible] = useDValue<boolean>(false, dVisible, onVisibleChange);

const size = dSize ?? gSize;
const disabled = (dDisabled || gDisabled || dFormControl?.control.disabled) ?? false;

Expand Down Expand Up @@ -118,7 +120,7 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
dFormControl={dFormControl}
dModel={dModel}
dFormat={format}
dVisible={dVisible}
dVisible={visible}
dPlacement={dPlacement}
dOrder={(date) => orderDate(date, dOrder, dShowTime ? undefined : 'date')}
dPlaceholder={[placeholderLeft, placeholderRight]}
Expand All @@ -129,7 +131,7 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
dDisabled={disabled}
dInputRender={dInputRender}
onModelChange={onModelChange}
onVisibleChange={onVisibleChange}
onVisibleChange={changeVisible}
onUpdatePanel={(date) => {
updatePanelRef.current?.(date);
updateTimePickerPanelRef.current?.(date);
Expand All @@ -148,7 +150,13 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
dDateCurrentSelected={date[index]}
dDateAnotherSelected={date[isFocus[0] ? 1 : 0]}
dConfigDate={dConfigDate ? (...args) => dConfigDate(...args, position, date) : undefined}
onDateChange={changeDate}
onDateChange={(date) => {
changeDate(date);

if (!dShowTime) {
changeVisible(false);
}
}}
></DPanel>
{dShowTime &&
React.cloneElement<DTimePickerPanelPrivateProps>(
Expand Down Expand Up @@ -176,6 +184,8 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
changeDate(d);
updatePanelRef.current?.(d[index]);
updateTimePickerPanelRef.current?.(d[index]);

changeVisible(false);
};

return (
Expand All @@ -197,6 +207,8 @@ function DatePicker(props: DDatePickerProps, ref: React.ForwardedRef<DDateInputR
changeDate(now);
updatePanelRef.current?.(now);
updateTimePickerPanelRef.current?.(now);

changeVisible(false);
}}
dType="link"
>
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/date-picker/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface DPanelProps {
dDateCurrentSelected: Date | null;
dDateAnotherSelected: Date | null;
dConfigDate: ((date: Date) => { disabled?: boolean }) | undefined;
onDateChange: (time: Date) => void;
onDateChange: (date: Date) => void;
}

function Panel(props: DPanelProps, ref: React.ForwardedRef<(date: Date) => void>): JSX.Element | null {
Expand Down
6 changes: 6 additions & 0 deletions packages/ui/src/components/root/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ export function DRoot(props: DRootProps): JSX.Element | null {

case 'zh-CN':
dayjs.locale('zh-cn');
dayjs.updateLocale('zh-cn', {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
meridiem: (hour: number, minute: number, isLowercase: number) => {
return hour > 12 ? 'PM' : 'AM';
},
});
break;

default:
Expand Down
16 changes: 11 additions & 5 deletions packages/ui/src/components/time-picker/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function Panel(props: DPanelProps, ref: React.ForwardedRef<(date: Date) => void>
return unit.join(':');
})();

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

dataRef.current.clearHTid = scrollTo(ulHRef.current, {
top: Array.prototype.indexOf.call(ulHRef.current.children, ulHRef.current.querySelector(`[data-h="${hour}"]`)) * 28,
behavior: 'smooth',
behavior,
});
}
}
Expand All @@ -89,7 +89,7 @@ function Panel(props: DPanelProps, ref: React.ForwardedRef<(date: Date) => void>
dataRef.current.clearMTid?.();
dataRef.current.clearMTid = scrollTo(ulMRef.current, {
top: Array.prototype.indexOf.call(ulMRef.current.children, ulMRef.current.querySelector(`[data-m="${minute}"]`)) * 28,
behavior: 'smooth',
behavior,
});
}
}
Expand All @@ -100,13 +100,19 @@ function Panel(props: DPanelProps, ref: React.ForwardedRef<(date: Date) => void>
dataRef.current.clearSTid?.();
dataRef.current.clearSTid = scrollTo(ulSRef.current, {
top: Array.prototype.indexOf.call(ulSRef.current.children, ulSRef.current.querySelector(`[data-s="${second}"]`)) * 28,
behavior: 'smooth',
behavior,
});
}
}
});

useImperativeHandle(ref, () => updateView, [updateView]);
useImperativeHandle(
ref,
() => (t) => {
updateView(t, undefined, 'instant');
},
[updateView]
);

return (
<div className={`${dPrefix}time-picker__panel`}>
Expand Down
10 changes: 7 additions & 3 deletions packages/ui/src/components/time-picker/TimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import React, { useRef } from 'react';
import { ClockCircleOutlined } from '@react-devui/icons';
import { getClassName } from '@react-devui/utils';

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

const [t] = useTranslation();

const [visible, changeVisible] = useDValue<boolean>(false, dVisible, onVisibleChange);

const size = dSize ?? gSize;
const disabled = (dDisabled || gDisabled || dFormControl?.control.disabled) ?? false;

Expand All @@ -109,7 +111,7 @@ function TimePicker(props: DTimePickerProps, ref: React.ForwardedRef<DTimePicker
dFormControl={dFormControl}
dModel={dModel}
dFormat={format}
dVisible={dVisible}
dVisible={visible}
dPlacement={dPlacement}
dOrder={(date) => orderTime(date, dOrder)}
dPlaceholder={[placeholderLeft, placeholderRight]}
Expand All @@ -120,7 +122,7 @@ function TimePicker(props: DTimePickerProps, ref: React.ForwardedRef<DTimePicker
dDisabled={disabled}
dInputRender={dInputRender}
onModelChange={onModelChange}
onVisibleChange={onVisibleChange}
onVisibleChange={changeVisible}
onUpdatePanel={(date) => {
updatePanelRef.current?.(date);
}}
Expand All @@ -144,6 +146,8 @@ function TimePicker(props: DTimePickerProps, ref: React.ForwardedRef<DTimePicker
const now = new Date();
changeDate(now);
updatePanelRef.current?.(now);

changeVisible(false);
}}
dType="link"
>
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/dayjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import 'dayjs/locale/zh-cn';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import isBetween from 'dayjs/plugin/isBetween';
import localeData from 'dayjs/plugin/localeData';
import updateLocale from 'dayjs/plugin/updateLocale';

dayjs.extend(customParseFormat);
dayjs.extend(isBetween);
dayjs.extend(localeData);
dayjs.extend(updateLocale);

export default dayjs;

0 comments on commit 132cecc

Please sign in to comment.