@acusti/date-picker 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # @acusti/date-picker
2
2
 
3
- [![latest version](https://img.shields.io/npm/v/@acusti/css-value-input?style=for-the-badge)](https://www.npmjs.com/package/@acusti/css-value-input)
4
- [![maintenance status](https://img.shields.io/npms-io/maintenance-score/@acusti/css-value-input?style=for-the-badge)](https://npms.io/search?q=%40acusti%2Fcss-value-input)
5
- [![bundle size](https://img.shields.io/bundlephobia/minzip/@acusti/css-value-input?style=for-the-badge)](https://bundlephobia.com/package/@acusti/css-value-input)
6
- [![downloads per month](https://img.shields.io/npm/dm/@acusti/css-value-input?style=for-the-badge)](https://www.npmjs.com/package/@acusti/css-value-input)
3
+ [![latest version](https://img.shields.io/npm/v/@acusti/date-picker?style=for-the-badge)](https://www.npmjs.com/package/@acusti/date-picker)
4
+ [![maintenance status](https://img.shields.io/npms-io/maintenance-score/@acusti/date-picker?style=for-the-badge)](https://npms.io/search?q=%40acusti%2Fdate-picker)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@acusti/date-picker?style=for-the-badge)](https://bundlephobia.com/package/@acusti/date-picker)
6
+ [![downloads per month](https://img.shields.io/npm/dm/@acusti/date-picker?style=for-the-badge)](https://www.npmjs.com/package/@acusti/date-picker)
7
7
 
8
8
  A group of React components and utils for rendering a date picker with
9
9
  support for ranges via a two-up month calendar view.
@@ -1,8 +1,10 @@
1
1
  import * as React from 'react';
2
2
  export type Props = {
3
3
  className?: string;
4
+ dateRangeStart?: Date | string | number;
5
+ dateRangeEnd?: Date | string | number;
4
6
  month: number;
5
7
  onChange?: (event: React.SyntheticEvent<HTMLElement>) => void;
6
8
  title?: string;
7
9
  };
8
- export default function MonthCalendar({ className, month, onChange, title }: Props): JSX.Element;
10
+ export default function MonthCalendar({ className, dateRangeEnd, dateRangeStart, month, onChange, title, }: Props): JSX.Element;
@@ -1,11 +1,11 @@
1
1
  import { Style } from '@acusti/styling';
2
2
  import clsx from 'clsx';
3
3
  import * as React from 'react';
4
- import { getMonthNameFromMonth, getYearFromMonth } from './utils.js';
4
+ import { getMonthFromDate, getMonthNameFromMonth, getYearFromMonth } from './utils.js';
5
5
  import { ROOT_CLASS_NAME, STYLES } from './styles/month-calendar.js';
6
6
  const { Fragment, useCallback } = React;
7
7
  const DAYS = Array(7).fill(null);
8
- export default function MonthCalendar({ className, month, onChange, title }) {
8
+ export default function MonthCalendar({ className, dateRangeEnd, dateRangeStart, month, onChange, title, }) {
9
9
  const year = getYearFromMonth(month);
10
10
  title = title || `${getMonthNameFromMonth(month)} ${year}`;
11
11
  const monthWithinYear = month % 12;
@@ -17,6 +17,33 @@ export default function MonthCalendar({ className, month, onChange, title }) {
17
17
  const firstDay = firstDate.getDay();
18
18
  const spacesAfterLastDay = 7 - (lastDate.getDay() % 7); // prettier-ignore
19
19
  const daySpaces = totalDays + firstDay + spacesAfterLastDay;
20
+ const [dateRangeStartDay, dateRangeEndDay] = [
21
+ dateRangeStart,
22
+ dateRangeEnd,
23
+ ].reduce((acc, date, index) => {
24
+ if (date != null && !(date instanceof Date)) {
25
+ date = new Date(date);
26
+ }
27
+ if (date == null || Number.isNaN(date.getTime()))
28
+ return acc;
29
+ const dateMonth = getMonthFromDate(date);
30
+ if (dateMonth < month)
31
+ acc[index] = -1;
32
+ else if (dateMonth > month)
33
+ acc[index] = totalDays + 1;
34
+ else
35
+ acc[index] = date.getDate();
36
+ if (index === 1) {
37
+ const startDay = acc[index - 1];
38
+ const endDay = acc[index];
39
+ // Ensure that end date is after start date and swap them if not
40
+ if (startDay != null && endDay != null && startDay > endDay) {
41
+ acc[index - 1] = endDay;
42
+ acc[index] = startDay;
43
+ }
44
+ }
45
+ return acc;
46
+ }, [null, null]);
20
47
  const handleClickDay = useCallback((event) => {
21
48
  if (onChange)
22
49
  onChange(event);
@@ -41,15 +68,23 @@ export default function MonthCalendar({ className, month, onChange, title }) {
41
68
  React.createElement("span", { className: "week-day-item-text" }, "Fr")),
42
69
  React.createElement("div", { className: "week-day-item" },
43
70
  React.createElement("span", { className: "week-day-item-text" }, "Sa"))),
44
- React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-days` }, Array(Math.ceil(daySpaces / 7))
71
+ React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-days` }, Array(Math.floor(daySpaces / 7))
45
72
  .fill(null)
46
73
  .map((_, weekIndex) => (React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-row`, key: `MonthRow-${weekIndex}` }, DAYS.map((_, dayIndex) => {
47
74
  dayIndex += weekIndex * 7;
48
75
  const dayNumber = (dayIndex - firstDay) + 1; // prettier-ignore
49
- return (React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-day-item`, key: `MonthDayItem-${dayNumber}`, onClick: handleClickDay },
50
- React.createElement("span", { className: "month-day-item-text" }, dayNumber < 1 || dayNumber > totalDays
51
- ? ''
52
- : dayNumber)));
76
+ const isEmpty = dayNumber < 1 || dayNumber > totalDays;
77
+ const isAfterDateRangeStart = dateRangeStartDay != null &&
78
+ dayNumber > dateRangeStartDay;
79
+ const isBeforeDateRangeEnd = dateRangeEndDay == null ||
80
+ dayNumber < dateRangeEndDay;
81
+ return (React.createElement("div", { className: clsx(`${ROOT_CLASS_NAME}-month-day-item`, {
82
+ 'is-empty': isEmpty,
83
+ 'is-selected': isAfterDateRangeStart &&
84
+ isBeforeDateRangeEnd,
85
+ 'end-date': dayNumber === dateRangeEndDay,
86
+ 'start-date': dayNumber === dateRangeStartDay,
87
+ }), key: `MonthDayItem-${dayNumber}`, onClick: handleClickDay }, isEmpty ? null : (React.createElement("span", { className: "month-day-item-text" }, dayNumber))));
53
88
  }))))))));
54
89
  }
55
90
  //# sourceMappingURL=MonthCalendar.js.map
@@ -8,6 +8,8 @@
8
8
  import * as React from "react";
9
9
  export type Props = {|
10
10
  className?: string,
11
+ dateRangeStart?: Date | string | number,
12
+ dateRangeEnd?: Date | string | number,
11
13
  month: number,
12
14
  onChange?: (event: SyntheticEvent<HTMLElement>) => void,
13
15
  title?: string,
@@ -1 +1 @@
1
- {"version":3,"file":"MonthCalendar.js","sourceRoot":"","sources":["../src/MonthCalendar.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MonthCalendar.js","sourceRoot":"","sources":["../src/MonthCalendar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAarE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;AAExC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEjC,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EAClC,SAAS,EACT,YAAY,EACZ,cAAc,EACd,KAAK,EACL,QAAQ,EACR,KAAK,GACD;IACJ,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACrC,KAAK,GAAG,KAAK,IAAI,GAAG,qBAAqB,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;IAC3D,MAAM,eAAe,GAAG,KAAK,GAAG,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1E,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC;IACpC,MAAM,kBAAkB,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB;IAC1E,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,kBAAkB,CAAC;IAE5D,MAAM,CAAC,iBAAiB,EAAE,eAAe,CAAC,GAAkB;QACxD,cAAc;QACd,YAAY;KACf,CAAC,MAAM,CACJ,CAAC,GAAkB,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QAChC,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE;YACzC,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;SACzB;QACD,IAAI,IAAI,IAAI,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAAE,OAAO,GAAG,CAAC;QAE7D,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,SAAS,GAAG,KAAK;YAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;aAClC,IAAI,SAAS,GAAG,KAAK;YAAE,GAAG,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC;;YAClD,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,KAAK,KAAK,CAAC,EAAE;YACb,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1B,gEAAgE;YAChE,IAAI,QAAQ,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,QAAQ,GAAG,MAAM,EAAE;gBACzD,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC;gBACxB,GAAG,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;aACzB;SACJ;QAED,OAAO,GAAG,CAAC;IACf,CAAC,EACD,CAAC,IAAI,EAAE,IAAI,CAAC,CACf,CAAC;IAEF,MAAM,cAAc,GAAG,WAAW,CAC9B,CAAC,KAAwC,EAAE,EAAE;QACzC,IAAI,QAAQ;YAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC,EACD,CAAC,QAAQ,CAAC,CACb,CAAC;IAEF,OAAO,CACH,oBAAC,QAAQ;QACL,oBAAC,KAAK,QAAE,MAAM,CAAS;QACvB,6BAAK,SAAS,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC;YAC5C,6BAAK,SAAS,EAAE,GAAG,eAAe,cAAc;gBAC5C,4BAAI,SAAS,EAAE,GAAG,eAAe,mBAAmB,IAAG,KAAK,CAAM,CAChE;YACN,6BAAK,SAAS,EAAE,GAAG,eAAe,aAAa;gBAC3C,6BAAK,SAAS,EAAC,eAAe;oBAC1B,8BAAM,SAAS,EAAC,oBAAoB,SAAU,CAC5C;gBACN,6BAAK,SAAS,EAAC,eAAe;oBAC1B,8BAAM,SAAS,EAAC,oBAAoB,SAAU,CAC5C;gBACN,6BAAK,SAAS,EAAC,eAAe;oBAC1B,8BAAM,SAAS,EAAC,oBAAoB,SAAU,CAC5C;gBACN,6BAAK,SAAS,EAAC,eAAe;oBAC1B,8BAAM,SAAS,EAAC,oBAAoB,SAAU,CAC5C;gBACN,6BAAK,SAAS,EAAC,eAAe;oBAC1B,8BAAM,SAAS,EAAC,oBAAoB,SAAU,CAC5C;gBACN,6BAAK,SAAS,EAAC,eAAe;oBAC1B,8BAAM,SAAS,EAAC,oBAAoB,SAAU,CAC5C;gBACN,6BAAK,SAAS,EAAC,eAAe;oBAC1B,8BAAM,SAAS,EAAC,oBAAoB,SAAU,CAC5C,CACJ;YACN,6BAAK,SAAS,EAAE,GAAG,eAAe,aAAa,IAC1C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;iBAC5B,IAAI,CAAC,IAAI,CAAC;iBACV,GAAG,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,CACnB,6BACI,SAAS,EAAE,GAAG,eAAe,YAAY,EACzC,GAAG,EAAE,YAAY,SAAS,EAAE,IAE3B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE;gBACtB,QAAQ,IAAI,SAAS,GAAG,CAAC,CAAC;gBAC1B,MAAM,SAAS,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,kBAAkB;gBAC/D,MAAM,OAAO,GACT,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,SAAS,CAAC;gBAC3C,MAAM,qBAAqB,GACvB,iBAAiB,IAAI,IAAI;oBACzB,SAAS,GAAG,iBAAiB,CAAC;gBAClC,MAAM,oBAAoB,GACtB,eAAe,IAAI,IAAI;oBACvB,SAAS,GAAG,eAAe,CAAC;gBAEhC,OAAO,CACH,6BACI,SAAS,EAAE,IAAI,CACX,GAAG,eAAe,iBAAiB,EACnC;wBACI,UAAU,EAAE,OAAO;wBACnB,aAAa,EACT,qBAAqB;4BACrB,oBAAoB;wBACxB,UAAU,EACN,SAAS,KAAK,eAAe;wBACjC,YAAY,EACR,SAAS,KAAK,iBAAiB;qBACtC,CACJ,EACD,GAAG,EAAE,gBAAgB,SAAS,EAAE,EAChC,OAAO,EAAE,cAAc,IAEtB,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACd,8BAAM,SAAS,EAAC,qBAAqB,IAChC,SAAS,CACP,CACV,CACC,CACT,CAAC;YACN,CAAC,CAAC,CACA,CACT,CAAC,CACJ,CACJ,CACC,CACd,CAAC;AACN,CAAC"}
@@ -76,7 +76,7 @@ h3.${ROOT_CLASS_NAME}-month-title-text {
76
76
  cursor: pointer;
77
77
  }
78
78
 
79
- .${ROOT_CLASS_NAME}-month-day-item.selected {
79
+ .${ROOT_CLASS_NAME}-month-day-item.is-selected {
80
80
  background-color: #f8f8f8;
81
81
  }
82
82
 
@@ -131,6 +131,10 @@ h3.${ROOT_CLASS_NAME}-month-title-text {
131
131
  visibility: hidden;
132
132
  }
133
133
 
134
+ .${ROOT_CLASS_NAME}-month-day-item.is-empty:after {
135
+ content: none;
136
+ }
137
+
134
138
  .${ROOT_CLASS_NAME}-month-day-item span.month-day-item-text {
135
139
  text-align: center;
136
140
  font-size: 13px;
@@ -1 +1 @@
1
- {"version":3,"file":"month-calendar.js","sourceRoot":"","sources":["../../src/styles/month-calendar.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAElD,MAAM,CAAC,MAAM,MAAM,GAAG;GACnB,eAAe;;;;;;;;GAQf,eAAe;;;;;;;;;KASb,eAAe;;;;;;;;;GASjB,eAAe;;;;;;;;;;GAUf,eAAe;;;;;;;GAOf,eAAe;;;;;;;;GAQf,eAAe;;;;;;GAMf,eAAe;;;;;;;;;GASf,eAAe;;;;;;;;;;GAUf,eAAe;;;;GAIf,eAAe;;;;;;GAMf,eAAe;;;;;GAKf,eAAe;;;;GAIf,eAAe;;;;;;GAMf,eAAe;;;;;GAKf,eAAe;;;;GAIf,eAAe;;;;;GAKf,eAAe;;;;;;;;;;;;;;;;GAgBf,eAAe;;;;;;;;;CASjB,CAAC"}
1
+ {"version":3,"file":"month-calendar.js","sourceRoot":"","sources":["../../src/styles/month-calendar.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAElD,MAAM,CAAC,MAAM,MAAM,GAAG;GACnB,eAAe;;;;;;;;GAQf,eAAe;;;;;;;;;KASb,eAAe;;;;;;;;;GASjB,eAAe;;;;;;;;;;GAUf,eAAe;;;;;;;GAOf,eAAe;;;;;;;;GAQf,eAAe;;;;;;GAMf,eAAe;;;;;;;;;GASf,eAAe;;;;;;;;;;GAUf,eAAe;;;;GAIf,eAAe;;;;;;GAMf,eAAe;;;;;GAKf,eAAe;;;;GAIf,eAAe;;;;;;GAMf,eAAe;;;;;GAKf,eAAe;;;;GAIf,eAAe;;;;;GAKf,eAAe;;;;;;;;;;;;;;;;GAgBf,eAAe;;;;GAIf,eAAe;;;;;;;;;CASjB,CAAC"}
package/dist/utils.js CHANGED
@@ -18,7 +18,14 @@ const MONTH_NAMES = [
18
18
  const getYearFromDate = (date) => date.getFullYear() - START_YEAR;
19
19
  export const getMonthFromDate = (date) => date.getMonth() + (getYearFromDate(date) * 12); // prettier-ignore
20
20
  export const getYearFromMonth = (month) => Math.floor(month / 12) + START_YEAR;
21
- export const getMonthNameFromMonth = (month) => MONTH_NAMES[month % 12];
21
+ export const getMonthNameFromMonth = (month) => {
22
+ let index = month % 12;
23
+ if (Number.isNaN(index))
24
+ return '';
25
+ if (index < 0)
26
+ index = 12 + index;
27
+ return MONTH_NAMES[index];
28
+ };
22
29
  export const getMonthAbbreviationFromMonth = (month) => {
23
30
  const monthName = getMonthNameFromMonth(month);
24
31
  if (monthName === 'September')
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,oEAAoE;AACpE,MAAM,UAAU,GAAG,IAAI,CAAC;AACxB,MAAM,WAAW,GAAG;IAChB,SAAS;IACT,UAAU;IACV,OAAO;IACP,OAAO;IACP,KAAK;IACL,MAAM;IACN,MAAM;IACN,QAAQ;IACR,WAAW;IACX,SAAS;IACT,UAAU;IACV,UAAU;CACb,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,IAAU,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,UAAU,CAAC;AAExE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAU,EAAE,EAAE,CAC3C,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,kBAAkB;AAEtE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,UAAU,CAAC;AAEvF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;AAEhF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,KAAa,EAAE,EAAE;IAC3D,MAAM,SAAS,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,SAAS,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC;IAC7C,OAAO,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACrC,CAAC,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,oEAAoE;AACpE,MAAM,UAAU,GAAG,IAAI,CAAC;AACxB,MAAM,WAAW,GAAG;IAChB,SAAS;IACT,UAAU;IACV,OAAO;IACP,OAAO;IACP,KAAK;IACL,MAAM;IACN,MAAM;IACN,QAAQ;IACR,WAAW;IACX,SAAS;IACT,UAAU;IACV,UAAU;CACb,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,IAAU,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,UAAU,CAAC;AAExE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAU,EAAE,EAAE,CAC3C,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,kBAAkB;AAEtE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,UAAU,CAAC;AAEvF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAa,EAAU,EAAE;IAC3D,IAAI,KAAK,GAAG,KAAK,GAAG,EAAE,CAAC;IACvB,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,IAAI,KAAK,GAAG,CAAC;QAAE,KAAK,GAAG,EAAE,GAAG,KAAK,CAAC;IAClC,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,KAAa,EAAE,EAAE;IAC3D,MAAM,SAAS,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,SAAS,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC;IAC7C,OAAO,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACrC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acusti/date-picker",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "React component that renders a date picker with the option to choose a range",
5
5
  "keywords": [
6
6
  "react",
@@ -2,21 +2,32 @@ import { Style } from '@acusti/styling';
2
2
  import clsx from 'clsx';
3
3
  import * as React from 'react';
4
4
 
5
- import { getMonthNameFromMonth, getYearFromMonth } from './utils.js';
5
+ import { getMonthFromDate, getMonthNameFromMonth, getYearFromMonth } from './utils.js';
6
6
  import { ROOT_CLASS_NAME, STYLES } from './styles/month-calendar.js';
7
7
 
8
8
  export type Props = {
9
9
  className?: string;
10
+ dateRangeStart?: Date | string | number;
11
+ dateRangeEnd?: Date | string | number;
10
12
  month: number; // a unique numerical value representing the number of months since jan 1970
11
13
  onChange?: (event: React.SyntheticEvent<HTMLElement>) => void;
12
14
  title?: string;
13
15
  };
14
16
 
17
+ type DateRangeDays = [number | null, number | null];
18
+
15
19
  const { Fragment, useCallback } = React;
16
20
 
17
21
  const DAYS = Array(7).fill(null);
18
22
 
19
- export default function MonthCalendar({ className, month, onChange, title }: Props) {
23
+ export default function MonthCalendar({
24
+ className,
25
+ dateRangeEnd,
26
+ dateRangeStart,
27
+ month,
28
+ onChange,
29
+ title,
30
+ }: Props) {
20
31
  const year = getYearFromMonth(month);
21
32
  title = title || `${getMonthNameFromMonth(month)} ${year}`;
22
33
  const monthWithinYear = month % 12;
@@ -29,6 +40,35 @@ export default function MonthCalendar({ className, month, onChange, title }: Pro
29
40
  const spacesAfterLastDay = 7 - (lastDate.getDay() % 7); // prettier-ignore
30
41
  const daySpaces = totalDays + firstDay + spacesAfterLastDay;
31
42
 
43
+ const [dateRangeStartDay, dateRangeEndDay]: DateRangeDays = [
44
+ dateRangeStart,
45
+ dateRangeEnd,
46
+ ].reduce(
47
+ (acc: DateRangeDays, date, index) => {
48
+ if (date != null && !(date instanceof Date)) {
49
+ date = new Date(date);
50
+ }
51
+ if (date == null || Number.isNaN(date.getTime())) return acc;
52
+
53
+ const dateMonth = getMonthFromDate(date);
54
+ if (dateMonth < month) acc[index] = -1;
55
+ else if (dateMonth > month) acc[index] = totalDays + 1;
56
+ else acc[index] = date.getDate();
57
+ if (index === 1) {
58
+ const startDay = acc[index - 1];
59
+ const endDay = acc[index];
60
+ // Ensure that end date is after start date and swap them if not
61
+ if (startDay != null && endDay != null && startDay > endDay) {
62
+ acc[index - 1] = endDay;
63
+ acc[index] = startDay;
64
+ }
65
+ }
66
+
67
+ return acc;
68
+ },
69
+ [null, null],
70
+ );
71
+
32
72
  const handleClickDay = useCallback(
33
73
  (event: React.SyntheticEvent<HTMLElement>) => {
34
74
  if (onChange) onChange(event);
@@ -67,7 +107,7 @@ export default function MonthCalendar({ className, month, onChange, title }: Pro
67
107
  </div>
68
108
  </div>
69
109
  <div className={`${ROOT_CLASS_NAME}-month-days`}>
70
- {Array(Math.ceil(daySpaces / 7))
110
+ {Array(Math.floor(daySpaces / 7))
71
111
  .fill(null)
72
112
  .map((_, weekIndex) => (
73
113
  <div
@@ -77,17 +117,38 @@ export default function MonthCalendar({ className, month, onChange, title }: Pro
77
117
  {DAYS.map((_, dayIndex) => {
78
118
  dayIndex += weekIndex * 7;
79
119
  const dayNumber = (dayIndex - firstDay) + 1; // prettier-ignore
120
+ const isEmpty =
121
+ dayNumber < 1 || dayNumber > totalDays;
122
+ const isAfterDateRangeStart =
123
+ dateRangeStartDay != null &&
124
+ dayNumber > dateRangeStartDay;
125
+ const isBeforeDateRangeEnd =
126
+ dateRangeEndDay == null ||
127
+ dayNumber < dateRangeEndDay;
128
+
80
129
  return (
81
130
  <div
82
- className={`${ROOT_CLASS_NAME}-month-day-item`}
131
+ className={clsx(
132
+ `${ROOT_CLASS_NAME}-month-day-item`,
133
+ {
134
+ 'is-empty': isEmpty,
135
+ 'is-selected':
136
+ isAfterDateRangeStart &&
137
+ isBeforeDateRangeEnd,
138
+ 'end-date':
139
+ dayNumber === dateRangeEndDay,
140
+ 'start-date':
141
+ dayNumber === dateRangeStartDay,
142
+ },
143
+ )}
83
144
  key={`MonthDayItem-${dayNumber}`}
84
145
  onClick={handleClickDay}
85
146
  >
86
- <span className="month-day-item-text">
87
- {dayNumber < 1 || dayNumber > totalDays
88
- ? ''
89
- : dayNumber}
90
- </span>
147
+ {isEmpty ? null : (
148
+ <span className="month-day-item-text">
149
+ {dayNumber}
150
+ </span>
151
+ )}
91
152
  </div>
92
153
  );
93
154
  })}
@@ -77,7 +77,7 @@ h3.${ROOT_CLASS_NAME}-month-title-text {
77
77
  cursor: pointer;
78
78
  }
79
79
 
80
- .${ROOT_CLASS_NAME}-month-day-item.selected {
80
+ .${ROOT_CLASS_NAME}-month-day-item.is-selected {
81
81
  background-color: #f8f8f8;
82
82
  }
83
83
 
@@ -132,6 +132,10 @@ h3.${ROOT_CLASS_NAME}-month-title-text {
132
132
  visibility: hidden;
133
133
  }
134
134
 
135
+ .${ROOT_CLASS_NAME}-month-day-item.is-empty:after {
136
+ content: none;
137
+ }
138
+
135
139
  .${ROOT_CLASS_NAME}-month-day-item span.month-day-item-text {
136
140
  text-align: center;
137
141
  font-size: 13px;
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { getMonthFromDate, getMonthNameFromMonth } from './utils.js';
4
+
5
+ describe('@acusti/date-picker', () => {
6
+ describe('utils', () => {
7
+ describe('getMonthNameFromMonth', () => {
8
+ it('returns the correct month name for a post-unix epoch month', () => {
9
+ expect(
10
+ getMonthNameFromMonth(getMonthFromDate(new Date(2023, 5, 19))),
11
+ ).toBe('June');
12
+ });
13
+
14
+ it('returns the correct month name for a pre-unix epoch month', () => {
15
+ expect(
16
+ getMonthNameFromMonth(getMonthFromDate(new Date(1865, 5, 19))),
17
+ ).toBe('June');
18
+ });
19
+
20
+ it('returns an empty string if given NaN (e.g. if dealing with an Invalid Date)', () => {
21
+ expect(getMonthNameFromMonth(getMonthFromDate(new Date('')))).toBe('');
22
+ });
23
+ });
24
+ });
25
+ });
package/src/utils.ts CHANGED
@@ -23,7 +23,12 @@ export const getMonthFromDate = (date: Date) =>
23
23
 
24
24
  export const getYearFromMonth = (month: number) => Math.floor(month / 12) + START_YEAR;
25
25
 
26
- export const getMonthNameFromMonth = (month: number) => MONTH_NAMES[month % 12];
26
+ export const getMonthNameFromMonth = (month: number): string => {
27
+ let index = month % 12;
28
+ if (Number.isNaN(index)) return '';
29
+ if (index < 0) index = 12 + index;
30
+ return MONTH_NAMES[index];
31
+ };
27
32
 
28
33
  export const getMonthAbbreviationFromMonth = (month: number) => {
29
34
  const monthName = getMonthNameFromMonth(month);