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

feat: support quarters, show callendar according to format #164

Merged
merged 2 commits into from
Jan 27, 2025
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
36 changes: 18 additions & 18 deletions src/components/DateField/hooks/useBaseDateFieldState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {DateTime} from '@gravity-ui/date-utils';

import type {ValidationState} from '../../types';
import {createPlaceholderValue} from '../../utils/dates';
import type {DateFieldSection, DateFieldSectionType} from '../types';
import type {DateFieldSection, DateFieldSectionType, FormatInfo} from '../types';
import {
EDITABLE_SEGMENTS,
formatSections,
Expand All @@ -14,6 +14,7 @@ import {

const PAGE_STEP: Partial<Record<DateFieldSectionType, number>> = {
year: 5,
quarter: 2,
month: 2,
weekday: 3,
day: 7,
Expand All @@ -29,6 +30,7 @@ export type BaseDateFieldStateOptions<T = DateTime> = {
timeZone: string;
validationState?: ValidationState;
editableSections: DateFieldSection[];
formatInfo: FormatInfo;
readOnly?: boolean;
disabled?: boolean;
selectedSectionIndexes: {startIndex: number; endIndex: number} | null;
Expand Down Expand Up @@ -66,9 +68,17 @@ export type DateFieldState<T = DateTime> = {
disabled?: boolean;
/** A list of segments for the current value. */
sections: DateFieldSection[];
/** Whether the the format is containing date parts */
/** Some info about available sections */
formatInfo: FormatInfo;
/**
* @deprecated use formatInfo.hasDate instead.
* Whether the the format is containing date parts
*/
hasDate: boolean;
/** Whether the the format is containing time parts */
/**
* @deprecated use formatInfo.hasTime instead.
* Whether the the format is containing time parts
*/
hasTime: boolean;
/** Selected sections */
selectedSectionIndexes: {startIndex: number; endIndex: number} | null;
Expand Down Expand Up @@ -122,6 +132,7 @@ export function useBaseDateFieldState<T = DateTime>(
validationState,
displayValue,
editableSections,
formatInfo,
selectedSectionIndexes,
selectedSections,
isEmpty,
Expand All @@ -140,19 +151,6 @@ export function useBaseDateFieldState<T = DateTime>(

const enteredKeys = React.useRef('');

const {hasDate, hasTime} = React.useMemo(() => {
let hasDateInner = false;
let hasTimeInner = false;
for (const s of editableSections) {
hasTimeInner ||= ['hour', 'minute', 'second'].includes(s.type);
hasDateInner ||= ['day', 'month', 'year'].includes(s.type);
}
return {
hasTime: hasTimeInner,
hasDate: hasDateInner,
};
}, [editableSections]);

return {
value,
isEmpty,
Expand All @@ -163,8 +161,9 @@ export function useBaseDateFieldState<T = DateTime>(
readOnly: props.readOnly,
disabled: props.disabled,
sections: editableSections,
hasDate,
hasTime,
formatInfo,
hasDate: formatInfo.hasDate,
hasTime: formatInfo.hasTime,
selectedSectionIndexes,
validationState,
setSelectedSections(position) {
Expand Down Expand Up @@ -425,6 +424,7 @@ export function useBaseDateFieldState<T = DateTime>(
case 'hour':
case 'minute':
case 'second':
case 'quarter':
case 'year': {
if (!Number.isInteger(Number(newValue))) {
return;
Expand Down
34 changes: 15 additions & 19 deletions src/components/DateField/hooks/useDateFieldState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ import {useControlledState} from '@gravity-ui/uikit';
import type {DateFieldBase} from '../../types/datePicker';
import {createPlaceholderValue, isInvalid} from '../../utils/dates';
import {useDefaultTimeZone} from '../../utils/useDefaultTimeZone';
import type {DateFieldSectionType, DateFieldSectionWithoutPosition} from '../types';
import type {
AvailableSections,
DateFieldSectionType,
DateFieldSectionWithoutPosition,
} from '../types';
import {
EDITABLE_SEGMENTS,
addSegment,
adjustDateToFormat,
getEditableSections,
getFormatInfo,
isAllSegmentsValid,
markValidSection,
parseDateFromString,
setSegment,
useFormatSections,
Expand Down Expand Up @@ -47,15 +53,10 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState

const format = props.format || 'L';
const sections = useFormatSections(format);
const allSegments: typeof EDITABLE_SEGMENTS = React.useMemo(
() =>
sections
.filter((seg) => EDITABLE_SEGMENTS[seg.type])
.reduce<typeof EDITABLE_SEGMENTS>((p, seg) => ({...p, [seg.type]: true}), {}),
[sections],
);
const formatInfo = React.useMemo(() => getFormatInfo(sections), [sections]);
const allSegments = formatInfo.availableUnits;

const validSegmentsState = React.useState<typeof EDITABLE_SEGMENTS>(() =>
const validSegmentsState = React.useState<AvailableSections>(() =>
value ? {...allSegments} : {},
);

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

if (isAllSegmentsValid(allSegments, validSegments)) {
if (!value || !newValue.isSame(value)) {
handleUpdateDate(newValue);
handleUpdateDate(adjustDateToFormat(newValue, formatInfo));
}
} else {
if (value) {
Expand All @@ -138,13 +139,7 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
}

function markValid(part: DateFieldSectionType) {
validSegments[part] = true;
if (validSegments.day && validSegments.month && validSegments.year && allSegments.weekday) {
validSegments.weekday = true;
}
if (validSegments.hour && allSegments.dayPeriod) {
validSegments.dayPeriod = true;
}
validSegments = markValidSection(allSegments, validSegments, part);
setValidSegments({...validSegments});
}

Expand Down Expand Up @@ -219,6 +214,7 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
timeZone,
validationState,
editableSections: sectionsState.editableSections,
formatInfo,
readOnly: props.readOnly,
disabled: props.disabled,
selectedSectionIndexes,
Expand All @@ -241,7 +237,7 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
function useSectionsState(
sections: DateFieldSectionWithoutPosition[],
value: DateTime,
validSegments: typeof EDITABLE_SEGMENTS,
validSegments: AvailableSections,
) {
const [state, setState] = React.useState(() => {
return {
Expand Down
1 change: 1 addition & 0 deletions src/components/DateField/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"year_placeholder": "Y",
"quarter_placeholder": "Q",
"month_placeholder": "M",
"weekday_placeholder": "E",
"day_placeholder": "D",
Expand Down
1 change: 1 addition & 0 deletions src/components/DateField/i18n/ru.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"year_placeholder": "Г",
"quarter_placeholder": "K",
"month_placeholder": "М",
"weekday_placeholder": "ДН",
"day_placeholder": "Д",
Expand Down
40 changes: 26 additions & 14 deletions src/components/DateField/types.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
export type DateFieldSectionType = Extract<
Intl.DateTimeFormatPartTypes,
| 'day'
| 'dayPeriod'
| 'hour'
| 'literal'
| 'minute'
| 'month'
| 'second'
| 'timeZoneName'
| 'weekday'
| 'year'
| 'unknown'
>;
export type DateFieldSectionType =
| Extract<
Intl.DateTimeFormatPartTypes,
| 'day'
| 'dayPeriod'
| 'hour'
| 'literal'
| 'minute'
| 'month'
| 'second'
| 'timeZoneName'
| 'weekday'
| 'year'
| 'unknown'
>
| 'quarter';

export type DateFormatTokenMap = {
[formatToken: string]:
Expand Down Expand Up @@ -91,3 +93,13 @@ export type DateFieldSectionWithoutPosition<TSection extends DateFieldSection =
| 'previousEditableSection'
| 'nextEditableSection'
>;

export type AvailableSections = Partial<Record<DateFieldSectionType, boolean>>;

export interface FormatInfo {
hasTime: boolean;
hasDate: boolean;
availableUnits: AvailableSections;
minDateUnit: 'day' | 'month' | 'quarter' | 'year';
minTimeUnit: 'second' | 'minute' | 'hour';
}
Loading
Loading