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(DateField): correctly work with 2-digits years #22

Merged
merged 2 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
30 changes: 18 additions & 12 deletions src/components/DateField/hooks/useDateFieldProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,40 +176,46 @@ export function useDateFieldProps(
onMouseUp(e: React.MouseEvent) {
e.preventDefault();
},
onBeforeInput(e: React.FormEvent) {
onBeforeInput(e) {
e.preventDefault();
// @ts-expect-error
const key = e.nativeEvent.data;
// eslint-disable-next-line no-eq-null, eqeqeq
if (key != null) {
const key = e.data;
if (key !== undefined && key !== null) {
state.onInput(key);
}
},
onPaste(e: React.ClipboardEvent) {
e.preventDefault();
if (state.readOnly) {
e.preventDefault();
return;
}

const pastedValue = e.clipboardData.getData('text');
if (state.setValueFromString(pastedValue)) {
e.preventDefault();
} else if (
if (
state.selectedSectionIndexes &&
state.selectedSectionIndexes.startIndex ===
state.selectedSectionIndexes.endIndex
) {
const activeSection =
state.sections[state.selectedSectionIndexes.startIndex];

const digitsOnly = /^\d+$/.test(pastedValue);
const lettersOnly = /^[a-zA-Z]+$/.test(pastedValue);

const isValidValue =
Boolean(activeSection) &&
((activeSection.contentType === 'digit' && /^\d+$/.test(pastedValue)) ||
activeSection.contentType === 'letter');
if (!isValidValue) {
e.preventDefault();
((activeSection.contentType === 'digit' && digitsOnly) ||
(activeSection.contentType === 'letter' && lettersOnly));
if (isValidValue) {
state.onInput(pastedValue);
return;
}
if (digitsOnly || lettersOnly) {
return;
}
}

state.setValueFromString(pastedValue);
},
},
},
Expand Down
189 changes: 23 additions & 166 deletions src/components/DateField/hooks/useDateFieldState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@
DateFieldSectionType,
DateFieldSectionWithoutPosition,
} from '../types';
import {splitFormatIntoSections} from '../utils';
import {
addSegment,
getDurationUnitFromSectionType,
getSectionLimits,
getSectionValue,
setSegment,
splitFormatIntoSections,
} from '../utils';

export interface DateFieldStateOptions extends DateFieldBase {}

Expand All @@ -27,12 +34,6 @@
weekday: true,
};

const TYPE_MAPPING = {
weekday: 'day',
day: 'date',
dayPeriod: 'hour',
} as const;

const PAGE_STEP: Partial<Record<DateFieldSectionType, number>> = {
year: 5,
month: 2,
Expand Down Expand Up @@ -121,7 +122,7 @@
sections
.filter((seg) => EDITABLE_SEGMENTS[seg.type])
.reduce<typeof EDITABLE_SEGMENTS>((p, seg) => {
p[seg.type] = true;

Check warning on line 125 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 @@ -194,8 +195,13 @@
}

if (Object.keys(validSegments).length >= Object.keys(allSegments).length) {
setDate(newValue);
if (!value || !newValue.isSame(value)) {
setDate(newValue);
}
} else {
if (value) {
setDate(null);
}
setPlaceholderDate(newValue);
}
}
Expand Down Expand Up @@ -396,9 +402,6 @@
currentValue = displayValue.set(type, placeholder[type]());
}

if (value) {
setDate(null);
}
setValue(currentValue);
},
clearAll() {
Expand Down Expand Up @@ -534,6 +537,7 @@
}
},
setValueFromString(str: string) {
enteredKeys.current = '';
let date = parseDate({input: str, format, timeZone: props.timeZone});
if (isValid(date)) {
if (props.timeZone && !isDateStringWithTimeZone(str)) {
Expand All @@ -560,65 +564,6 @@
return /z$/i.test(str) || /[+-]\d\d:\d\d$/.test(str);
}

function addSegment(section: DateFieldSection, date: DateTime, amount: number) {
let val = section.value ?? 0;
if (section.type === 'dayPeriod') {
val = date.hour() + (date.hour() > 12 ? -12 : 12);
} else {
val = val + amount;
const min = section.minValue;
const max = section.maxValue;
if (typeof min === 'number' && typeof max === 'number') {
const length = max - min + 1;
val = ((val - min + length) % length) + min;
}
}
const type = getDurationUnitFromSectionType(section.type);
return date.set(type, val);
}

function setSegment(section: DateFieldSection, date: DateTime, amount: number) {
const type = section.type;
switch (type) {
case 'day':
case 'weekday':
case 'month':
case 'year': {
return date.set(getDurationUnitFromSectionType(type), amount);
}
case 'dayPeriod': {
const hours = date.hour();
const wasPM = hours >= 12;
const isPM = amount >= 12;
if (isPM === wasPM) {
return date;
}
return date.set('hour', wasPM ? hours - 12 : hours + 12);
}
case 'hour': {
// In 12 hour time, ensure that AM/PM does not change
let sectionAmount = amount;
if (section.minValue === 12 || section.maxValue === 11) {
const hours = date.hour();
const wasPM = hours >= 12;
if (!wasPM && sectionAmount === 12) {
sectionAmount = 0;
}
if (wasPM && sectionAmount < 12) {
sectionAmount += 12;
}
}
return date.set('hour', sectionAmount);
}
case 'minute':
case 'second': {
return date.set(type, amount);
}
}

return date;
}

function getCurrentEditableSectionIndex(
sections: DateFieldSection[],
selectedSections: 'all' | number,
Expand Down Expand Up @@ -690,20 +635,26 @@
let renderedValue = section.placeholder;
if ((isEditable && validSegments[section.type]) || section.type === 'timeZoneName') {
renderedValue = value.format(section.format);
if (
section.contentType === 'digit' &&
renderedValue.length < section.placeholder.length
) {
renderedValue = renderedValue.padStart(section.placeholder.length, '0');
}
}

const sectionLength = renderedValue.length;

const newSection = {
...section,
value: getValue(value, section.type),
value: getSectionValue(section, value),
textValue: renderedValue,
start: position,
end: position + sectionLength,
modified: false,
previousEditableSection,
nextEditableSection: previousEditableSection,
...getSectionLimits(value, section.type, {hour12: isHour12(section)}),
...getSectionLimits(section, value),
};

newSections.push(newSection);
Expand All @@ -726,97 +677,3 @@

return newSections;
}

function getValue(date: DateTime, type: DateFieldSectionType) {
switch (type) {
case 'year':
case 'month':
case 'hour':
case 'minute':
case 'second': {
return date[type]();
}
case 'day': {
return date.date();
}
case 'weekday': {
return date.day();
}
case 'dayPeriod': {
return date.hour() >= 12 ? 12 : 0;
}
}
return undefined;
}

function isHour12(section: DateFieldSectionWithoutPosition) {
if (section.type === 'hour') {
return dateTime().set('hour', 15).format(section.format) !== '15';
}
return false;
}

function getSectionLimits(date: DateTime, type: DateFieldSectionType, options: {hour12: boolean}) {
switch (type) {
case 'year': {
return {
minValue: 1,
maxValue: 9999,
};
}
case 'month': {
return {
minValue: 0,
maxValue: 11,
};
}
case 'weekday': {
return {
minValue: 0,
maxValue: 6,
};
}
case 'day': {
return {
minValue: 1,
maxValue: date ? date.daysInMonth() : 31,
};
}
case 'hour': {
if (options.hour12) {
const isPM = date.hour() >= 12;
return {
minValue: isPM ? 12 : 0,
maxValue: isPM ? 23 : 11,
};
}
return {
minValue: 0,
maxValue: 23,
};
}
case 'minute':
case 'second': {
return {
minValue: 0,
maxValue: 59,
};
}
}
return {};
}

function getDurationUnitFromSectionType(type: DateFieldSectionType) {
if (type === 'literal' || type === 'timeZoneName' || type === 'unknown') {
throw new Error(`${type} section does not have duration unit.`);
}

if (type in TYPE_MAPPING) {
return TYPE_MAPPING[type as keyof typeof TYPE_MAPPING];
}

return type as Exclude<
DateFieldSectionType,
keyof typeof TYPE_MAPPING | 'literal' | 'timeZoneName' | 'unknown'
>;
}
Loading
Loading