Skip to content

Commit 24b89d7

Browse files
committed
fix: correctly control popup open state in pickers
1 parent f109e42 commit 24b89d7

File tree

10 files changed

+250
-220
lines changed

10 files changed

+250
-220
lines changed

src/components/DateField/hooks/useDateFieldProps.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,15 @@ export function useDateFieldProps(
103103
if (state.selectedSectionIndexes !== null) {
104104
return;
105105
}
106-
const input = inputRef.current;
106+
const input = e.target;
107+
const isAutofocus = !inputRef.current;
107108
setTimeout(() => {
108109
if (!input || input !== inputRef.current) {
109110
return;
110111
}
111-
112-
if (
112+
if (isAutofocus) {
113+
state.focusSectionInPosition(0);
114+
} else if (
113115
// avoid selecting all sections when focusing empty field without value
114116
input.value.length &&
115117
Number(input.selectionEnd) - Number(input.selectionStart) ===

src/components/DateField/hooks/useDateFieldState.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,7 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
156156
: placeholderDate;
157157
const sectionsState = useSectionsState(sections, displayValue, validSegments);
158158

159-
const [selectedSections, setSelectedSections] = React.useState<number | 'all'>(() => {
160-
return sectionsState.editableSections[0]?.previousEditableSection ?? -1;
161-
});
159+
const [selectedSections, setSelectedSections] = React.useState<number | 'all'>(-1);
162160

163161
const selectedSectionIndexes = React.useMemo<{
164162
startIndex: number;

src/components/DatePicker/DatePicker.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ $block: '.#{variables.$ns}date-picker';
77

88
display: inline-block;
99

10+
outline: none;
11+
1012
&__field {
1113
width: 100%;
1214

@@ -15,6 +17,12 @@ $block: '.#{variables.$ns}date-picker';
1517
}
1618
}
1719

20+
&__popup-anchor {
21+
position: absolute;
22+
z-index: -1;
23+
inset: 0;
24+
}
25+
1826
&__popup-content {
1927
outline: none;
2028
}

src/components/DatePicker/DatePicker.tsx

Lines changed: 31 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React from 'react';
22

3-
import {TextInput, useFocusWithin, useMobile} from '@gravity-ui/uikit';
3+
import {Calendar as CalendarIcon} from '@gravity-ui/icons';
4+
import {Button, Icon, Popup, TextInput, useMobile} from '@gravity-ui/uikit';
45

5-
import type {CalendarProps} from '../Calendar';
6-
import {useDateFieldProps, useDateFieldState} from '../DateField';
6+
import {Calendar, type CalendarProps} from '../Calendar';
7+
import {DateField} from '../DateField';
78
import type {
89
AccessibilityProps,
910
DateFieldBase,
@@ -14,8 +15,8 @@ import type {
1415
TextInputProps,
1516
} from '../types';
1617

17-
import {DesktopCalendar, DesktopCalendarButton} from './DesktopCalendar';
1818
import {MobileCalendar, MobileCalendarIcon} from './MobileCalendar';
19+
import {useDatePickerProps} from './hooks/useDatePickerProps';
1920
import {useDatePickerState} from './hooks/useDatePickerState';
2021
import {b} from './utils';
2122

@@ -32,16 +33,7 @@ export interface DatePickerProps
3233
children?: (props: CalendarProps) => React.ReactNode;
3334
}
3435

35-
export function DatePicker({
36-
value,
37-
defaultValue,
38-
onUpdate,
39-
className,
40-
onFocus,
41-
onBlur,
42-
children,
43-
...props
44-
}: DatePickerProps) {
36+
export function DatePicker({value, defaultValue, onUpdate, className, ...props}: DatePickerProps) {
4537
const anchorRef = React.useRef<HTMLDivElement>(null);
4638

4739
const state = useDatePickerState({
@@ -51,70 +43,44 @@ export function DatePicker({
5143
onUpdate,
5244
});
5345

54-
const [isMobile] = useMobile();
55-
56-
const [isActive, setActive] = React.useState(false);
57-
const {focusWithinProps} = useFocusWithin({
58-
onFocusWithin: onFocus,
59-
onBlurWithin: onBlur,
60-
onFocusWithinChange(isFocusWithin) {
61-
setActive(isFocusWithin);
62-
},
63-
});
46+
const {groupProps, fieldProps, calendarButtonProps, popupProps, calendarProps, timeInputProps} =
47+
useDatePickerProps(state, props);
6448

65-
const fieldState = useDateFieldState({
66-
value: state.value,
67-
onUpdate: state.setValue,
68-
disabled: state.disabled,
69-
readOnly: state.readOnly,
70-
validationState: props.validationState,
71-
minValue: props.minValue,
72-
maxValue: props.maxValue,
73-
isDateUnavailable: props.isDateUnavailable,
74-
format: state.format,
75-
placeholderValue: props.placeholderValue,
76-
timeZone: props.timeZone,
77-
});
78-
79-
const {inputProps} = useDateFieldProps(fieldState, props);
49+
const [isMobile] = useMobile();
8050

8151
return (
82-
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
83-
<div
84-
ref={anchorRef}
85-
className={b(null, className)}
86-
style={props.style}
87-
{...focusWithinProps}
88-
role="group"
89-
aria-disabled={state.disabled || undefined}
90-
onKeyDown={(e) => {
91-
if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
92-
e.preventDefault();
93-
e.stopPropagation();
94-
state.setOpen(true);
95-
}
96-
}}
97-
>
52+
<div className={b(null, className)} {...groupProps}>
9853
{isMobile ? (
9954
<MobileCalendar props={props} state={state} />
10055
) : (
101-
<DesktopCalendar
102-
anchorRef={anchorRef}
103-
props={props}
104-
state={state}
105-
renderCalendar={children}
106-
/>
56+
<div ref={anchorRef} className={b('popup-anchor')}>
57+
<Popup anchorRef={anchorRef} {...popupProps}>
58+
<div className={b('popup-content')}>
59+
{typeof props.children === 'function' ? (
60+
props.children(calendarProps)
61+
) : (
62+
<Calendar {...calendarProps} />
63+
)}
64+
{state.hasTime && (
65+
<div className={b('time-field-wrapper')}>
66+
<DateField {...timeInputProps} />
67+
</div>
68+
)}
69+
</div>
70+
</Popup>
71+
</div>
10772
)}
10873
<TextInput
109-
{...inputProps}
110-
value={fieldState.isEmpty && !isActive && props.placeholder ? '' : inputProps.value}
74+
{...fieldProps}
11175
className={b('field', {mobile: isMobile})}
112-
hasClear={!isMobile && inputProps.hasClear}
76+
hasClear={!isMobile && fieldProps.hasClear}
11377
rightContent={
11478
isMobile ? (
11579
<MobileCalendarIcon props={props} state={state} />
11680
) : (
117-
<DesktopCalendarButton props={props} state={state} />
81+
<Button {...calendarButtonProps}>
82+
<Icon data={CalendarIcon} />
83+
</Button>
11884
)
11985
}
12086
/>

src/components/DatePicker/DesktopCalendar.tsx

Lines changed: 0 additions & 118 deletions
This file was deleted.

0 commit comments

Comments
 (0)