Skip to content

Commit 25ffeb3

Browse files
committed
replace moment.js with date-fns
1 parent e11a33d commit 25ffeb3

File tree

10 files changed

+218
-154
lines changed

10 files changed

+218
-154
lines changed

components/dash-core-components/package-lock.json

Lines changed: 11 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/dash-core-components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"highlight.js": "^11.8.0",
5353
"js-search": "^2.0.1",
5454
"mathjax": "^3.2.2",
55-
"moment": "^2.29.4",
55+
"date-fns": "^4.1.0",
5656
"node-polyfill-webpack-plugin": "^2.0.1",
5757
"prop-types": "^15.8.1",
5858
"ramda": "^0.30.1",

components/dash-core-components/src/fragments/DatePickerRange.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ArrowLeftIcon,
88
ArrowRightIcon,
99
} from '@radix-ui/react-icons';
10+
import {addDays, subDays} from 'date-fns';
1011
import AutosizeInput from 'react-input-autosize';
1112
import Calendar, {CalendarHandle} from '../utils/calendar/Calendar';
1213
import {DatePickerRangeProps, CalendarDirection} from '../types';
@@ -19,7 +20,6 @@ import {
1920
} from '../utils/calendar/helpers';
2021
import '../components/css/datepickers.css';
2122
import uuid from 'uniqid';
22-
import moment from 'moment';
2323

2424
const DatePickerRange = ({
2525
id,
@@ -81,12 +81,8 @@ const DatePickerRange = ({
8181
) {
8282
const minimumNightsDates: Date[] = [];
8383
for (let i = 1; i < minimum_nights; i++) {
84-
minimumNightsDates.push(
85-
moment(internalStartDate).add(i, 'day').toDate()
86-
);
87-
minimumNightsDates.push(
88-
moment(internalStartDate).subtract(i, 'day').toDate()
89-
);
84+
minimumNightsDates.push(addDays(internalStartDate, i));
85+
minimumNightsDates.push(subDays(internalStartDate, i));
9086
}
9187
return [...baseDates, ...minimumNightsDates];
9288
}
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import moment from 'moment';
1+
import {format, startOfDay, parseISO} from 'date-fns';
22
import {isNil} from 'ramda';
33

44
export default {
55
extract: (propValue?: string) => {
6-
if (!isNil(propValue)) {
7-
return moment(propValue).startOf('day').format('YYYY-MM-DD');
6+
if (isNil(propValue)) {
7+
return propValue;
88
}
9-
return propValue;
9+
10+
const parsed = parseISO(propValue);
11+
return format(startOfDay(parsed), 'yyyy-MM-dd');
1012
},
1113
apply: (storedValue?: string) => storedValue,
1214
};

components/dash-core-components/src/utils/calendar/Calendar.tsx

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import React, {
77
useState,
88
forwardRef,
99
} from 'react';
10-
import moment from 'moment';
10+
import {addMonths, subMonths} from 'date-fns';
1111
import {
1212
ArrowUpIcon,
1313
ArrowDownIcon,
@@ -92,7 +92,7 @@ const CalendarComponent = ({
9292
}, [activeYear, monthFormat]);
9393

9494
useImperativeHandle(forwardedRef, () => ({
95-
focusDate: (date = moment([activeYear, activeMonth, 1]).toDate()) => {
95+
focusDate: (date = new Date(activeYear, activeMonth, 1)) => {
9696
setFocusedDate(date);
9797
},
9898
setVisibleDate: (date: Date) => {
@@ -112,15 +112,9 @@ const CalendarComponent = ({
112112
prevFocusedDateRef.current = focusedDate;
113113

114114
const halfRange = Math.floor((numberOfMonthsShown - 1) / 2);
115-
const activeMonthStart = moment([activeYear, activeMonth, 1]);
116-
const visibleStart = activeMonthStart
117-
.clone()
118-
.subtract(halfRange, 'months')
119-
.toDate();
120-
const visibleEnd = activeMonthStart
121-
.clone()
122-
.add(halfRange, 'months')
123-
.toDate();
115+
const activeMonthStart = new Date(activeYear, activeMonth, 1);
116+
const visibleStart = subMonths(activeMonthStart, halfRange);
117+
const visibleEnd = addMonths(activeMonthStart, halfRange);
124118

125119
const focusedMonthStart = new Date(
126120
focusedDate.getFullYear(),
@@ -220,18 +214,17 @@ const CalendarComponent = ({
220214

221215
const changeMonthBy = useCallback(
222216
(months: number) => {
223-
const currentDate = moment([activeYear, activeMonth, 1]);
217+
const currentDate = new Date(activeYear, activeMonth, 1);
224218

225219
// In RTL mode, directions are reversed
226220
const actualMonths =
227221
direction === CalendarDirection.RightToLeft ? -months : months;
228222

229-
const newDate = currentDate.clone().add(actualMonths, 'month');
230-
const newMonthStart = newDate.toDate();
223+
const newMonthStart = addMonths(currentDate, actualMonths);
231224

232225
if (isDateInRange(newMonthStart, minDateAllowed, maxDateAllowed)) {
233-
setActiveYear(newDate.year());
234-
setActiveMonth(newDate.month());
226+
setActiveYear(newMonthStart.getFullYear());
227+
setActiveMonth(newMonthStart.getMonth());
235228
}
236229
},
237230
[activeYear, activeMonth, minDateAllowed, maxDateAllowed, direction]
@@ -272,11 +265,8 @@ const CalendarComponent = ({
272265

273266
const canChangeMonthBy = useCallback(
274267
(months: number) => {
275-
const currentDate = moment([activeYear, activeMonth, 1]);
276-
const targetMonth = currentDate
277-
.clone()
278-
.add(months, 'month')
279-
.toDate();
268+
const currentDate = new Date(activeYear, activeMonth, 1);
269+
const targetMonth = addMonths(currentDate, months);
280270

281271
return isDateInRange(targetMonth, minDateAllowed, maxDateAllowed);
282272
},
@@ -350,15 +340,15 @@ const CalendarComponent = ({
350340
// Center the view: start from (numberOfMonthsShown - 1) / 2 months before activeMonth
351341
const offset =
352342
i - Math.floor((numberOfMonthsShown - 1) / 2);
353-
const monthDate = moment([activeYear, activeMonth, 1]).add(
354-
offset,
355-
'months'
343+
const monthDate = addMonths(
344+
new Date(activeYear, activeMonth, 1),
345+
offset
356346
);
357347
return (
358348
<CalendarMonth
359349
key={i}
360-
year={monthDate.year()}
361-
month={monthDate.month()}
350+
year={monthDate.getFullYear()}
351+
month={monthDate.getMonth()}
362352
minDateAllowed={minDateAllowed}
363353
maxDateAllowed={maxDateAllowed}
364354
disabledDates={disabledDates}

components/dash-core-components/src/utils/calendar/CalendarMonth.tsx

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
import React, {useCallback, useMemo} from 'react';
2-
import moment from 'moment';
2+
import {
3+
addDays,
4+
subDays,
5+
addWeeks,
6+
subWeeks,
7+
addMonths,
8+
subMonths,
9+
setDay,
10+
startOfWeek,
11+
endOfWeek,
12+
format,
13+
} from 'date-fns';
14+
import type {Day} from 'date-fns';
315
import CalendarDay from './CalendarDay';
416
import CalendarDayPadding from './CalendarDayPadding';
517
import {createMonthGrid} from './createMonthGrid';
@@ -26,7 +38,7 @@ type CalendarMonthProps = {
2638
onSelectionEnd?: (date: Date) => void;
2739
onDayFocused?: (date: Date) => void;
2840
onDaysHighlighted?: (date: Date) => void;
29-
firstDayOfWeek?: number; // 0-7
41+
firstDayOfWeek?: Day;
3042
showOutsideDays?: boolean;
3143
daySize?: number;
3244
monthFormat?: string;
@@ -80,17 +92,15 @@ export const CalendarMonth = ({
8092
);
8193

8294
const daysOfTheWeek = useMemo(() => {
83-
return Array.from({length: 7}, (_, i) =>
84-
moment()
85-
.day((i + firstDayOfWeek) % 7)
86-
.format('dd')
87-
);
95+
return Array.from({length: 7}, (_, i) => {
96+
const date = setDay(new Date(), (i + firstDayOfWeek) % 7);
97+
return format(date, 'EEEEEE');
98+
});
8899
}, [firstDayOfWeek]);
89100

90101
const handleKeyDown = useCallback(
91102
(e: React.KeyboardEvent, date: Date) => {
92-
const m = moment(date);
93-
let newDate: moment.Moment | null = null;
103+
let newDate: Date | null = null;
94104

95105
switch (e.key) {
96106
case ' ':
@@ -107,52 +117,41 @@ export const CalendarMonth = ({
107117
case 'ArrowRight':
108118
newDate =
109119
direction === CalendarDirection.RightToLeft
110-
? m.subtract(1, 'day')
111-
: m.add(1, 'day');
120+
? subDays(date, 1)
121+
: addDays(date, 1);
112122
break;
113123
case 'ArrowLeft':
114124
newDate =
115125
direction === CalendarDirection.RightToLeft
116-
? m.add(1, 'day')
117-
: m.subtract(1, 'day');
126+
? addDays(date, 1)
127+
: subDays(date, 1);
118128
break;
119129
case 'ArrowDown':
120-
newDate = m.add(1, 'week');
130+
newDate = addWeeks(date, 1);
121131
break;
122132
case 'ArrowUp':
123-
newDate = m.subtract(1, 'week');
133+
newDate = subWeeks(date, 1);
124134
break;
125135
case 'PageDown':
126-
newDate = m.add(1, 'month');
136+
newDate = addMonths(date, 1);
127137
break;
128138
case 'PageUp':
129-
newDate = m.subtract(1, 'month');
139+
newDate = subMonths(date, 1);
130140
break;
131141
case 'Home':
132-
// Navigate to week start (respecting firstDayOfWeek)
133-
newDate = m.clone().day(firstDayOfWeek);
134-
// If we went forward, adjust backward to current week
135-
if (newDate.isAfter(m, 'day')) {
136-
newDate.subtract(1, 'week');
137-
}
142+
newDate = startOfWeek(date, {weekStartsOn: firstDayOfWeek});
138143
break;
139144
case 'End':
140-
// Navigate to week end (respecting firstDayOfWeek)
141-
newDate = m.clone().day((firstDayOfWeek + 6) % 7);
142-
// If we went backward, adjust forward to current week
143-
if (newDate.isBefore(m, 'day')) {
144-
newDate.add(1, 'week');
145-
}
145+
newDate = endOfWeek(date, {weekStartsOn: firstDayOfWeek});
146146
break;
147147
default:
148148
return;
149149
}
150150

151151
if (newDate) {
152152
e.preventDefault();
153-
const newDateObj = newDate.toDate();
154-
if (isDateInRange(newDateObj, minDateAllowed, maxDateAllowed)) {
155-
onDayFocused?.(newDateObj);
153+
if (isDateInRange(newDate, minDateAllowed, maxDateAllowed)) {
154+
onDayFocused?.(newDate);
156155
}
157156
}
158157
},
Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import moment from 'moment';
1+
import {getDay, getDaysInMonth, addDays, getMonth, type Day} from 'date-fns';
22

33
/**
44
* Creates a 2D array of Date objects representing a calendar month grid.
@@ -7,33 +7,22 @@ import moment from 'moment';
77
export const createMonthGrid = (
88
year: number,
99
month: number,
10-
firstDayOfWeek: number,
10+
firstDayOfWeek: Day,
1111
showOutsideDays = true
1212
): (Date | null)[][] => {
13-
const firstDay = moment([year, month, 1]);
14-
const offset = (firstDay.day() - firstDayOfWeek + 7) % 7;
15-
const daysInMonth = firstDay.daysInMonth();
16-
const weeksNeeded = Math.ceil((offset + daysInMonth) / 7);
17-
const startDate = firstDay.clone().subtract(offset, 'days');
13+
const firstDay = new Date(year, month, 1);
14+
const offset = (getDay(firstDay) - firstDayOfWeek + 7) % 7;
15+
const startDate = addDays(firstDay, -offset);
16+
const weeksNeeded = Math.ceil((offset + getDaysInMonth(firstDay)) / 7);
1817

19-
const grid: (Date | null)[][] = [];
18+
return Array.from({length: 6}, (_, week) =>
19+
Array.from({length: 7}, (_, day) => {
20+
if (week >= weeksNeeded) {
21+
return null;
22+
}
2023

21-
for (let week = 0; week < weeksNeeded; week++) {
22-
grid.push(
23-
Array.from({length: 7}, (_, day) => {
24-
const date = startDate.clone().add(week * 7 + day, 'days');
25-
if (!showOutsideDays && date.month() !== month) {
26-
return null;
27-
}
28-
return date.toDate();
29-
})
30-
);
31-
}
32-
33-
// Pad with empty rows to always have 6 rows total
34-
while (grid.length < 6) {
35-
grid.push(Array.from({length: 7}, () => null));
36-
}
37-
38-
return grid;
24+
const date = addDays(startDate, week * 7 + day);
25+
return showOutsideDays || getMonth(date) === month ? date : null;
26+
})
27+
);
3928
};

0 commit comments

Comments
 (0)