@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 +4 -4
- package/dist/MonthCalendar.d.ts +3 -1
- package/dist/MonthCalendar.js +42 -7
- package/dist/MonthCalendar.js.flow +2 -0
- package/dist/MonthCalendar.js.map +1 -1
- package/dist/styles/month-calendar.js +5 -1
- package/dist/styles/month-calendar.js.map +1 -1
- package/dist/utils.js +8 -1
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/MonthCalendar.tsx +70 -9
- package/src/styles/month-calendar.ts +5 -1
- package/src/utils.test.ts +25 -0
- package/src/utils.ts +6 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @acusti/date-picker
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@acusti/date-picker)
|
|
4
|
+
[](https://npms.io/search?q=%40acusti%2Fdate-picker)
|
|
5
|
+
[](https://bundlephobia.com/package/@acusti/date-picker)
|
|
6
|
+
[](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.
|
package/dist/MonthCalendar.d.ts
CHANGED
|
@@ -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;
|
package/dist/MonthCalendar.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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":"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,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"MonthCalendar.js","sourceRoot":"","sources":["../src/MonthCalendar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -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) =>
|
|
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,
|
|
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
package/src/MonthCalendar.tsx
CHANGED
|
@@ -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({
|
|
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.
|
|
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={
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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) =>
|
|
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);
|