Skip to content

Commit f161573

Browse files
committed
feat: TimePicker always show correct position when open
1 parent 0326ad1 commit f161573

File tree

6 files changed

+85
-59
lines changed

6 files changed

+85
-59
lines changed

src/PanelContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface PanelContextProps {
1717
onDateMouseLeave?: (date: any) => void;
1818
onSelect?: OnSelect<any>;
1919
hideRanges?: boolean;
20+
open?: boolean;
2021
}
2122

2223
const PanelContext = React.createContext<PanelContextProps>({});

src/Picker.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
401401
hideHeader: picker === 'time',
402402
panelRef: panelDivRef,
403403
onSelect: onContextSelect,
404+
open: mergedOpen,
404405
}}
405406
>
406407
<PickerTrigger

src/RangePicker.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,6 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
414414
const canTrigger =
415415
values === null || (canStartValueTrigger && canEndValueTrigger);
416416

417-
console.log('>>>', canTrigger);
418-
419417
if (canTrigger) {
420418
// Trigger onChange only when value is validate
421419
setInnerValue(values);
@@ -942,6 +940,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
942940
onDateMouseLeave,
943941
hideRanges: true,
944942
onSelect: onContextSelect,
943+
open: mergedOpen,
945944
}}
946945
>
947946
<PickerTrigger

src/panels/TimePanel/TimeUnitColumn.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
22
import classNames from 'classnames';
33
import { scrollTo } from '../../utils/uiUtil';
4+
import PanelContext from '../../PanelContext';
45

56
export interface Unit {
67
label: React.ReactText;
@@ -27,21 +28,28 @@ function TimeUnitColumn(props: TimeUnitColumnProps) {
2728
hideDisabledOptions,
2829
} = props;
2930
const cellPrefixCls = `${prefixCls}-cell`;
31+
const { open } = React.useContext(PanelContext);
3032

31-
const initRef = React.useRef(true);
3233
const ulRef = React.useRef<HTMLUListElement>(null);
3334
const liRefs = React.useRef<Map<number, HTMLElement | null>>(new Map());
3435

3536
// `useLayoutEffect` here to avoid blink by duration is 0
3637
React.useLayoutEffect(() => {
3738
const li = liRefs.current.get(value!);
38-
if (li) {
39-
scrollTo(ulRef.current!, li.offsetTop, initRef.current ? 0 : 120);
39+
if (li && open !== false) {
40+
scrollTo(ulRef.current!, li.offsetTop, 120);
4041
}
41-
42-
initRef.current = false;
4342
}, [value]);
4443

44+
React.useLayoutEffect(() => {
45+
if (open) {
46+
const li = liRefs.current.get(value!);
47+
if (li) {
48+
scrollTo(ulRef.current!, li.offsetTop, 0);
49+
}
50+
}
51+
}, [open]);
52+
4553
return (
4654
<ul
4755
className={classNames(`${prefixCls}-column`, {

src/utils/uiUtil.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,37 @@
11
import KeyCode from 'rc-util/lib/KeyCode';
22
import { PanelMode, PickerMode } from '../interface';
33

4+
const scrollIds = new Map<HTMLElement, number>();
5+
46
/* eslint-disable no-param-reassign */
57
export function scrollTo(element: HTMLElement, to: number, duration: number) {
8+
if (scrollIds.get(element)) {
9+
cancelAnimationFrame(scrollIds.get(element)!);
10+
}
11+
612
// jump to target if duration zero
713
if (duration <= 0) {
8-
requestAnimationFrame(() => {
9-
element.scrollTop = to;
10-
});
14+
scrollIds.set(
15+
element,
16+
requestAnimationFrame(() => {
17+
element.scrollTop = to;
18+
}),
19+
);
1120

1221
return;
1322
}
1423
const difference = to - element.scrollTop;
1524
const perTick = (difference / duration) * 10;
1625

17-
requestAnimationFrame(() => {
18-
element.scrollTop += perTick;
19-
if (element.scrollTop !== to) {
20-
scrollTo(element, to, duration - 10);
21-
}
22-
});
26+
scrollIds.set(
27+
element,
28+
requestAnimationFrame(() => {
29+
element.scrollTop += perTick;
30+
if (element.scrollTop !== to) {
31+
scrollTo(element, to, duration - 10);
32+
}
33+
}),
34+
);
2335
}
2436
/* eslint-enable */
2537

tests/panel.spec.tsx

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -92,52 +92,57 @@ describe('Picker.Panel', () => {
9292
});
9393

9494
describe('time click to scroll', () => {
95-
let domSpy: ReturnType<typeof spyElementPrototypes>;
96-
const requestAnimationFrameSpy = jest.spyOn(
97-
global,
98-
'requestAnimationFrame' as any,
99-
);
100-
101-
beforeEach(() => {
102-
let scrollTop = 90;
95+
[true, false].forEach(bool => {
96+
it(`spy requestAnimationFrame: ${bool}`, () => {
97+
let scrollTop = 90;
98+
const domSpy = spyElementPrototypes(HTMLElement, {
99+
scrollTop: {
100+
get: () => scrollTop,
101+
set: ((_: Function, value: number) => {
102+
scrollTop = value;
103+
}) as any,
104+
},
105+
});
106+
107+
let requestAnimationFrameSpy = jest.spyOn(
108+
global,
109+
'requestAnimationFrame' as any,
110+
);
103111

104-
domSpy = spyElementPrototypes(HTMLElement, {
105-
scrollTop: {
106-
get: () => scrollTop,
107-
set: ((_: Function, value: number) => {
108-
scrollTop = value;
109-
}) as any,
110-
},
112+
// Spy to trigger 2 way of test for checking case cover
113+
if (bool) {
114+
requestAnimationFrameSpy = requestAnimationFrameSpy.mockImplementation(
115+
window.setTimeout as any,
116+
);
117+
}
118+
119+
jest.useFakeTimers();
120+
const wrapper = mount(<MomentPickerPanel picker="time" />);
121+
122+
// Multiple times should only one work
123+
wrapper
124+
.find('ul')
125+
.first()
126+
.find('li')
127+
.at(3)
128+
.simulate('click');
129+
130+
wrapper
131+
.find('ul')
132+
.first()
133+
.find('li')
134+
.at(11)
135+
.simulate('click');
136+
jest.runAllTimers();
137+
138+
expect(requestAnimationFrameSpy).toHaveBeenCalled();
139+
140+
jest.useRealTimers();
141+
142+
domSpy.mockRestore();
143+
requestAnimationFrameSpy.mockRestore();
111144
});
112145
});
113-
afterEach(() => {
114-
domSpy.mockRestore();
115-
});
116-
117-
it('scroll', () => {
118-
jest.useFakeTimers();
119-
const wrapper = mount(<MomentPickerPanel picker="time" />);
120-
121-
// Multiple times should only one work
122-
wrapper
123-
.find('ul')
124-
.first()
125-
.find('li')
126-
.at(3)
127-
.simulate('click');
128-
129-
wrapper
130-
.find('ul')
131-
.first()
132-
.find('li')
133-
.at(11)
134-
.simulate('click');
135-
jest.runAllTimers();
136-
137-
expect(requestAnimationFrameSpy).toHaveBeenCalled();
138-
139-
jest.useRealTimers();
140-
});
141146
});
142147

143148
describe('click button to switch', () => {

0 commit comments

Comments
 (0)