@acusti/date-picker 0.0.1
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 +9 -0
- package/dist/MonthCalendar.d.ts +8 -0
- package/dist/MonthCalendar.js +57 -0
- package/dist/MonthCalendar.js.flow +15 -0
- package/dist/MonthCalendar.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.flow +9 -0
- package/dist/index.js.map +1 -0
- package/dist/styles/month-calendar.d.ts +2 -0
- package/dist/styles/month-calendar.js +152 -0
- package/dist/styles/month-calendar.js.map +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +28 -0
- package/dist/utils.js.flow +11 -0
- package/dist/utils.js.map +1 -0
- package/package.json +59 -0
- package/src/MonthCalendar.tsx +103 -0
- package/src/index.ts +2 -0
- package/src/styles/month-calendar.ts +153 -0
- package/src/utils.ts +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @acusti/date-picker
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@acusti/css-value-input)
|
|
4
|
+
[](https://npms.io/search?q=%40acusti%2Fcss-value-input)
|
|
5
|
+
[](https://bundlephobia.com/package/@acusti/css-value-input)
|
|
6
|
+
[](https://www.npmjs.com/package/@acusti/css-value-input)
|
|
7
|
+
|
|
8
|
+
A group of React components and utils for rendering a date picker with
|
|
9
|
+
support for ranges via a two-up month calendar view.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type Props = {
|
|
3
|
+
className?: string;
|
|
4
|
+
month: number;
|
|
5
|
+
onChange?: (event: React.SyntheticEvent<HTMLElement>) => void;
|
|
6
|
+
title?: string;
|
|
7
|
+
};
|
|
8
|
+
export default function MonthCalendar({ className, month, onChange, title }: Props): JSX.Element;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Style } from '@acusti/styling';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { getMonthNameFromMonth, getYearFromMonth } from './utils.js';
|
|
5
|
+
import { ROOT_CLASS_NAME, STYLES } from './styles/month-calendar.js';
|
|
6
|
+
const { Fragment, useCallback } = React;
|
|
7
|
+
const DAYS = Array(7).fill(null);
|
|
8
|
+
export default function MonthCalendar({ className, month, onChange, title }) {
|
|
9
|
+
const year = getYearFromMonth(month);
|
|
10
|
+
title = title || `${getMonthNameFromMonth(month)} ${year}`;
|
|
11
|
+
const monthWithinYear = month % 12;
|
|
12
|
+
const firstDate = new Date(year, monthWithinYear, 1);
|
|
13
|
+
const nextMonth = month + 1;
|
|
14
|
+
const lastDate = new Date(getYearFromMonth(nextMonth), nextMonth % 12, 1);
|
|
15
|
+
lastDate.setDate(lastDate.getDate() - 1);
|
|
16
|
+
const totalDays = lastDate.getDate();
|
|
17
|
+
const firstDay = firstDate.getDay();
|
|
18
|
+
const spacesAfterLastDay = 7 - (lastDate.getDay() % 7); // prettier-ignore
|
|
19
|
+
const daySpaces = totalDays + firstDay + spacesAfterLastDay;
|
|
20
|
+
const handleClickDay = useCallback((event) => {
|
|
21
|
+
if (onChange)
|
|
22
|
+
onChange(event);
|
|
23
|
+
}, [onChange]);
|
|
24
|
+
return (React.createElement(Fragment, null,
|
|
25
|
+
React.createElement(Style, null, STYLES),
|
|
26
|
+
React.createElement("div", { className: clsx(ROOT_CLASS_NAME, className) },
|
|
27
|
+
React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-item` },
|
|
28
|
+
React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-title` },
|
|
29
|
+
React.createElement("h3", { className: `${ROOT_CLASS_NAME}-month-title-text` }, title)),
|
|
30
|
+
React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-week` },
|
|
31
|
+
React.createElement("div", { className: "week-day-item" },
|
|
32
|
+
React.createElement("span", { className: "week-day-item-text" }, "Su")),
|
|
33
|
+
React.createElement("div", { className: "week-day-item" },
|
|
34
|
+
React.createElement("span", { className: "week-day-item-text" }, "Mo")),
|
|
35
|
+
React.createElement("div", { className: "week-day-item" },
|
|
36
|
+
React.createElement("span", { className: "week-day-item-text" }, "Tu")),
|
|
37
|
+
React.createElement("div", { className: "week-day-item" },
|
|
38
|
+
React.createElement("span", { className: "week-day-item-text" }, "We")),
|
|
39
|
+
React.createElement("div", { className: "week-day-item" },
|
|
40
|
+
React.createElement("span", { className: "week-day-item-text" }, "Th")),
|
|
41
|
+
React.createElement("div", { className: "week-day-item" },
|
|
42
|
+
React.createElement("span", { className: "week-day-item-text" }, "Fr")),
|
|
43
|
+
React.createElement("div", { className: "week-day-item" },
|
|
44
|
+
React.createElement("span", { className: "week-day-item-text" }, "Sa"))),
|
|
45
|
+
React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-days` }, Array(Math.ceil(daySpaces / 7))
|
|
46
|
+
.fill(null)
|
|
47
|
+
.map((_, weekIndex) => (React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-row`, key: `MonthRow-${weekIndex}` }, DAYS.map((_, dayIndex) => {
|
|
48
|
+
dayIndex += weekIndex * 7;
|
|
49
|
+
const dayNumber = (dayIndex - firstDay) + 1; // prettier-ignore
|
|
50
|
+
return (React.createElement("div", { className: `${ROOT_CLASS_NAME}-month-day-item`, key: `MonthDayItem-${dayNumber}`, onClick: handleClickDay },
|
|
51
|
+
React.createElement("span", { className: "month-day-item-text" }, dayNumber < 1 ||
|
|
52
|
+
dayNumber > totalDays
|
|
53
|
+
? ''
|
|
54
|
+
: dayNumber)));
|
|
55
|
+
})))))))));
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=MonthCalendar.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flowtype definitions for MonthCalendar
|
|
3
|
+
* Generated by Flowgen from a Typescript Definition
|
|
4
|
+
* Flowgen v1.20.1
|
|
5
|
+
* @flow
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
export type Props = {|
|
|
10
|
+
className?: string,
|
|
11
|
+
month: number,
|
|
12
|
+
onChange?: (event: SyntheticEvent<HTMLElement>) => void,
|
|
13
|
+
title?: string,
|
|
14
|
+
|};
|
|
15
|
+
declare export default function MonthCalendar(x: Props): React$Node;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MonthCalendar.js","sourceRoot":"","sources":["../src/MonthCalendar.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC9D,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { SYSTEM_UI_FONT } from '@acusti/styling';
|
|
2
|
+
export const ROOT_CLASS_NAME = 'uktmonthcalendar';
|
|
3
|
+
export const STYLES = `
|
|
4
|
+
.${ROOT_CLASS_NAME} {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex: 1 1 auto;
|
|
7
|
+
justify-content: space-between;
|
|
8
|
+
font-family: ${SYSTEM_UI_FONT};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.${ROOT_CLASS_NAME}-month-item {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
flex: 1 1 auto;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
max-width: 325px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.${ROOT_CLASS_NAME}-month-title {
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
flex: 0 0 auto;
|
|
24
|
+
box-sizing: border-box;
|
|
25
|
+
padding-bottom: 25px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
h3.${ROOT_CLASS_NAME}-month-title-text {
|
|
29
|
+
font-size: 18px;
|
|
30
|
+
line-height: 23px;
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
color: #000;
|
|
33
|
+
margin: 0px;
|
|
34
|
+
text-align: center;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.${ROOT_CLASS_NAME}-month-week {
|
|
38
|
+
flex: 0 0 auto;
|
|
39
|
+
display: grid;
|
|
40
|
+
grid-column-gap: 0px;
|
|
41
|
+
grid-template-columns: repeat(auto-fit, minmax(46px, 1fr));
|
|
42
|
+
grid-auto-flow: dense;
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
padding-bottom: 12px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.${ROOT_CLASS_NAME}-month-week .week-day-item {
|
|
48
|
+
flex: 1 1 auto;
|
|
49
|
+
display: flex;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
align-items: center;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.${ROOT_CLASS_NAME}-month-week span.week-day-item-text {
|
|
55
|
+
text-align: center;
|
|
56
|
+
font-size: 13px;
|
|
57
|
+
line-height: 21px;
|
|
58
|
+
margin: 0px;
|
|
59
|
+
color: #9a9a9a;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.${ROOT_CLASS_NAME}-month-days {
|
|
63
|
+
flex: 1 1 auto;
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.${ROOT_CLASS_NAME}-month-row {
|
|
69
|
+
flex: 1 1 auto;
|
|
70
|
+
display: grid;
|
|
71
|
+
grid-column-gap: 0px;
|
|
72
|
+
grid-template-columns: repeat(auto-fit, minmax(46px, 1fr));
|
|
73
|
+
grid-auto-flow: dense;
|
|
74
|
+
margin-bottom: 1px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.${ROOT_CLASS_NAME}-month-day-item {
|
|
78
|
+
display: flex;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
align-items: center;
|
|
81
|
+
position: relative;
|
|
82
|
+
height: 46px;
|
|
83
|
+
width: 46px;
|
|
84
|
+
cursor: pointer;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.${ROOT_CLASS_NAME}-month-day-item.selected {
|
|
88
|
+
background-color: #f8f8f8;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.${ROOT_CLASS_NAME}-month-day-item.start-date {
|
|
92
|
+
background-color: #f8f8f8;
|
|
93
|
+
border-top-left-radius: 50%;
|
|
94
|
+
border-bottom-left-radius: 50%;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.${ROOT_CLASS_NAME}-month-day-item.start-date:after {
|
|
98
|
+
background-color: #000;
|
|
99
|
+
opacity: 1;
|
|
100
|
+
visibility: visible;
|
|
101
|
+
}
|
|
102
|
+
.${ROOT_CLASS_NAME}-month-day-item.start-date span.month-day-item-text {
|
|
103
|
+
color: #fff;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.${ROOT_CLASS_NAME}-month-day-item.end-date {
|
|
107
|
+
background-color: #f8f8f8;
|
|
108
|
+
border-top-right-radius: 50%;
|
|
109
|
+
border-bottom-right-radius: 50%;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.${ROOT_CLASS_NAME}-month-day-item.end-date:after {
|
|
113
|
+
background-color: #000;
|
|
114
|
+
opacity: 1;
|
|
115
|
+
visibility: visible;
|
|
116
|
+
}
|
|
117
|
+
.${ROOT_CLASS_NAME}-month-day-item.end-date span.month-day-item-text {
|
|
118
|
+
color: #fff;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.${ROOT_CLASS_NAME}-month-day-item:hover:after {
|
|
122
|
+
opacity: 1;
|
|
123
|
+
visibility: visible;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.${ROOT_CLASS_NAME}-month-day-item:after {
|
|
127
|
+
content: "";
|
|
128
|
+
position: absolute;
|
|
129
|
+
left: 50%;
|
|
130
|
+
top: 50%;
|
|
131
|
+
transform: translate(-50%, -50%);
|
|
132
|
+
pointer-events: none;
|
|
133
|
+
border-radius: 50%;
|
|
134
|
+
border: 1px solid #000;
|
|
135
|
+
width: 43px;
|
|
136
|
+
height: 43px;
|
|
137
|
+
transition: opacity 0.25s ease-in-out;
|
|
138
|
+
opacity: 0;
|
|
139
|
+
visibility: hidden;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.${ROOT_CLASS_NAME}-month-day-item span.month-day-item-text {
|
|
143
|
+
text-align: center;
|
|
144
|
+
font-size: 13px;
|
|
145
|
+
line-height: 21px;
|
|
146
|
+
margin: 0px;
|
|
147
|
+
color: #000;
|
|
148
|
+
position: relative;
|
|
149
|
+
z-index: 1;
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
//# sourceMappingURL=month-calendar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"month-calendar.js","sourceRoot":"","sources":["../../src/styles/month-calendar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAElD,MAAM,CAAC,MAAM,MAAM,GAAG;GACnB,eAAe;;;;mBAIC,cAAc;;;GAG9B,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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const getMonthFromDate: (date: Date) => number;
|
|
2
|
+
export declare const getYearFromMonth: (month: number) => number;
|
|
3
|
+
export declare const getMonthNameFromMonth: (month: number) => string;
|
|
4
|
+
export declare const getMonthAbbreviationFromMonth: (month: number) => string;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// The following utils work on a “month” as a unique numerical value
|
|
2
|
+
// representing the number of months since the unix epoch (jan 1970)
|
|
3
|
+
const START_YEAR = 1970;
|
|
4
|
+
const MONTH_NAMES = [
|
|
5
|
+
'January',
|
|
6
|
+
'February',
|
|
7
|
+
'March',
|
|
8
|
+
'April',
|
|
9
|
+
'May',
|
|
10
|
+
'June',
|
|
11
|
+
'July',
|
|
12
|
+
'August',
|
|
13
|
+
'September',
|
|
14
|
+
'October',
|
|
15
|
+
'November',
|
|
16
|
+
'December',
|
|
17
|
+
];
|
|
18
|
+
const getYearFromDate = (date) => date.getFullYear() - START_YEAR;
|
|
19
|
+
export const getMonthFromDate = (date) => date.getMonth() + (getYearFromDate(date) * 12); // prettier-ignore
|
|
20
|
+
export const getYearFromMonth = (month) => Math.floor(month / 12) + START_YEAR;
|
|
21
|
+
export const getMonthNameFromMonth = (month) => MONTH_NAMES[month % 12];
|
|
22
|
+
export const getMonthAbbreviationFromMonth = (month) => {
|
|
23
|
+
const monthName = getMonthNameFromMonth(month);
|
|
24
|
+
if (monthName === 'September')
|
|
25
|
+
return 'Sept';
|
|
26
|
+
return monthName.substring(0, 3);
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flowtype definitions for utils
|
|
3
|
+
* Generated by Flowgen from a Typescript Definition
|
|
4
|
+
* Flowgen v1.20.1
|
|
5
|
+
* @flow
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare export var getMonthFromDate: (date: Date) => number;
|
|
9
|
+
declare export var getYearFromMonth: (month: number) => number;
|
|
10
|
+
declare export var getMonthNameFromMonth: (month: number) => string;
|
|
11
|
+
declare export var getMonthAbbreviationFromMonth: (month: number) => string;
|
|
@@ -0,0 +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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@acusti/date-picker",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "React component that renders a date picker with the option to choose a range",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"react-component",
|
|
8
|
+
"datepicker",
|
|
9
|
+
"date-picker",
|
|
10
|
+
"daterangepicker",
|
|
11
|
+
"date-range-picker",
|
|
12
|
+
"date",
|
|
13
|
+
"picker",
|
|
14
|
+
"form",
|
|
15
|
+
"input",
|
|
16
|
+
"ssr",
|
|
17
|
+
"typescript",
|
|
18
|
+
"ts",
|
|
19
|
+
"flow"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"exports": "./dist/index.js",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"src"
|
|
29
|
+
],
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/acusti/uikit.git",
|
|
33
|
+
"directory": "packages/date-picker"
|
|
34
|
+
},
|
|
35
|
+
"author": "andrew patton <andrew@acusti.ca> (https://www.acusti.ca)",
|
|
36
|
+
"license": "Unlicense",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/acusti/uikit/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/acusti/uikit/tree/main/packages/date-picker#readme",
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@testing-library/dom": "^9.3.1",
|
|
43
|
+
"@testing-library/react": "^14.0.0",
|
|
44
|
+
"@testing-library/user-event": "^14.4.3",
|
|
45
|
+
"@types/react": "^18.0.25",
|
|
46
|
+
"jsdom": "^22.1.0",
|
|
47
|
+
"react": "^18",
|
|
48
|
+
"react-dom": "^18",
|
|
49
|
+
"typescript": "^5.1.6"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@acusti/styling": "^0.6.0",
|
|
53
|
+
"clsx": "^2"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"react": "^16.8 || ^17 || ^18",
|
|
57
|
+
"react-dom": "^16.8 || ^17 || ^18"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Style } from '@acusti/styling';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import { getMonthNameFromMonth, getYearFromMonth } from './utils.js';
|
|
6
|
+
import { ROOT_CLASS_NAME, STYLES } from './styles/month-calendar.js';
|
|
7
|
+
|
|
8
|
+
export type Props = {
|
|
9
|
+
className?: string;
|
|
10
|
+
month: number; // a unique numerical value representing the number of months since jan 1970
|
|
11
|
+
onChange?: (event: React.SyntheticEvent<HTMLElement>) => void;
|
|
12
|
+
title?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const { Fragment, useCallback } = React;
|
|
16
|
+
|
|
17
|
+
const DAYS = Array(7).fill(null);
|
|
18
|
+
|
|
19
|
+
export default function MonthCalendar({ className, month, onChange, title }: Props) {
|
|
20
|
+
const year = getYearFromMonth(month);
|
|
21
|
+
title = title || `${getMonthNameFromMonth(month)} ${year}`;
|
|
22
|
+
const monthWithinYear = month % 12;
|
|
23
|
+
const firstDate = new Date(year, monthWithinYear, 1);
|
|
24
|
+
const nextMonth = month + 1;
|
|
25
|
+
const lastDate = new Date(getYearFromMonth(nextMonth), nextMonth % 12, 1);
|
|
26
|
+
lastDate.setDate(lastDate.getDate() - 1);
|
|
27
|
+
const totalDays = lastDate.getDate();
|
|
28
|
+
const firstDay = firstDate.getDay();
|
|
29
|
+
const spacesAfterLastDay = 7 - (lastDate.getDay() % 7); // prettier-ignore
|
|
30
|
+
const daySpaces = totalDays + firstDay + spacesAfterLastDay;
|
|
31
|
+
|
|
32
|
+
const handleClickDay = useCallback(
|
|
33
|
+
(event: React.SyntheticEvent<HTMLElement>) => {
|
|
34
|
+
if (onChange) onChange(event);
|
|
35
|
+
},
|
|
36
|
+
[onChange],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Fragment>
|
|
41
|
+
<Style>{STYLES}</Style>
|
|
42
|
+
<div className={clsx(ROOT_CLASS_NAME, className)}>
|
|
43
|
+
<div className={`${ROOT_CLASS_NAME}-month-item`}>
|
|
44
|
+
<div className={`${ROOT_CLASS_NAME}-month-title`}>
|
|
45
|
+
<h3 className={`${ROOT_CLASS_NAME}-month-title-text`}>{title}</h3>
|
|
46
|
+
</div>
|
|
47
|
+
<div className={`${ROOT_CLASS_NAME}-month-week`}>
|
|
48
|
+
<div className="week-day-item">
|
|
49
|
+
<span className="week-day-item-text">Su</span>
|
|
50
|
+
</div>
|
|
51
|
+
<div className="week-day-item">
|
|
52
|
+
<span className="week-day-item-text">Mo</span>
|
|
53
|
+
</div>
|
|
54
|
+
<div className="week-day-item">
|
|
55
|
+
<span className="week-day-item-text">Tu</span>
|
|
56
|
+
</div>
|
|
57
|
+
<div className="week-day-item">
|
|
58
|
+
<span className="week-day-item-text">We</span>
|
|
59
|
+
</div>
|
|
60
|
+
<div className="week-day-item">
|
|
61
|
+
<span className="week-day-item-text">Th</span>
|
|
62
|
+
</div>
|
|
63
|
+
<div className="week-day-item">
|
|
64
|
+
<span className="week-day-item-text">Fr</span>
|
|
65
|
+
</div>
|
|
66
|
+
<div className="week-day-item">
|
|
67
|
+
<span className="week-day-item-text">Sa</span>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div className={`${ROOT_CLASS_NAME}-month-days`}>
|
|
71
|
+
{Array(Math.ceil(daySpaces / 7))
|
|
72
|
+
.fill(null)
|
|
73
|
+
.map((_, weekIndex) => (
|
|
74
|
+
<div
|
|
75
|
+
className={`${ROOT_CLASS_NAME}-month-row`}
|
|
76
|
+
key={`MonthRow-${weekIndex}`}
|
|
77
|
+
>
|
|
78
|
+
{DAYS.map((_, dayIndex) => {
|
|
79
|
+
dayIndex += weekIndex * 7;
|
|
80
|
+
const dayNumber = (dayIndex - firstDay) + 1; // prettier-ignore
|
|
81
|
+
return (
|
|
82
|
+
<div
|
|
83
|
+
className={`${ROOT_CLASS_NAME}-month-day-item`}
|
|
84
|
+
key={`MonthDayItem-${dayNumber}`}
|
|
85
|
+
onClick={handleClickDay}
|
|
86
|
+
>
|
|
87
|
+
<span className="month-day-item-text">
|
|
88
|
+
{dayNumber < 1 ||
|
|
89
|
+
dayNumber > totalDays
|
|
90
|
+
? ''
|
|
91
|
+
: dayNumber}
|
|
92
|
+
</span>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
})}
|
|
96
|
+
</div>
|
|
97
|
+
))}
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</Fragment>
|
|
102
|
+
);
|
|
103
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { SYSTEM_UI_FONT } from '@acusti/styling';
|
|
2
|
+
|
|
3
|
+
export const ROOT_CLASS_NAME = 'uktmonthcalendar';
|
|
4
|
+
|
|
5
|
+
export const STYLES = `
|
|
6
|
+
.${ROOT_CLASS_NAME} {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex: 1 1 auto;
|
|
9
|
+
justify-content: space-between;
|
|
10
|
+
font-family: ${SYSTEM_UI_FONT};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.${ROOT_CLASS_NAME}-month-item {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
flex: 1 1 auto;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
max-width: 325px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.${ROOT_CLASS_NAME}-month-title {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
flex: 0 0 auto;
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
padding-bottom: 25px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
h3.${ROOT_CLASS_NAME}-month-title-text {
|
|
31
|
+
font-size: 18px;
|
|
32
|
+
line-height: 23px;
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
color: #000;
|
|
35
|
+
margin: 0px;
|
|
36
|
+
text-align: center;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.${ROOT_CLASS_NAME}-month-week {
|
|
40
|
+
flex: 0 0 auto;
|
|
41
|
+
display: grid;
|
|
42
|
+
grid-column-gap: 0px;
|
|
43
|
+
grid-template-columns: repeat(auto-fit, minmax(46px, 1fr));
|
|
44
|
+
grid-auto-flow: dense;
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
padding-bottom: 12px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.${ROOT_CLASS_NAME}-month-week .week-day-item {
|
|
50
|
+
flex: 1 1 auto;
|
|
51
|
+
display: flex;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
align-items: center;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.${ROOT_CLASS_NAME}-month-week span.week-day-item-text {
|
|
57
|
+
text-align: center;
|
|
58
|
+
font-size: 13px;
|
|
59
|
+
line-height: 21px;
|
|
60
|
+
margin: 0px;
|
|
61
|
+
color: #9a9a9a;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.${ROOT_CLASS_NAME}-month-days {
|
|
65
|
+
flex: 1 1 auto;
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.${ROOT_CLASS_NAME}-month-row {
|
|
71
|
+
flex: 1 1 auto;
|
|
72
|
+
display: grid;
|
|
73
|
+
grid-column-gap: 0px;
|
|
74
|
+
grid-template-columns: repeat(auto-fit, minmax(46px, 1fr));
|
|
75
|
+
grid-auto-flow: dense;
|
|
76
|
+
margin-bottom: 1px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.${ROOT_CLASS_NAME}-month-day-item {
|
|
80
|
+
display: flex;
|
|
81
|
+
justify-content: center;
|
|
82
|
+
align-items: center;
|
|
83
|
+
position: relative;
|
|
84
|
+
height: 46px;
|
|
85
|
+
width: 46px;
|
|
86
|
+
cursor: pointer;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.${ROOT_CLASS_NAME}-month-day-item.selected {
|
|
90
|
+
background-color: #f8f8f8;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.${ROOT_CLASS_NAME}-month-day-item.start-date {
|
|
94
|
+
background-color: #f8f8f8;
|
|
95
|
+
border-top-left-radius: 50%;
|
|
96
|
+
border-bottom-left-radius: 50%;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.${ROOT_CLASS_NAME}-month-day-item.start-date:after {
|
|
100
|
+
background-color: #000;
|
|
101
|
+
opacity: 1;
|
|
102
|
+
visibility: visible;
|
|
103
|
+
}
|
|
104
|
+
.${ROOT_CLASS_NAME}-month-day-item.start-date span.month-day-item-text {
|
|
105
|
+
color: #fff;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.${ROOT_CLASS_NAME}-month-day-item.end-date {
|
|
109
|
+
background-color: #f8f8f8;
|
|
110
|
+
border-top-right-radius: 50%;
|
|
111
|
+
border-bottom-right-radius: 50%;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.${ROOT_CLASS_NAME}-month-day-item.end-date:after {
|
|
115
|
+
background-color: #000;
|
|
116
|
+
opacity: 1;
|
|
117
|
+
visibility: visible;
|
|
118
|
+
}
|
|
119
|
+
.${ROOT_CLASS_NAME}-month-day-item.end-date span.month-day-item-text {
|
|
120
|
+
color: #fff;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.${ROOT_CLASS_NAME}-month-day-item:hover:after {
|
|
124
|
+
opacity: 1;
|
|
125
|
+
visibility: visible;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.${ROOT_CLASS_NAME}-month-day-item:after {
|
|
129
|
+
content: "";
|
|
130
|
+
position: absolute;
|
|
131
|
+
left: 50%;
|
|
132
|
+
top: 50%;
|
|
133
|
+
transform: translate(-50%, -50%);
|
|
134
|
+
pointer-events: none;
|
|
135
|
+
border-radius: 50%;
|
|
136
|
+
border: 1px solid #000;
|
|
137
|
+
width: 43px;
|
|
138
|
+
height: 43px;
|
|
139
|
+
transition: opacity 0.25s ease-in-out;
|
|
140
|
+
opacity: 0;
|
|
141
|
+
visibility: hidden;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.${ROOT_CLASS_NAME}-month-day-item span.month-day-item-text {
|
|
145
|
+
text-align: center;
|
|
146
|
+
font-size: 13px;
|
|
147
|
+
line-height: 21px;
|
|
148
|
+
margin: 0px;
|
|
149
|
+
color: #000;
|
|
150
|
+
position: relative;
|
|
151
|
+
z-index: 1;
|
|
152
|
+
}
|
|
153
|
+
`;
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// The following utils work on a “month” as a unique numerical value
|
|
2
|
+
// representing the number of months since the unix epoch (jan 1970)
|
|
3
|
+
const START_YEAR = 1970;
|
|
4
|
+
const MONTH_NAMES = [
|
|
5
|
+
'January',
|
|
6
|
+
'February',
|
|
7
|
+
'March',
|
|
8
|
+
'April',
|
|
9
|
+
'May',
|
|
10
|
+
'June',
|
|
11
|
+
'July',
|
|
12
|
+
'August',
|
|
13
|
+
'September',
|
|
14
|
+
'October',
|
|
15
|
+
'November',
|
|
16
|
+
'December',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const getYearFromDate = (date: Date) => date.getFullYear() - START_YEAR;
|
|
20
|
+
|
|
21
|
+
export const getMonthFromDate = (date: Date) =>
|
|
22
|
+
date.getMonth() + (getYearFromDate(date) * 12); // prettier-ignore
|
|
23
|
+
|
|
24
|
+
export const getYearFromMonth = (month: number) => Math.floor(month / 12) + START_YEAR;
|
|
25
|
+
|
|
26
|
+
export const getMonthNameFromMonth = (month: number) => MONTH_NAMES[month % 12];
|
|
27
|
+
|
|
28
|
+
export const getMonthAbbreviationFromMonth = (month: number) => {
|
|
29
|
+
const monthName = getMonthNameFromMonth(month);
|
|
30
|
+
if (monthName === 'September') return 'Sept';
|
|
31
|
+
return monthName.substring(0, 3);
|
|
32
|
+
};
|