Skip to content

Commit d7d1d71

Browse files
committed
feat!: support quarters, show callendar according to format
- support quarters in format - use calendar modes in pickers according to format - reset value of components missing from format
1 parent 3c64ae3 commit d7d1d71

20 files changed

+274
-109
lines changed

src/components/DateField/hooks/useBaseDateFieldState.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {DateTime} from '@gravity-ui/date-utils';
44

55
import type {ValidationState} from '../../types';
66
import {createPlaceholderValue} from '../../utils/dates';
7-
import type {DateFieldSection, DateFieldSectionType} from '../types';
7+
import type {DateFieldSection, DateFieldSectionType, FormatInfo} from '../types';
88
import {
99
EDITABLE_SEGMENTS,
1010
formatSections,
@@ -14,6 +14,7 @@ import {
1414

1515
const PAGE_STEP: Partial<Record<DateFieldSectionType, number>> = {
1616
year: 5,
17+
quarter: 2,
1718
month: 2,
1819
weekday: 3,
1920
day: 7,
@@ -29,6 +30,7 @@ export type BaseDateFieldStateOptions<T = DateTime> = {
2930
timeZone: string;
3031
validationState?: ValidationState;
3132
editableSections: DateFieldSection[];
33+
formatInfo: FormatInfo;
3234
readOnly?: boolean;
3335
disabled?: boolean;
3436
selectedSectionIndexes: {startIndex: number; endIndex: number} | null;
@@ -66,9 +68,17 @@ export type DateFieldState<T = DateTime> = {
6668
disabled?: boolean;
6769
/** A list of segments for the current value. */
6870
sections: DateFieldSection[];
69-
/** Whether the the format is containing date parts */
71+
/** Some info about available sections */
72+
formatInfo: FormatInfo;
73+
/**
74+
* @deprecated use formatInfo.hasDate instead.
75+
* Whether the the format is containing date parts
76+
*/
7077
hasDate: boolean;
71-
/** Whether the the format is containing time parts */
78+
/**
79+
* @deprecated use formatInfo.hasTime instead.
80+
* Whether the the format is containing time parts
81+
*/
7282
hasTime: boolean;
7383
/** Selected sections */
7484
selectedSectionIndexes: {startIndex: number; endIndex: number} | null;
@@ -122,6 +132,7 @@ export function useBaseDateFieldState<T = DateTime>(
122132
validationState,
123133
displayValue,
124134
editableSections,
135+
formatInfo,
125136
selectedSectionIndexes,
126137
selectedSections,
127138
isEmpty,
@@ -140,19 +151,6 @@ export function useBaseDateFieldState<T = DateTime>(
140151

141152
const enteredKeys = React.useRef('');
142153

143-
const {hasDate, hasTime} = React.useMemo(() => {
144-
let hasDateInner = false;
145-
let hasTimeInner = false;
146-
for (const s of editableSections) {
147-
hasTimeInner ||= ['hour', 'minute', 'second'].includes(s.type);
148-
hasDateInner ||= ['day', 'month', 'year'].includes(s.type);
149-
}
150-
return {
151-
hasTime: hasTimeInner,
152-
hasDate: hasDateInner,
153-
};
154-
}, [editableSections]);
155-
156154
return {
157155
value,
158156
isEmpty,
@@ -163,8 +161,9 @@ export function useBaseDateFieldState<T = DateTime>(
163161
readOnly: props.readOnly,
164162
disabled: props.disabled,
165163
sections: editableSections,
166-
hasDate,
167-
hasTime,
164+
formatInfo,
165+
hasDate: formatInfo.hasDate,
166+
hasTime: formatInfo.hasTime,
168167
selectedSectionIndexes,
169168
validationState,
170169
setSelectedSections(position) {
@@ -425,6 +424,7 @@ export function useBaseDateFieldState<T = DateTime>(
425424
case 'hour':
426425
case 'minute':
427426
case 'second':
427+
case 'quarter':
428428
case 'year': {
429429
if (!Number.isInteger(Number(newValue))) {
430430
return;

src/components/DateField/hooks/useDateFieldState.ts

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@ import {useControlledState} from '@gravity-ui/uikit';
66
import type {DateFieldBase} from '../../types/datePicker';
77
import {createPlaceholderValue, isInvalid} from '../../utils/dates';
88
import {useDefaultTimeZone} from '../../utils/useDefaultTimeZone';
9-
import type {DateFieldSectionType, DateFieldSectionWithoutPosition} from '../types';
9+
import type {
10+
AvailableSections,
11+
DateFieldSectionType,
12+
DateFieldSectionWithoutPosition,
13+
} from '../types';
1014
import {
11-
EDITABLE_SEGMENTS,
1215
addSegment,
16+
adjustDateToFormat,
1317
getEditableSections,
18+
getFormatInfo,
1419
isAllSegmentsValid,
20+
markValidSection,
1521
parseDateFromString,
1622
setSegment,
1723
useFormatSections,
@@ -47,15 +53,10 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
4753

4854
const format = props.format || 'L';
4955
const sections = useFormatSections(format);
50-
const allSegments: typeof EDITABLE_SEGMENTS = React.useMemo(
51-
() =>
52-
sections
53-
.filter((seg) => EDITABLE_SEGMENTS[seg.type])
54-
.reduce<typeof EDITABLE_SEGMENTS>((p, seg) => ({...p, [seg.type]: true}), {}),
55-
[sections],
56-
);
56+
const formatInfo = React.useMemo(() => getFormatInfo(sections), [sections]);
57+
const allSegments = formatInfo.availableUnits;
5758

58-
const validSegmentsState = React.useState<typeof EDITABLE_SEGMENTS>(() =>
59+
const validSegmentsState = React.useState<AvailableSections>(() =>
5960
value ? {...allSegments} : {},
6061
);
6162

@@ -127,7 +128,7 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
127128

128129
if (isAllSegmentsValid(allSegments, validSegments)) {
129130
if (!value || !newValue.isSame(value)) {
130-
handleUpdateDate(newValue);
131+
handleUpdateDate(adjustDateToFormat(newValue, formatInfo));
131132
}
132133
} else {
133134
if (value) {
@@ -138,13 +139,7 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
138139
}
139140

140141
function markValid(part: DateFieldSectionType) {
141-
validSegments[part] = true;
142-
if (validSegments.day && validSegments.month && validSegments.year && allSegments.weekday) {
143-
validSegments.weekday = true;
144-
}
145-
if (validSegments.hour && allSegments.dayPeriod) {
146-
validSegments.dayPeriod = true;
147-
}
142+
validSegments = markValidSection(allSegments, validSegments, part);
148143
setValidSegments({...validSegments});
149144
}
150145

@@ -219,6 +214,7 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
219214
timeZone,
220215
validationState,
221216
editableSections: sectionsState.editableSections,
217+
formatInfo,
222218
readOnly: props.readOnly,
223219
disabled: props.disabled,
224220
selectedSectionIndexes,
@@ -241,7 +237,7 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
241237
function useSectionsState(
242238
sections: DateFieldSectionWithoutPosition[],
243239
value: DateTime,
244-
validSegments: typeof EDITABLE_SEGMENTS,
240+
validSegments: AvailableSections,
245241
) {
246242
const [state, setState] = React.useState(() => {
247243
return {

src/components/DateField/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"year_placeholder": "Y",
3+
"quarter_placeholder": "Q",
34
"month_placeholder": "M",
45
"weekday_placeholder": "E",
56
"day_placeholder": "D",

src/components/DateField/i18n/ru.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"year_placeholder": "Г",
3+
"quarter_placeholder": "K",
34
"month_placeholder": "М",
45
"weekday_placeholder": "ДН",
56
"day_placeholder": "Д",

src/components/DateField/types.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
export type DateFieldSectionType = Extract<
2-
Intl.DateTimeFormatPartTypes,
3-
| 'day'
4-
| 'dayPeriod'
5-
| 'hour'
6-
| 'literal'
7-
| 'minute'
8-
| 'month'
9-
| 'second'
10-
| 'timeZoneName'
11-
| 'weekday'
12-
| 'year'
13-
| 'unknown'
14-
>;
1+
export type DateFieldSectionType =
2+
| Extract<
3+
Intl.DateTimeFormatPartTypes,
4+
| 'day'
5+
| 'dayPeriod'
6+
| 'hour'
7+
| 'literal'
8+
| 'minute'
9+
| 'month'
10+
| 'second'
11+
| 'timeZoneName'
12+
| 'weekday'
13+
| 'year'
14+
| 'unknown'
15+
>
16+
| 'quarter';
1517

1618
export type DateFormatTokenMap = {
1719
[formatToken: string]:
@@ -91,3 +93,13 @@ export type DateFieldSectionWithoutPosition<TSection extends DateFieldSection =
9193
| 'previousEditableSection'
9294
| 'nextEditableSection'
9395
>;
96+
97+
export type AvailableSections = Partial<Record<DateFieldSectionType, boolean>>;
98+
99+
export interface FormatInfo {
100+
hasTime: boolean;
101+
hasDate: boolean;
102+
availableUnits: AvailableSections;
103+
minDateUnit: 'day' | 'month' | 'quarter' | 'year';
104+
minTimeUnit: 'second' | 'minute' | 'hour';
105+
}

0 commit comments

Comments
 (0)