` with the **names of the days of the week in short form**, starting from **Sunday to Saturday**.
@@ -86,6 +86,10 @@ To customize the Date Picker content footer you can use the [`footerElement`](#a
If you want to expand or close the picker from outside of the component, use the props `expand` and `onExpandChange`.
+## Note about Moment.js
+
+In version 3.4.0, we made the decision to switch to use [date-fns](https://date-fns.org/v2.14.0) instead of Moment.js due to dependency size. Making this changed help reduce the size of @clayui/date-picker by almost 50 KB.
+
## API
[APITable "clay-date-picker/src/index.tsx"]
diff --git a/packages/clay-date-picker/docs/index.js b/packages/clay-date-picker/docs/index.js
index 78a5832d00..9a6bfd3ec8 100644
--- a/packages/clay-date-picker/docs/index.js
+++ b/packages/clay-date-picker/docs/index.js
@@ -50,7 +50,7 @@ const DatePickerLocale = () => {
return (
= ({
}) => {
const memoizedYears: Array = React.useMemo(
() =>
- Helpers.range(years).map((elem) => {
+ range(years).map((elem) => {
return {
label: elem,
value: elem,
@@ -69,7 +68,7 @@ const ClayDatePickerDateNavigation: React.FunctionComponent = ({
* years in the range
*/
function handleChangeMonth(month: number) {
- const date = moment(currentMonth).clone().add(month, 'M').toDate();
+ const date = addMonths(currentMonth, month);
const year = date.getFullYear();
if (memoizedYears.find((elem) => elem.value === year)) {
diff --git a/packages/clay-date-picker/src/DayNumber.tsx b/packages/clay-date-picker/src/DayNumber.tsx
index 9c25557c20..ae3819857c 100644
--- a/packages/clay-date-picker/src/DayNumber.tsx
+++ b/packages/clay-date-picker/src/DayNumber.tsx
@@ -4,10 +4,9 @@
*/
import classnames from 'classnames';
-import moment from 'moment';
import React from 'react';
-import {IDay} from './Helpers';
+import {IDay, formatDate, setDate} from './Helpers';
interface IProps {
day: IDay;
@@ -25,27 +24,25 @@ const ClayDatePickerDayNumber: React.FunctionComponent = ({
const classNames = classnames(
'date-picker-date date-picker-calendar-item',
{
- active:
- moment(day.date).format('YYYY-MM-DD') ===
- moment(daySelected).format('YYYY-MM-DD'),
+ active: day.date.toDateString() === daySelected.toDateString(),
disabled: day.outside || disabled,
}
);
- const handleClick = () => onClick(day.date);
-
return (
onClick(day.date)}
type="button"
>
{day.date.getDate()}
diff --git a/packages/clay-date-picker/src/Helpers.ts b/packages/clay-date-picker/src/Helpers.ts
index b7f098910c..44fdbb2275 100644
--- a/packages/clay-date-picker/src/Helpers.ts
+++ b/packages/clay-date-picker/src/Helpers.ts
@@ -3,7 +3,10 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
-import moment from 'moment';
+import {default as formatDate} from 'date-fns/format';
+import {default as parseDate} from 'date-fns/parse';
+
+export {formatDate, parseDate};
export interface IDay {
date: Date;
@@ -17,8 +20,8 @@ export type Month = Array;
/**
* Clone a date object.
*/
-export function clone(d: Date) {
- return new Date(d.getTime());
+export function clone(date: number | Date) {
+ return new Date(date instanceof Date ? date.getTime() : date);
}
export function getDaysInMonth(d: Date) {
@@ -103,17 +106,36 @@ export function range({end, start}: {end: number; start: number}) {
);
}
-/**
- * Helper function for getting certain props from `moment`.
- * This allows users to not have to import and use `moment` themselves.
- */
-export function getLocaleProps(locale: string) {
- const localeData = moment.localeData(locale);
-
- return {
- dateFormat: localeData.longDateFormat('L'),
- firstDayOfWeek: localeData.firstDayOfWeek(),
- months: localeData.months(),
- weekdaysShort: localeData.weekdaysShort(),
- };
+export function addMonths(date: number | Date, months: number) {
+ date = clone(date);
+
+ date.setMonth(date.getMonth() + months);
+
+ return date;
+}
+
+export function setDate(
+ date: Date,
+ obj: {
+ date?: number | string;
+ seconds?: number | string;
+ milliseconds?: number | string;
+ hours?: number | string;
+ minutes?: number | string;
+ year?: number | string;
+ }
+) {
+ date = clone(date);
+
+ return Object.keys(obj).reduce((acc, key) => {
+ const method = `set${key.charAt(0).toUpperCase() + key.slice(1)}`;
+ // @ts-ignore
+ acc[method](obj[key]);
+
+ return acc;
+ }, date);
+}
+
+export function isValid(date: Date) {
+ return date instanceof Date && !isNaN(date.getTime());
}
diff --git a/packages/clay-date-picker/src/Hooks.ts b/packages/clay-date-picker/src/Hooks.ts
index a39371bfed..af31e7a07f 100644
--- a/packages/clay-date-picker/src/Hooks.ts
+++ b/packages/clay-date-picker/src/Hooks.ts
@@ -3,25 +3,24 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
-import moment from 'moment';
import React from 'react';
-import * as Helpers from './Helpers';
+import {Month, formatDate, getWeekArray, setDate} from './Helpers';
import {FirstDayOfWeek} from './types';
/**
* Generates the table of days of the month.
*/
const useWeeks = (currentMonth: Date, firstDayOfWeek: FirstDayOfWeek) => {
- const [weeks, set] = React.useState(() =>
- Helpers.getWeekArray(currentMonth, firstDayOfWeek)
+ const [weeks, set] = React.useState(() =>
+ getWeekArray(currentMonth, firstDayOfWeek)
);
function setWeeks(value: Date) {
- set(Helpers.getWeekArray(value, firstDayOfWeek));
+ set(getWeekArray(value, firstDayOfWeek));
}
- return [weeks, setWeeks] as [Helpers.Month, (value: Date) => void];
+ return [weeks, setWeeks] as [Month, (value: Date) => void];
};
/**
@@ -29,19 +28,21 @@ const useWeeks = (currentMonth: Date, firstDayOfWeek: FirstDayOfWeek) => {
*/
const useCurrentTime = (format: string) => {
const [currentTime, set] = React.useState(() =>
- moment().set('h', 0).set('m', 0).format(format)
+ formatDate(setDate(new Date(), {hours: 0, minutes: 0}), format)
);
function setCurrentTime(
hours: number | string,
minutes: number | string
): void {
+ const date = setDate(new Date(), {hours, minutes});
+
if (typeof hours !== 'string') {
- hours = moment().set('h', hours).format('H');
+ hours = formatDate(date, 'H');
}
if (typeof minutes !== 'string') {
- minutes = moment().set('m', minutes).format('m');
+ minutes = formatDate(date, 'm');
}
set(`${hours}:${minutes}`);
diff --git a/packages/clay-date-picker/src/InputDate.tsx b/packages/clay-date-picker/src/InputDate.tsx
index 43f34f6b31..a01f6bc9d3 100644
--- a/packages/clay-date-picker/src/InputDate.tsx
+++ b/packages/clay-date-picker/src/InputDate.tsx
@@ -4,9 +4,10 @@
*/
import {ClayInput} from '@clayui/form';
-import moment from 'moment';
import React from 'react';
+import {formatDate, isValid} from './Helpers';
+
interface IProps extends React.HTMLAttributes {
ariaLabel?: string;
currentTime: string;
@@ -15,7 +16,6 @@ interface IProps extends React.HTMLAttributes {
inputName?: string;
placeholder?: string;
time: boolean;
- timeFormat: string;
useNative: boolean;
value: string;
}
@@ -28,7 +28,6 @@ const ClayDatePickerInputDate = React.forwardRef(
dateFormat,
inputName = 'datePicker',
time = false,
- timeFormat,
useNative = false,
value = '',
...otherProps
@@ -36,10 +35,8 @@ const ClayDatePickerInputDate = React.forwardRef(
ref
) => {
const isValidValue = (value: string | Date): string => {
- const format = time ? `${dateFormat} ${timeFormat}` : dateFormat;
-
- if (moment(value, format).isValid() && value instanceof Date) {
- const date = moment(value).clone().format(dateFormat);
+ if (value instanceof Date && isValid(value)) {
+ const date = formatDate(value, dateFormat);
return time ? `${date} ${currentTime}` : date;
}
diff --git a/packages/clay-date-picker/src/index.tsx b/packages/clay-date-picker/src/index.tsx
index dd7832e27b..5224e278cb 100644
--- a/packages/clay-date-picker/src/index.tsx
+++ b/packages/clay-date-picker/src/index.tsx
@@ -7,13 +7,12 @@ import Button from '@clayui/button';
import DropDown from '@clayui/drop-down';
import {ClayInput} from '@clayui/form';
import Icon from '@clayui/icon';
-import moment from 'moment';
import React from 'react';
import DateNavigation from './DateNavigation';
import DayNumber from './DayNumber';
import DaysTable from './DaysTable';
-import {getLocaleProps} from './Helpers';
+import {isValid, parseDate, setDate} from './Helpers';
import {useCurrentTime, useWeeks} from './Hooks';
import InputDate from './InputDate';
import TimePicker from './TimePicker';
@@ -21,7 +20,7 @@ import Weekday from './Weekday';
import WeekdayHeader from './WeekdayHeader';
import {FirstDayOfWeek, IAriaLabels, IYears} from './types';
-export {FirstDayOfWeek, getLocaleProps};
+export {FirstDayOfWeek};
interface IProps extends React.HTMLAttributes {
/**
@@ -31,7 +30,7 @@ interface IProps extends React.HTMLAttributes {
/**
* Set the format of how the date will appear in the input element.
- * See available: https://momentjs.com/docs/#/parsing/string-format/
+ * See available: https://date-fns.org/v2.14.0/docs/format
*/
dateFormat?: string;
@@ -163,10 +162,10 @@ const ClayDatePicker: React.FunctionComponent = React.forwardRef<
buttonNextMonth: 'Select the next month',
buttonPreviousMonth: 'Select the previous month',
},
- dateFormat = 'YYYY-MM-DD',
+ dateFormat = 'yyyy-MM-dd',
disabled,
expanded,
- firstDayOfWeek = 0,
+ firstDayOfWeek = FirstDayOfWeek.Sunday,
footerElement,
id,
initialExpanded = false,
@@ -208,14 +207,13 @@ const ClayDatePicker: React.FunctionComponent = React.forwardRef<
* Normalize date for always set noon to avoid time zone issues
*/
const normalizeDate = (date: Date) =>
- moment(date)
- .clone()
- .set('date', 1)
- .set('hour', 12)
- .set('minute', 0)
- .set('second', 0)
- .set('millisecond', 0)
- .toDate();
+ setDate(date, {
+ date: 1,
+ hours: 12,
+ milliseconds: 1,
+ minutes: 1,
+ seconds: 1,
+ });
/**
* Indicates the current month rendered on the screen.
@@ -286,6 +284,7 @@ const ClayDatePicker: React.FunctionComponent = React.forwardRef<
*/
const handleDayClicked = (date: Date) => {
setDaySelected(date);
+
onValueChange(date, 'click');
};
@@ -297,15 +296,16 @@ const ClayDatePicker: React.FunctionComponent = React.forwardRef<
const inputChange = (event: React.ChangeEvent) => {
const {value} = event.target;
const format = time ? `${dateFormat} ${TIME_FORMAT}` : dateFormat;
- const date = moment(value, format);
- const year = date.year();
- if (date.isValid() && year >= years.start && years.end >= year) {
- changeMonth(date.toDate());
- setDaySelected(date.toDate());
+ const date = parseDate(value, format, new Date());
+ const year = date.getFullYear();
+
+ if (isValid(date) && year >= years.start && years.end >= year) {
+ changeMonth(date);
+ setDaySelected(date);
if (time) {
- setCurrentTime(date.hours(), date.minutes());
+ setCurrentTime(date.getHours(), date.getMinutes());
}
}
@@ -329,8 +329,11 @@ const ClayDatePicker: React.FunctionComponent = React.forwardRef<
// Hack to force InputDate to add `currentTime` to the value of
// the input when the value was edited by the user.
- if (typeof value === 'string' && moment(value, format).isValid()) {
- onValueChange(moment(value, format).toDate(), 'time');
+ if (
+ typeof value === 'string' &&
+ isValid(parseDate(value, format, new Date()))
+ ) {
+ onValueChange(parseDate(value, format, new Date()), 'time');
}
setCurrentTime(hours, minutes);
@@ -356,7 +359,6 @@ const ClayDatePicker: React.FunctionComponent = React.forwardRef<
placeholder={placeholder}
ref={ref}
time={time}
- timeFormat={TIME_FORMAT}
useNative={useNative}
value={value}
/>
diff --git a/packages/clay-date-picker/stories/index.tsx b/packages/clay-date-picker/stories/index.tsx
index a1d6a44d56..cbca021112 100644
--- a/packages/clay-date-picker/stories/index.tsx
+++ b/packages/clay-date-picker/stories/index.tsx
@@ -9,7 +9,7 @@ import {boolean} from '@storybook/addon-knobs';
import {storiesOf} from '@storybook/react';
import React from 'react';
-import ClayDatePicker, {FirstDayOfWeek, getLocaleProps} from '../src';
+import ClayDatePicker, {FirstDayOfWeek} from '../src';
const ClayDatePickerWithState = (props: {[key: string]: any}) => {
const [value, setValue] = React.useState('');
@@ -67,7 +67,7 @@ storiesOf('Components|ClayDatePicker', module)
))
.add('w/ locale', () => (
))
- .add('w/ getLocaleProps', () => (
-
- ))
.add('w/ custom expand', () => {
const [expanded, setExpanded] = React.useState(false);
const [value, setValue] = React.useState('');
diff --git a/packages/demos/stories/Recharts.tsx b/packages/demos/stories/Recharts.tsx
index 78a56569a6..514af79187 100644
--- a/packages/demos/stories/Recharts.tsx
+++ b/packages/demos/stories/Recharts.tsx
@@ -7,7 +7,7 @@ import '@clayui/css/lib/css/atlas.css';
import {ClayRadio, ClayRadioGroup} from '@clayui/form';
import ClayLayout from '@clayui/layout';
import {storiesOf} from '@storybook/react';
-import moment from 'moment';
+import {getYear, parse as parseDate} from 'date-fns';
import * as React from 'react';
import {
Area,
@@ -332,7 +332,11 @@ storiesOf('Demos|Recharts', module)
>
- moment(val).year()} />
+
+ getYear(parseDate(val, 'dd/LL/yyyy', new Date()))
+ }
+ />
diff --git a/yarn.lock b/yarn.lock
index c7d3af2517..a3a64cecfa 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7701,6 +7701,11 @@ dataloader@^1.4.0:
resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8"
integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==
+date-fns@^2.14.0:
+ version "2.14.0"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.14.0.tgz#359a87a265bb34ef2e38f93ecf63ac453f9bc7ba"
+ integrity sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw==
+
date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
@@ -16148,7 +16153,7 @@ modify-values@^1.0.0:
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
-moment@^2.21.0, moment@^2.22.2, moment@^2.5.1:
+moment@^2.21.0, moment@^2.5.1:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==