Skip to content

Commit da38cbb

Browse files
crazyairzombieJ
andauthored
feat: support focus options (#767)
* feat: support focus options * feat: demo * feat: 类型合并 * feat: 默认值 * feat: 优化代码 * feat: type * feat: type * feat: type * feat: type * feat: type * feat: type * feat: type * feat: type * chore: merge usePickerRef * chore: fix null type * chore: simplify ts --------- Co-authored-by: 二货机器人 <[email protected]>
1 parent b219e15 commit da38cbb

File tree

11 files changed

+109
-48
lines changed

11 files changed

+109
-48
lines changed

docs/demo/focus.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: focus
3+
nav:
4+
title: Demo
5+
path: /demo
6+
---
7+
8+
<code src="../examples/focus.tsx"></code>

docs/examples/focus.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React, { useLayoutEffect, useRef, useState } from 'react';
2+
import '../../assets/index.less';
3+
import { Picker, RangePicker, type PickerRef } from '../../src';
4+
import momentGenerateConfig from '../../src/generate/moment';
5+
import zhCN from '../../src/locale/zh_CN';
6+
import type { RangePickerRef } from '../../src/interface';
7+
8+
const MyPicker = () => {
9+
const ref = useRef<PickerRef>(null);
10+
useLayoutEffect(() => {
11+
if (ref.current) {
12+
ref.current.focus({ preventScroll: true });
13+
}
14+
}, []);
15+
16+
return (
17+
<div>
18+
<Picker open locale={zhCN} generateConfig={momentGenerateConfig} ref={ref} />
19+
</div>
20+
);
21+
};
22+
23+
const MyRangePicker = () => {
24+
const ref = useRef<RangePickerRef>(null);
25+
useLayoutEffect(() => {
26+
if (ref.current) {
27+
ref.current.focus({ preventScroll: true, index: 1 });
28+
}
29+
}, []);
30+
31+
return (
32+
<div>
33+
<RangePicker open locale={zhCN} generateConfig={momentGenerateConfig} ref={ref} />
34+
</div>
35+
);
36+
};
37+
38+
const Demo = () => {
39+
const [open, setOpen] = useState(false);
40+
const [open2, setOpen2] = useState(false);
41+
return (
42+
<div>
43+
<div style={{ height: '50vh' }} />
44+
<a onClick={() => setOpen(!open)}>picker {`${open}`}</a>
45+
<br />
46+
<a onClick={() => setOpen2(!open2)}>rangePicker {`${open2}`}</a>
47+
<div style={{ height: '80vh' }} />
48+
{open && <MyPicker />}
49+
{open2 && <MyRangePicker />}
50+
<div style={{ height: '30vh' }} />
51+
</div>
52+
);
53+
};
54+
55+
export default Demo;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"gh-pages": "npm run build && father doc deploy",
3232
"prepublishOnly": "npm run compile && np --yolo --no-publish",
3333
"lint": "eslint src/ --ext .ts,.tsx,.jsx,.js,.md",
34+
"lint:tsc": "tsc -p tsconfig.json --noEmit",
3435
"prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
3536
"test": "rc-test",
3637
"coverage": "father test --coverage",

src/PickerInput/RangePicker.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type {
1010
OnOpenChange,
1111
OpenConfig,
1212
PanelMode,
13-
PickerRef,
13+
RangePickerRef,
1414
RangeTimeProps,
1515
SelectorProps,
1616
SharedHTMLAttrs,
@@ -26,7 +26,7 @@ import useCellRender from './hooks/useCellRender';
2626
import useFieldsInvalidate from './hooks/useFieldsInvalidate';
2727
import useFilledProps from './hooks/useFilledProps';
2828
import useOpen from './hooks/useOpen';
29-
import { usePickerRef } from './hooks/usePickerRef';
29+
import usePickerRef from './hooks/usePickerRef';
3030
import usePresets from './hooks/usePresets';
3131
import useRangeActive from './hooks/useRangeActive';
3232
import useRangeDisabledDate from './hooks/useRangeDisabledDate';
@@ -136,7 +136,7 @@ function getActiveRange(activeIndex: number) {
136136

137137
function RangePicker<DateType extends object = any>(
138138
props: RangePickerProps<DateType>,
139-
ref: React.Ref<PickerRef>,
139+
ref: React.Ref<RangePickerRef>,
140140
) {
141141
// ========================= Prop =========================
142142
const [filledProps, internalPicker, complexPicker, formatList, maskFormat, isInvalidateDate] =
@@ -412,7 +412,7 @@ function RangePicker<DateType extends object = any>(
412412
if (nextIndex === null) {
413413
triggerOpen(false, { force: true });
414414
} else if (!skipFocus) {
415-
selectorRef.current.focus(nextIndex);
415+
selectorRef.current.focus({ index: nextIndex });
416416
}
417417
};
418418

@@ -422,7 +422,7 @@ function RangePicker<DateType extends object = any>(
422422
// Click to focus the enabled input
423423
const enabledIndex = disabled.findIndex((d) => !d);
424424
if (enabledIndex >= 0) {
425-
selectorRef.current.focus(enabledIndex);
425+
selectorRef.current.focus({ index: enabledIndex });
426426
}
427427
}
428428

@@ -754,7 +754,7 @@ function RangePicker<DateType extends object = any>(
754754
}
755755

756756
const RefRangePicker = React.forwardRef(RangePicker) as <DateType extends object = any>(
757-
props: RangePickerProps<DateType> & React.RefAttributes<PickerRef>,
757+
props: RangePickerProps<DateType> & React.RefAttributes<RangePickerRef>,
758758
) => React.ReactElement;
759759

760760
if (process.env.NODE_ENV !== 'production') {

src/PickerInput/Selector/Input.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import useLockEffect from '../hooks/useLockEffect';
99
import Icon from './Icon';
1010
import MaskFormat from './MaskFormat';
1111
import { getMaskRange } from './util';
12+
import type { PickerRef } from '../../interface';
1213

1314
// Format logic
1415
//
@@ -22,11 +23,8 @@ import { getMaskRange } from './util';
2223
// 2. Re-selection the mask cell
2324
// 3. If `cacheValue` match the limit length or cell format (like 1 ~ 12 month), go to next cell
2425

25-
export interface InputRef {
26-
nativeElement: HTMLDivElement;
26+
export interface InputRef extends PickerRef {
2727
inputElement: HTMLInputElement;
28-
focus: VoidFunction;
29-
blur: VoidFunction;
3028
}
3129

3230
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
@@ -97,8 +95,8 @@ const Input = React.forwardRef<InputRef, InputProps>((props, ref) => {
9795
React.useImperativeHandle(ref, () => ({
9896
nativeElement: holderRef.current,
9997
inputElement: inputRef.current,
100-
focus: () => {
101-
inputRef.current.focus();
98+
focus: (options) => {
99+
inputRef.current.focus(options);
102100
},
103101
blur: () => {
104102
inputRef.current.blur();

src/PickerInput/Selector/RangeSelector.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import classNames from 'classnames';
22
import ResizeObserver from 'rc-resize-observer';
33
import { useEvent } from 'rc-util';
44
import * as React from 'react';
5-
import type { SelectorProps, SelectorRef } from '../../interface';
5+
import type { RangePickerRef, SelectorProps } from '../../interface';
66
import PickerContext from '../context';
77
import useInputProps from './hooks/useInputProps';
88
import useRootProps from './hooks/useRootProps';
@@ -46,7 +46,7 @@ export interface RangeSelectorProps<DateType = any> extends SelectorProps<DateTy
4646

4747
function RangeSelector<DateType extends object = any>(
4848
props: RangeSelectorProps<DateType>,
49-
ref: React.Ref<SelectorRef>,
49+
ref: React.Ref<RangePickerRef>,
5050
) {
5151
const {
5252
id,
@@ -138,8 +138,13 @@ function RangeSelector<DateType extends object = any>(
138138

139139
React.useImperativeHandle(ref, () => ({
140140
nativeElement: rootRef.current,
141-
focus: (index = 0) => {
142-
getInput(index)?.focus();
141+
focus: (options) => {
142+
if (typeof options === 'object') {
143+
const { index = 0, ...rest } = options || {};
144+
getInput(index)?.focus(rest);
145+
} else {
146+
getInput(options ?? 0)?.focus();
147+
}
143148
},
144149
blur: () => {
145150
getInput(0)?.blur();

src/PickerInput/Selector/SingleSelector/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import classNames from 'classnames';
22
import * as React from 'react';
3-
import type { InternalMode, SelectorProps, SelectorRef } from '../../../interface';
3+
import type { InternalMode, PickerRef, SelectorProps } from '../../../interface';
44
import { isSame } from '../../../utils/dateUtil';
55
import PickerContext from '../../context';
66
import type { PickerProps } from '../../SinglePicker';
@@ -36,7 +36,7 @@ export interface SingleSelectorProps<DateType extends object = any>
3636

3737
function SingleSelector<DateType extends object = any>(
3838
props: SingleSelectorProps<DateType>,
39-
ref: React.Ref<SelectorRef>,
39+
ref: React.Ref<PickerRef>,
4040
) {
4141
const {
4242
id,
@@ -116,8 +116,8 @@ function SingleSelector<DateType extends object = any>(
116116

117117
React.useImperativeHandle(ref, () => ({
118118
nativeElement: rootRef.current,
119-
focus: () => {
120-
inputRef.current?.focus();
119+
focus: (options) => {
120+
inputRef.current?.focus(options);
121121
},
122122
blur: () => {
123123
inputRef.current?.blur();

src/PickerInput/SinglePicker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import useCellRender from './hooks/useCellRender';
2323
import useFieldsInvalidate from './hooks/useFieldsInvalidate';
2424
import useFilledProps from './hooks/useFilledProps';
2525
import useOpen from './hooks/useOpen';
26-
import { usePickerRef } from './hooks/usePickerRef';
26+
import usePickerRef from './hooks/usePickerRef';
2727
import usePresets from './hooks/usePresets';
2828
import useRangeActive from './hooks/useRangeActive';
2929
import useRangePickerValue from './hooks/useRangePickerValue';

src/PickerInput/hooks/usePickerRef.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import * as React from 'react';
2-
import type { PickerRef, SelectorRef } from '../../interface';
2+
import type { PickerRef } from '../../interface';
33

4-
export function usePickerRef(ref: React.Ref<PickerRef>) {
5-
const selectorRef = React.useRef<SelectorRef>();
4+
type PickerRefType<OptionType> = Omit<PickerRef, 'focus'> & {
5+
focus: (options?: OptionType) => void;
6+
};
7+
8+
export default function usePickerRef<OptionType>(ref: React.Ref<PickerRefType<OptionType>>) {
9+
const selectorRef = React.useRef<PickerRefType<OptionType>>();
610

711
React.useImperativeHandle(ref, () => ({
812
nativeElement: selectorRef.current?.nativeElement,
9-
focus: () => {
10-
selectorRef.current?.focus();
13+
focus: (options) => {
14+
selectorRef.current?.focus(options);
1115
},
1216
blur: () => {
1317
selectorRef.current?.blur();

src/interface.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -432,12 +432,18 @@ export interface SharedPickerProps<DateType extends object = any>
432432
renderExtraFooter?: (mode: PanelMode) => React.ReactNode;
433433
}
434434

435+
// picker
435436
export interface PickerRef {
436437
nativeElement: HTMLDivElement;
437-
focus: VoidFunction;
438+
focus: (options?: FocusOptions) => void;
438439
blur: VoidFunction;
439440
}
440441

442+
// rangePicker
443+
export interface RangePickerRef extends Omit<PickerRef, 'focus'> {
444+
focus: (index?: number | (FocusOptions & { index?: number })) => void;
445+
}
446+
441447
// ======================== Selector ========================
442448
export interface OpenConfig {
443449
index?: number;
@@ -506,12 +512,6 @@ export interface SelectorProps<DateType = any> extends SharedHTMLAttrs {
506512
inputReadOnly?: boolean;
507513
}
508514

509-
export interface SelectorRef {
510-
nativeElement: HTMLDivElement;
511-
focus: (index?: number) => void;
512-
blur: VoidFunction;
513-
}
514-
515515
// ========================== MISC ==========================
516516
// https://stackoverflow.com/a/39495173; need TypeScript >= 4.5
517517
type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N

0 commit comments

Comments
 (0)