Skip to content

Commit 57eabf9

Browse files
committed
feat(RelativeRangeDatePicker): add renderControl props
1 parent 41c4b27 commit 57eabf9

File tree

13 files changed

+310
-194
lines changed

13 files changed

+310
-194
lines changed

src/components/RelativeRangeDatePicker/RelativeRangeDatePicker.scss

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

88
display: inline-flex;
99

10-
&__input {
11-
caret-color: transparent;
12-
13-
&_mobile {
14-
pointer-events: none;
15-
}
16-
}
17-
18-
&__mobile-trigger {
19-
--_--g-date-mobile-trigger-clear-width: 0px;
20-
--_--g-date-mobile-trigger-errors-width: 0px;
21-
--_--g-date-mobile-trigger-button-width: 24px;
22-
23-
position: absolute;
24-
inset: 0;
25-
inset-inline-end: calc(
26-
var(--g-spacing-2) + var(--_--g-date-mobile-trigger-button-width) +
27-
var(--_--g-date-mobile-trigger-clear-width) +
28-
var(--_--g-date-mobile-trigger-errors-width)
29-
);
30-
31-
opacity: 0;
32-
33-
&_size_s {
34-
--_--g-date-mobile-trigger-button-width: 20px;
35-
}
36-
&_size_l {
37-
--_--g-date-mobile-trigger-button-width: 28px;
38-
}
39-
&_size_xl {
40-
--_--g-date-mobile-trigger-button-width: 36px;
41-
}
42-
43-
&_has-clear {
44-
--_--g-date-mobile-trigger-clear-width: calc(
45-
var(--_--g-date-mobile-trigger-button-width) + 2px
46-
);
47-
}
48-
&_has-errors {
49-
--_--g-date-mobile-trigger-errors-width: calc(
50-
var(--_--g-date-mobile-trigger-button-width) + 2px
51-
);
52-
}
53-
}
54-
5510
&__value-label {
5611
display: flex;
5712

src/components/RelativeRangeDatePicker/RelativeRangeDatePicker.tsx

Lines changed: 25 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,21 @@
22

33
import React from 'react';
44

5-
import type {DateTime} from '@gravity-ui/date-utils';
6-
import {Calendar as CalendarIcon} from '@gravity-ui/icons';
7-
import {
8-
Button,
9-
Icon,
10-
TextInput,
11-
useControlledState,
12-
useFocusWithin,
13-
useMobile,
14-
} from '@gravity-ui/uikit';
5+
import {useControlledState, useFocusWithin, useMobile} from '@gravity-ui/uikit';
156

167
import {block} from '../../utils/cn';
178
import {HiddenInput} from '../HiddenInput/HiddenInput';
189
import type {Value} from '../RelativeDatePicker';
19-
import type {
20-
DomProps,
21-
FocusableProps,
22-
InputBase,
23-
InputDOMProps,
24-
RangeValue,
25-
StyleProps,
26-
TextInputProps,
27-
Validation,
28-
} from '../types';
29-
import {getButtonSizeForInput} from '../utils/getButtonSizeForInput';
3010

11+
import {Control} from './components/Control/Control';
3112
import {PickerDialog} from './components/PickerDialog/PickerDialog';
32-
import type {Preset} from './components/Presets/defaultPresets';
33-
import type {PresetTab} from './components/Presets/utils';
3413
import {useRelativeRangeDatePickerState} from './hooks/useRelativeRangeDatePickerState';
35-
import type {RelativeRangeDatePickerStateOptions} from './hooks/useRelativeRangeDatePickerState';
36-
import {i18n} from './i18n';
37-
import {getDefaultTitle} from './utils';
14+
import type {RelativeRangeDatePickerProps} from './types';
3815

3916
import './RelativeRangeDatePicker.scss';
4017

4118
const b = block('relative-range-date-picker');
4219

43-
export interface RelativeRangeDatePickerProps
44-
extends RelativeRangeDatePickerStateOptions,
45-
DomProps,
46-
InputBase,
47-
InputDOMProps,
48-
TextInputProps,
49-
Validation,
50-
FocusableProps,
51-
StyleProps {
52-
/** Format of the date when rendered in the input. [Available formats](https://day.js.org/docs/en/display/format) */
53-
format?: string;
54-
/** A placeholder date that controls the default values of each segment when the user first interacts with them. Defaults to today's date at midnight. */
55-
placeholderValue?: DateTime;
56-
/** Apply changes with button */
57-
withApplyButton?: boolean;
58-
/** Show time zone selector */
59-
withZonesList?: boolean;
60-
/** Show relative range presets */
61-
withPresets?: boolean;
62-
/** Custom preset tabs */
63-
presetTabs?: PresetTab[];
64-
/** Custom docs for presets, if empty array docs will be hidden */
65-
docs?: Preset[];
66-
/** Show selected relative values as absolute dates */
67-
alwaysShowAsAbsolute?: boolean;
68-
/** */
69-
getRangeTitle?: (value: RangeValue<Value | null> | null, timeZone: string) => string;
70-
/** Sets the CSS className for the popup element. */
71-
popupClassName?: string;
72-
/** Handler that is called when the popup's open state changes. */
73-
onOpenChange?: (open: boolean) => void;
74-
}
75-
7620
export function RelativeRangeDatePicker(props: RelativeRangeDatePickerProps) {
7721
const state = useRelativeRangeDatePickerState(props);
7822

@@ -99,58 +43,27 @@ export function RelativeRangeDatePicker(props: RelativeRangeDatePickerProps) {
9943
},
10044
});
10145

102-
const {alwaysShowAsAbsolute, presetTabs, getRangeTitle} = props;
103-
const format = props.format || 'L';
104-
const text = React.useMemo(
105-
() =>
106-
typeof getRangeTitle === 'function'
107-
? getRangeTitle(state.value, state.timeZone)
108-
: getDefaultTitle({
109-
value: state.value,
110-
timeZone: state.timeZone,
111-
alwaysShowAsAbsolute: alwaysShowAsAbsolute,
112-
format,
113-
presets: presetTabs?.flatMap(({presets}) => presets),
114-
}),
115-
[alwaysShowAsAbsolute, format, getRangeTitle, presetTabs, state.timeZone, state.value],
116-
);
117-
118-
const validationState = props.validationState || (state.isInvalid ? 'invalid' : undefined);
119-
const errorMessage = props.errorMessage ?? state.errors.join('\n');
120-
12146
return (
12247
<div
12348
ref={anchorRef}
12449
{...focusWithinProps}
12550
className={b(null, props.className)}
12651
style={props.style}
12752
>
128-
<TextInput
129-
id={props.id}
130-
autoFocus={props.autoFocus}
131-
controlRef={inputRef}
132-
value={text}
133-
placeholder={props.placeholder}
134-
onUpdate={(v) => {
135-
if (!props.readOnly && !v) {
136-
state.setValue(null, 'default');
53+
<Control
54+
props={props}
55+
state={state}
56+
open={open}
57+
isMobile={isMobile}
58+
ref={inputRef}
59+
onClick={() => {
60+
if (props.disabled) {
61+
return;
62+
}
63+
if (!open) {
64+
setIsActive(true);
65+
setOpen(true);
13766
}
138-
}}
139-
controlProps={{
140-
role: 'combobox',
141-
'aria-expanded': open,
142-
disabled: isMobile,
143-
readOnly: props.readOnly,
144-
className: b('input', {mobile: isMobile}),
145-
onClick: () => {
146-
if (props.disabled) {
147-
return;
148-
}
149-
if (!open) {
150-
setIsActive(true);
151-
setOpen(true);
152-
}
153-
},
15467
}}
15568
onKeyDown={(e) => {
15669
if (props.disabled) {
@@ -161,52 +74,22 @@ export function RelativeRangeDatePicker(props: RelativeRangeDatePickerProps) {
16174
setOpen(true);
16275
}
16376
}}
77+
onClickCalendar={() => {
78+
setIsActive(true);
79+
setOpen(!open);
80+
}}
16481
onFocus={() => {
16582
if (!isActive) {
16683
setIsActive(true);
16784
setOpen(true);
16885
}
16986
}}
170-
validationState={validationState}
171-
errorMessage={errorMessage}
172-
errorPlacement={props.errorPlacement}
173-
pin={props.pin}
174-
size={props.size}
175-
label={props.label}
176-
hasClear={props.hasClear}
177-
disabled={props.disabled}
178-
endContent={
179-
<Button
180-
view="flat-secondary"
181-
size={getButtonSizeForInput(props.size)}
182-
disabled={props.disabled}
183-
extraProps={{
184-
'aria-haspopup': 'dialog',
185-
'aria-expanded': open,
186-
'aria-label': i18n('Range date picker'),
187-
}}
188-
onClick={() => {
189-
setIsActive(true);
190-
setOpen(!open);
191-
}}
192-
>
193-
<Icon data={CalendarIcon} />
194-
</Button>
195-
}
87+
onUpdate={(v: string) => {
88+
if (!props.readOnly && !v) {
89+
state.setValue(null, 'default');
90+
}
91+
}}
19692
/>
197-
{isMobile ? (
198-
<button
199-
className={b('mobile-trigger', {
200-
'has-clear': Boolean(props.hasClear && state.value),
201-
'has-errors': state.isInvalid && props.errorPlacement === 'inside',
202-
size: props.size,
203-
})}
204-
onClick={() => {
205-
setIsActive(true);
206-
setOpen(true);
207-
}}
208-
/>
209-
) : null}
21093
<HiddenInput
21194
name={props.name}
21295
form={props.form}

src/components/RelativeRangeDatePicker/__stories__/RelativeRangeDatePiker.stories.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {Meta, StoryObj} from '@storybook/react';
99
import {timeZoneControl} from '../../../demo/utils/zones';
1010
import type {Value} from '../../RelativeDatePicker';
1111
import {RelativeRangeDatePicker} from '../RelativeRangeDatePicker';
12-
import type {RelativeRangeDatePickerProps} from '../RelativeRangeDatePicker';
12+
import type {RelativeRangeDatePickerProps} from '../types';
1313

1414
const meta: Meta<typeof RelativeRangeDatePicker> = {
1515
title: 'Components/RelativeRangeDatePicker',
@@ -123,3 +123,21 @@ export const InsideDialog: StoryObj<
123123
},
124124
},
125125
};
126+
127+
export const CustomControl: StoryObj<RelativeRangeDatePickerProps> = {
128+
...Default,
129+
render: (props) => {
130+
return (
131+
<RelativeRangeDatePicker
132+
{...props}
133+
renderControl={({title, onClick, onFocus, ref}) => {
134+
return (
135+
<Button ref={ref} onClick={onClick} onFocus={onFocus}>
136+
{title || 'Not selected'}
137+
</Button>
138+
);
139+
}}
140+
/>
141+
);
142+
},
143+
};
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
@use '../../../variables.scss';
2+
3+
$block: '.#{variables.$ns}relative-range-date-picker-control';
4+
5+
#{$block} {
6+
&__input {
7+
caret-color: transparent;
8+
9+
&_mobile {
10+
pointer-events: none;
11+
}
12+
}
13+
14+
&__mobile-trigger {
15+
--_--g-date-mobile-trigger-clear-width: 0px;
16+
--_--g-date-mobile-trigger-errors-width: 0px;
17+
--_--g-date-mobile-trigger-button-width: 24px;
18+
19+
position: absolute;
20+
inset: 0;
21+
inset-inline-end: calc(
22+
var(--g-spacing-2) + var(--_--g-date-mobile-trigger-button-width) +
23+
var(--_--g-date-mobile-trigger-clear-width) +
24+
var(--_--g-date-mobile-trigger-errors-width)
25+
);
26+
27+
opacity: 0;
28+
29+
&_size_s {
30+
--_--g-date-mobile-trigger-button-width: 20px;
31+
}
32+
&_size_l {
33+
--_--g-date-mobile-trigger-button-width: 28px;
34+
}
35+
&_size_xl {
36+
--_--g-date-mobile-trigger-button-width: 36px;
37+
}
38+
39+
&_has-clear {
40+
--_--g-date-mobile-trigger-clear-width: calc(
41+
var(--_--g-date-mobile-trigger-button-width) + 2px
42+
);
43+
}
44+
&_has-errors {
45+
--_--g-date-mobile-trigger-errors-width: calc(
46+
var(--_--g-date-mobile-trigger-button-width) + 2px
47+
);
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)