Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: correctly control popup open state in pickers #17

Merged
merged 1 commit into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/components/DateField/hooks/useDateFieldProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,15 @@ export function useDateFieldProps(
if (state.selectedSectionIndexes !== null) {
return;
}
const input = inputRef.current;
const input = e.target;
const isAutofocus = !inputRef.current;
setTimeout(() => {
if (!input || input !== inputRef.current) {
return;
}

if (
if (isAutofocus) {
state.focusSectionInPosition(0);
} else if (
// avoid selecting all sections when focusing empty field without value
input.value.length &&
Number(input.selectionEnd) - Number(input.selectionStart) ===
Expand Down
4 changes: 1 addition & 3 deletions src/components/DateField/hooks/useDateFieldState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
sections
.filter((seg) => EDITABLE_SEGMENTS[seg.type])
.reduce<typeof EDITABLE_SEGMENTS>((p, seg) => {
p[seg.type] = true;

Check warning on line 124 in src/components/DateField/hooks/useDateFieldState.ts

View workflow job for this annotation

GitHub Actions / Verify Files

Assignment to property of function parameter 'p'
return p;
}, {}),
[sections],
Expand Down Expand Up @@ -156,9 +156,7 @@
: placeholderDate;
const sectionsState = useSectionsState(sections, displayValue, validSegments);

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

const selectedSectionIndexes = React.useMemo<{
startIndex: number;
Expand Down
8 changes: 8 additions & 0 deletions src/components/DatePicker/DatePicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ $block: '.#{variables.$ns}date-picker';

display: inline-block;

outline: none;

&__field {
width: 100%;

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

&__popup-anchor {
position: absolute;
z-index: -1;
inset: 0;
}

&__popup-content {
outline: none;
}
Expand Down
96 changes: 31 additions & 65 deletions src/components/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';

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

import type {CalendarProps} from '../Calendar';
import {useDateFieldProps, useDateFieldState} from '../DateField';
import {Calendar, type CalendarProps} from '../Calendar';
import {DateField} from '../DateField';
import type {
AccessibilityProps,
DateFieldBase,
Expand All @@ -14,8 +15,8 @@ import type {
TextInputProps,
} from '../types';

import {DesktopCalendar, DesktopCalendarButton} from './DesktopCalendar';
import {MobileCalendar, MobileCalendarIcon} from './MobileCalendar';
import {useDatePickerProps} from './hooks/useDatePickerProps';
import {useDatePickerState} from './hooks/useDatePickerState';
import {b} from './utils';

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

export function DatePicker({
value,
defaultValue,
onUpdate,
className,
onFocus,
onBlur,
children,
...props
}: DatePickerProps) {
export function DatePicker({value, defaultValue, onUpdate, className, ...props}: DatePickerProps) {
const anchorRef = React.useRef<HTMLDivElement>(null);

const state = useDatePickerState({
Expand All @@ -51,70 +43,44 @@ export function DatePicker({
onUpdate,
});

const [isMobile] = useMobile();

const [isActive, setActive] = React.useState(false);
const {focusWithinProps} = useFocusWithin({
onFocusWithin: onFocus,
onBlurWithin: onBlur,
onFocusWithinChange(isFocusWithin) {
setActive(isFocusWithin);
},
});
const {groupProps, fieldProps, calendarButtonProps, popupProps, calendarProps, timeInputProps} =
useDatePickerProps(state, props);

const fieldState = useDateFieldState({
value: state.value,
onUpdate: state.setValue,
disabled: state.disabled,
readOnly: state.readOnly,
validationState: props.validationState,
minValue: props.minValue,
maxValue: props.maxValue,
isDateUnavailable: props.isDateUnavailable,
format: state.format,
placeholderValue: props.placeholderValue,
timeZone: props.timeZone,
});

const {inputProps} = useDateFieldProps(fieldState, props);
const [isMobile] = useMobile();

return (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
<div
ref={anchorRef}
className={b(null, className)}
style={props.style}
{...focusWithinProps}
role="group"
aria-disabled={state.disabled || undefined}
onKeyDown={(e) => {
if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
e.preventDefault();
e.stopPropagation();
state.setOpen(true);
}
}}
>
<div className={b(null, className)} {...groupProps}>
{isMobile ? (
<MobileCalendar props={props} state={state} />
) : (
<DesktopCalendar
anchorRef={anchorRef}
props={props}
state={state}
renderCalendar={children}
/>
<div ref={anchorRef} className={b('popup-anchor')}>
<Popup anchorRef={anchorRef} {...popupProps}>
<div className={b('popup-content')}>
{typeof props.children === 'function' ? (
props.children(calendarProps)
) : (
<Calendar {...calendarProps} />
)}
{state.hasTime && (
<div className={b('time-field-wrapper')}>
<DateField {...timeInputProps} />
</div>
)}
</div>
</Popup>
</div>
)}
<TextInput
{...inputProps}
value={fieldState.isEmpty && !isActive && props.placeholder ? '' : inputProps.value}
{...fieldProps}
className={b('field', {mobile: isMobile})}
hasClear={!isMobile && inputProps.hasClear}
hasClear={!isMobile && fieldProps.hasClear}
rightContent={
isMobile ? (
<MobileCalendarIcon props={props} state={state} />
) : (
<DesktopCalendarButton props={props} state={state} />
<Button {...calendarButtonProps}>
<Icon data={CalendarIcon} />
</Button>
)
}
/>
Expand Down
118 changes: 0 additions & 118 deletions src/components/DatePicker/DesktopCalendar.tsx

This file was deleted.

Loading
Loading