Skip to content

Commit 0fa205d

Browse files
committed
feat!: streamline relative range date picker dialog props, extract picker form
1 parent fac625d commit 0fa205d

File tree

9 files changed

+326
-257
lines changed

9 files changed

+326
-257
lines changed

src/components/RelativeRangeDatePicker/RelativeRangeDatePicker.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ export function RelativeRangeDatePicker(props: RelativeRangeDatePickerProps) {
130130
/>
131131

132132
<PickerDialog
133-
state={state}
134-
props={props}
133+
value={state.value}
134+
onUpdate={state.setValue}
135+
timeZone={state.timeZone}
135136
open={open}
136137
onClose={(reason) => {
137138
setOpen(false);
@@ -143,9 +144,24 @@ export function RelativeRangeDatePicker(props: RelativeRangeDatePickerProps) {
143144
}}
144145
anchor={anchor}
145146
modal
147+
popupClassName={props.popupClassName}
148+
popupStyle={props.popupStyle}
149+
popupPlacement={props.popupPlacement}
150+
popupOffset={props.popupOffset}
146151
isMobile={isMobile}
147-
className={props.popupClassName}
148-
placement={props.popupPlacement}
152+
size={props.size}
153+
format={props.format}
154+
readOnly={props.readOnly}
155+
minValue={props.minValue}
156+
maxValue={props.maxValue}
157+
allowNullableValues={props.allowNullableValues}
158+
isDateUnavailable={props.isDateUnavailable}
159+
placeholderValue={props.placeholderValue}
160+
withPresets={props.withPresets}
161+
presetTabs={props.presetTabs}
162+
docs={props.docs}
163+
withApplyButton={props.withApplyButton}
164+
withZonesList={props.withZonesList}
149165
/>
150166
</div>
151167
);

src/components/RelativeRangeDatePicker/components/PickerDialog/PickerDialog.scss

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,4 @@ $block: '.#{variables.$ns}relative-range-date-picker-dialog';
2222
width: 380px;
2323
}
2424
}
25-
26-
&__pickers {
27-
display: flex;
28-
flex-direction: column;
29-
gap: var(--g-spacing-2);
30-
31-
.g-text-input__label {
32-
width: 40px;
33-
34-
#{$block}__content_size_xl & {
35-
width: 50px;
36-
}
37-
}
38-
}
39-
40-
&__apply {
41-
margin-block-start: var(--g-spacing-2);
42-
}
43-
44-
&__zone {
45-
margin-block: var(--g-spacing-2) calc(-1 * var(--_--popup-content-padding));
46-
margin-inline: calc(-1 * var(--_--popup-content-padding));
47-
48-
border-block-start: 1px solid var(--g-color-line-generic);
49-
}
50-
51-
&__presets {
52-
margin-inline: calc(-1 * var(--_--popup-content-padding));
53-
}
5425
}
Lines changed: 43 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,59 @@
11
'use client';
22

3-
import {dateTime} from '@gravity-ui/date-utils';
4-
import {Button, Popup, Sheet} from '@gravity-ui/uikit';
5-
import type {PopupOffset, PopupPlacement} from '@gravity-ui/uikit';
3+
import {Popup, Sheet} from '@gravity-ui/uikit';
4+
import type {PopupProps} from '@gravity-ui/uikit';
65

76
import {block} from '../../../../utils/cn';
8-
import {RelativeDatePicker} from '../../../RelativeDatePicker';
9-
import type {RelativeDatePickerProps} from '../../../RelativeDatePicker';
10-
import type {RelativeRangeDatePickerState} from '../../hooks/useRelativeRangeDatePickerState';
11-
import type {RelativeRangeDatePickerProps} from '../../types';
12-
import {Presets} from '../Presets/Presets';
13-
import {Zones} from '../Zones/Zones';
7+
import type {PopupStyleProps} from '../../../types';
148

15-
import {i18n} from './i18n';
16-
import {useRelativeRangeDatePickerDialogState} from './useRelativeRangeDatePickerDialogState';
9+
import {PickerForm} from './PickerForm';
10+
import type {PickerFormProps} from './PickerForm';
1711

1812
import './PickerDialog.scss';
1913

2014
const b = block('relative-range-date-picker-dialog');
2115

22-
export interface PickerDialogProps {
23-
className?: string;
24-
state: RelativeRangeDatePickerState;
25-
props: RelativeRangeDatePickerProps;
16+
export interface PickerDialogProps extends PickerFormProps, PopupStyleProps {
17+
/** Popup anchor */
18+
anchor: HTMLElement | null;
19+
/** Manages `Popup` visibility */
2620
open: boolean;
27-
placement?: PopupPlacement;
28-
offset?: PopupOffset;
21+
/** Handles popup close event */
22+
onClose: (reason?: Parameters<NonNullable<PopupProps['onOpenChange']>>[2] | 'apply') => void;
23+
/** If true `Popup` act like a modal dialog */
24+
modal?: boolean;
25+
/** If true `Sheet` is used instead of `Popup` */
2926
isMobile?: boolean;
30-
anchor: HTMLElement | null;
31-
onClose: () => void;
32-
focusInput?: () => void;
33-
disableFocusTrap?: boolean;
3427
}
3528

3629
export function PickerDialog({
37-
props,
38-
state,
3930
open,
4031
onClose,
41-
focusInput,
4232
isMobile,
4333
anchor,
44-
className,
45-
placement,
46-
offset,
47-
disableFocusTrap,
34+
popupClassName,
35+
popupStyle,
36+
popupPlacement,
37+
popupOffset,
38+
modal,
39+
...props
4840
}: PickerDialogProps) {
4941
if (isMobile) {
5042
return (
5143
<Sheet
5244
visible={open}
53-
onClose={onClose}
54-
contentClassName={b('content', {mobile: true, size: 'xl'}, className)}
45+
onClose={() => {
46+
onClose('outside-press');
47+
}}
48+
contentClassName={b('content', {mobile: true, size: 'xl'}, popupClassName)}
5549
>
56-
<DialogContent {...props} size="xl" state={state} onApply={onClose} />
50+
<PickerForm
51+
{...props}
52+
size="xl"
53+
onApply={() => {
54+
onClose('apply');
55+
}}
56+
/>
5757
</Sheet>
5858
);
5959
}
@@ -63,114 +63,23 @@ export function PickerDialog({
6363
open={open}
6464
onOpenChange={(isOpen, _event, reason) => {
6565
if (!isOpen) {
66-
onClose();
67-
if (reason === 'escape-key') {
68-
focusInput?.();
69-
}
66+
onClose(reason);
7067
}
7168
}}
72-
placement={placement}
73-
offset={offset}
69+
placement={popupPlacement}
70+
offset={popupOffset}
7471
role="dialog"
7572
anchorElement={anchor}
76-
className={b('content', {size: props.size}, className)}
77-
modal={!disableFocusTrap}
73+
className={b('content', {size: props.size}, popupClassName)}
74+
style={popupStyle}
75+
modal={modal}
7876
>
79-
<DialogContent {...props} state={state} onApply={onClose} />
77+
<PickerForm
78+
{...props}
79+
onApply={() => {
80+
onClose('apply');
81+
}}
82+
/>
8083
</Popup>
8184
);
8285
}
83-
84-
function DialogContent(
85-
props: {
86-
state: RelativeRangeDatePickerState;
87-
onApply: () => void;
88-
} & RelativeRangeDatePickerProps,
89-
) {
90-
const state = useRelativeRangeDatePickerDialogState(props.state, props);
91-
92-
const placeholderValue =
93-
props.placeholderValue?.timeZone(props.state.timeZone) ||
94-
dateTime({timeZone: props.state.timeZone});
95-
const fieldProps: RelativeDatePickerProps = {
96-
timeZone: props.state.timeZone,
97-
format: props.format,
98-
minValue: props.minValue,
99-
maxValue: props.maxValue,
100-
hasClear: props.allowNullableValues,
101-
readOnly: props.readOnly,
102-
size: props.size,
103-
errorPlacement: 'inside',
104-
};
105-
return (
106-
<div>
107-
<div className={b('pickers')}>
108-
<RelativeDatePicker
109-
{...fieldProps}
110-
validationState={state.startValidation?.isInvalid ? 'invalid' : undefined}
111-
errorMessage={
112-
state.startValidation?.errors?.join('\n') || i18n('Value is incorrect.')
113-
}
114-
placeholderValue={placeholderValue.startOf('day')}
115-
label={i18n('From')}
116-
value={state.start}
117-
onUpdate={state.setStart}
118-
/>
119-
<RelativeDatePicker
120-
{...fieldProps}
121-
validationState={state.endValidation?.isInvalid ? 'invalid' : undefined}
122-
errorMessage={
123-
state.endValidation?.errors?.join('\n') || i18n('Value is incorrect.')
124-
}
125-
placeholderValue={placeholderValue.endOf('day')}
126-
label={i18n('To')}
127-
value={state.end}
128-
onUpdate={state.setEnd}
129-
roundUp
130-
/>
131-
</div>
132-
{props.withApplyButton && !props.readOnly ? (
133-
<Button
134-
disabled={state.isInvalid}
135-
size={props.size}
136-
onClick={() => {
137-
state.applyValue();
138-
props.onApply();
139-
}}
140-
className={b('apply')}
141-
width="max"
142-
>
143-
{i18n('Apply')}
144-
</Button>
145-
) : null}
146-
{props.withPresets && !props.readOnly ? (
147-
<Presets
148-
size={props.size}
149-
presetTabs={props.presetTabs}
150-
onChoosePreset={(start, end) => {
151-
state.setRange(
152-
{type: 'relative', value: start},
153-
{type: 'relative', value: end},
154-
);
155-
if (!props.withApplyButton) {
156-
props.onApply();
157-
}
158-
}}
159-
minValue={props.minValue}
160-
docs={props.docs}
161-
className={b('presets')}
162-
/>
163-
) : null}
164-
{props.withZonesList ? (
165-
<div className={b('zone')}>
166-
<Zones
167-
value={state.timeZone}
168-
onUpdate={state.setTimeZone}
169-
disabled={props.readOnly}
170-
size={props.size}
171-
/>
172-
</div>
173-
) : null}
174-
</div>
175-
);
176-
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@use '../../../variables';
2+
3+
$block: '.#{variables.$ns}relative-range-date-picker-form';
4+
5+
#{$block} {
6+
&__pickers {
7+
display: flex;
8+
flex-direction: column;
9+
gap: var(--g-spacing-2);
10+
11+
.g-text-input__label {
12+
width: 40px;
13+
14+
#{$block}_size_xl & {
15+
width: 50px;
16+
}
17+
}
18+
}
19+
20+
&__apply {
21+
margin-block-start: var(--g-spacing-2);
22+
}
23+
24+
&__zone {
25+
margin-block: var(--g-spacing-2) calc(-1 * var(--_--popup-content-padding));
26+
margin-inline: calc(-1 * var(--_--popup-content-padding));
27+
28+
border-block-start: 1px solid var(--g-color-line-generic);
29+
}
30+
31+
&__presets {
32+
margin-inline: calc(-1 * var(--_--popup-content-padding));
33+
}
34+
}

0 commit comments

Comments
 (0)