Skip to content

Commit 670432e

Browse files
committed
feat: Re-trigger onOpenChange if open is fixed
1 parent 38540f3 commit 670432e

File tree

5 files changed

+66
-14
lines changed

5 files changed

+66
-14
lines changed

examples/uncontrolled.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,10 @@ export default () => (
1414
locale={zhCN}
1515
picker="week"
1616
allowClear
17-
/>
18-
<Picker<Moment>
19-
generateConfig={momentGenerateConfig}
20-
locale={zhCN}
21-
picker="month"
22-
allowClear
23-
/>
24-
<Picker<Moment>
25-
generateConfig={momentGenerateConfig}
26-
locale={zhCN}
27-
allowClear
17+
open
18+
onOpenChange={open => {
19+
console.log('=>', open);
20+
}}
2821
/>
2922
<button type="button">233</button>
3023
</div>

src/PanelContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface PanelContextProps {
99
operationRef?: React.MutableRefObject<ContextOperationRefProps | null>;
1010
/** Only work with time panel */
1111
hideHeader?: boolean;
12+
panelRef?: React.Ref<HTMLDivElement>;
1213
}
1314

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

src/Picker.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ import { isEqual } from './utils/dateUtil';
2525
import { toArray } from './utils/miscUtil';
2626
import PanelContext, { ContextOperationRefProps } from './PanelContext';
2727
import { PickerMode } from './interface';
28-
import { getDefaultFormat, getInputSize } from './utils/uiUtil';
28+
import {
29+
getDefaultFormat,
30+
getInputSize,
31+
addGlobalClickEvent,
32+
} from './utils/uiUtil';
2933

3034
export interface PickerSharedProps<DateType> {
3135
dropdownClassName?: string;
@@ -128,6 +132,10 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
128132
getDefaultFormat(format, picker, showTime, use12Hours),
129133
);
130134

135+
// Panel ref
136+
const panelDivRef = React.useRef<HTMLDivElement>(null);
137+
const inputDivRef = React.useRef<HTMLDivElement>(null);
138+
131139
// Real value
132140
const [innerValue, setInnerValue] = React.useState<DateType | null>(() => {
133141
if (value !== undefined) {
@@ -351,6 +359,22 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
351359
}
352360
}, [mergedValue]);
353361

362+
// Global click handler
363+
React.useEffect(() =>
364+
addGlobalClickEvent(({ target }: MouseEvent) => {
365+
if (
366+
mergedOpen &&
367+
panelDivRef.current &&
368+
!panelDivRef.current.contains(target as Node) &&
369+
inputDivRef.current &&
370+
!inputDivRef.current.contains(target as Node) &&
371+
onOpenChange
372+
) {
373+
onOpenChange(false);
374+
}
375+
}),
376+
);
377+
354378
// ============================= Panel =============================
355379
const panelProps = {
356380
// Remove `picker` & `format` here since TimePicker is little different with other panel
@@ -401,6 +425,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
401425
value={{
402426
operationRef,
403427
hideHeader: picker === 'time',
428+
panelRef: panelDivRef,
404429
}}
405430
>
406431
<div
@@ -420,7 +445,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
420445
getPopupContainer={getPopupContainer}
421446
transitionName={transitionName}
422447
>
423-
<div className={`${prefixCls}-input`}>
448+
<div className={`${prefixCls}-input`} ref={inputDivRef}>
424449
<input
425450
disabled={disabled}
426451
readOnly={inputReadOnly || !typing}

src/PickerPanel.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
117117
onMouseDown,
118118
} = props as MergedPickerPanelProps<DateType>;
119119

120-
const { operationRef } = React.useContext(PanelContext);
120+
const { operationRef, panelRef: panelDivRef } = React.useContext(
121+
PanelContext,
122+
);
121123
const { extraFooterSelections } = React.useContext(RangeContext);
122124
const panelRef = React.useRef<PanelRefProps>({});
123125

@@ -390,6 +392,7 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
390392
onKeyDown={onInternalKeyDown}
391393
onBlur={onInternalBlur}
392394
onMouseDown={onMouseDown}
395+
ref={panelDivRef}
393396
>
394397
{panelNode}
395398
{extraFooter || todayNode || extraSelectionNode ? (

src/utils/uiUtil.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,36 @@ export function getInputSize(picker: PickerMode | undefined, format: string) {
152152
return Math.max(defaultSize, format.length) + 2;
153153
}
154154

155+
// ===================== Window =====================
156+
type ClickEventHandler = (event: MouseEvent) => void;
157+
let globalClickFunc: ClickEventHandler | null = null;
158+
const clickCallbacks = new Set<ClickEventHandler>();
159+
160+
export function addGlobalClickEvent(callback: ClickEventHandler) {
161+
if (
162+
!globalClickFunc &&
163+
typeof window !== 'undefined' &&
164+
window.addEventListener
165+
) {
166+
globalClickFunc = (e: MouseEvent) => {
167+
clickCallbacks.forEach(queueFunc => {
168+
queueFunc(e);
169+
});
170+
};
171+
window.addEventListener('click', globalClickFunc);
172+
}
173+
174+
clickCallbacks.add(callback);
175+
176+
return () => {
177+
clickCallbacks.delete(callback);
178+
if (clickCallbacks.size === 0) {
179+
window.removeEventListener('click', globalClickFunc!);
180+
globalClickFunc = null;
181+
}
182+
};
183+
}
184+
155185
// ====================== Mode ======================
156186
const getYearNextMode = (next: PanelMode): PanelMode => {
157187
if (next === 'month' || next === 'date') {

0 commit comments

Comments
 (0)