├── .gitignore
├── .idea
├── $CACHE_FILE$
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── modules.xml
├── react-horizontal-datepicker.iml
└── vcs.xml
├── LICENSE
├── README.md
├── dist
├── components
│ ├── DatePicker.js
│ ├── DatePicker.module.css
│ ├── DateView.js
│ └── MonthView.js
└── global
│ └── helpers
│ └── hexToRgb.js
├── package-lock.json
├── package.json
├── public
└── index.html
├── src
├── App.css
├── App.js
├── components
│ ├── DatePicker.js
│ ├── DatePicker.module.css
│ ├── DateView.js
│ └── MonthView.js
├── global
│ └── helpers
│ │ └── hexToRgb.js
├── index.css
└── index.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 | package-lock.json
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | /.idea/
25 | /.idea/workspace.xml
26 |
--------------------------------------------------------------------------------
/.idea/$CACHE_FILE$:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | CoffeeScript
12 |
13 |
14 | GeneralCoffeeScript
15 |
16 |
17 | GeneralJavaScript
18 |
19 |
20 | HTML
21 |
22 |
23 | JavaScript
24 |
25 |
26 | TypeScript
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/react-horizontal-datepicker.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Kushagra Agrawal
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## react-horizontal-datepicker
2 | #### V2 with new logic and completely removing dependency on react-waypoint as well as leaner code which now uses css-modules
3 |
4 | A simple and lightweight easily style-able Side scrolling datepicker, built with ❤️
5 |
6 | Bundle size of 469 Bytes Minified + Gzipped
7 |
8 | 
9 |
10 | ### Installation
11 |
12 | Run `yarn add react-horizontal-datepicker`
13 | or
14 | Run `npm i react-horizontal-datepicker`
15 |
16 | ### Usage
17 |
18 | Import:
19 |
20 | `import DatePicker from "react-horizontal-datepicker";`
21 |
22 | and simply use the component as:
23 |
24 | ```javascript
25 |
26 | ```
27 |
28 | example at the end
29 |
30 | #### Available Props are
31 |
32 | | Prop | Type | Default | Description |
33 | | ------------- |:-------:| :-------:| ----------- |
34 | | getSelectedDay | Function | | Function to get the selected Day |
35 | | endDate | Number| 90 | Number of days to render from current date |
36 | | selectDate | Date | | prop to send selected date manually or from another calendar component |
37 | | color | String | 'rgb(54, 105, 238)' | Set the primary color can be any color format in string |
38 | | labelFormat | String | 'MMMM yyyy' | Month label format - uses [date-fns format](https://date-fns.org/v1.30.1/docs/format) types |
39 | | marked | Object | | Marking targeted date with text below (Optional) |
40 |
41 | ### Example:
42 |
43 | https://codesandbox.io/s/vigilant-newton-gn0g7
44 |
45 | ```javascript
46 | function App() {
47 |
48 | const selectedDay = (val) =>{
49 | console.log(val)
50 | };
51 |
52 | return (
53 |
59 | );
60 | }
61 | ```
62 |
63 | ### Using marked dates
64 |
65 | Example:
66 |
67 | ```javascript
68 | function App() {
69 |
70 | const selectedDay = (val) =>{
71 | console.log(val)
72 | };
73 |
74 | return (
75 |
98 | );
99 | }
100 | ```
101 |
102 |
103 | ### Todo
104 | use react window for efficienc
--------------------------------------------------------------------------------
/dist/components/DatePicker.js:
--------------------------------------------------------------------------------
1 | function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2 |
3 | /* eslint-disable react-hooks/exhaustive-deps */
4 | import { addDays } from "date-fns";
5 | import React from "react";
6 | import hexToRgb from "../global/helpers/hexToRgb";
7 | import styles from "./DatePicker.module.css";
8 | import { DateView } from "./DateView";
9 | import { MonthView } from './MonthView';
10 |
11 | const DatePicker = props => {
12 | const next = event => {
13 | event.preventDefault();
14 | const e = document.getElementById('container');
15 | const width = e ? e.getBoundingClientRect().width : null;
16 | e.scrollLeft += width - 60;
17 | };
18 |
19 | const prev = event => {
20 | event.preventDefault();
21 | const e = document.getElementById('container');
22 | const width = e ? e.getBoundingClientRect().width : null;
23 | e.scrollLeft -= width - 60;
24 | };
25 |
26 | const primaryColor = props.color ? props.color.indexOf("rgb") > 0 ? props.color : hexToRgb(props.color) : 'rgb(54, 105, 238)';
27 | const startDate = props.startDate || new Date();
28 | const lastDate = addDays(startDate, props.days || 90);
29 | let buttonzIndex = {
30 | zIndex: 2
31 | };
32 | let buttonStyle = {
33 | background: primaryColor
34 | };
35 | let Component = DateView;
36 |
37 | if (props.type === "month") {
38 | buttonzIndex = {
39 | zIndex: 5
40 | };
41 | Component = MonthView;
42 | buttonStyle = {
43 | background: primaryColor,
44 | marginBottom: "5px"
45 | };
46 | }
47 |
48 | return /*#__PURE__*/React.createElement("div", {
49 | className: styles.container
50 | }, /*#__PURE__*/React.createElement("div", {
51 | className: styles.buttonWrapper,
52 | style: buttonzIndex
53 | }, /*#__PURE__*/React.createElement("button", {
54 | className: styles.button,
55 | style: buttonStyle,
56 | onClick: prev
57 | }, "<")), /*#__PURE__*/React.createElement(Component, _extends({}, props, {
58 | primaryColor: primaryColor,
59 | startDate: startDate,
60 | lastDate: lastDate
61 | })), /*#__PURE__*/React.createElement("div", {
62 | className: styles.buttonWrapper,
63 | style: buttonzIndex
64 | }, /*#__PURE__*/React.createElement("button", {
65 | className: styles.button,
66 | style: buttonStyle,
67 | onClick: next
68 | }, ">")));
69 | };
70 |
71 | export { DatePicker };
--------------------------------------------------------------------------------
/dist/components/DatePicker.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | width: 100%;
4 | background: inherit;
5 | }
6 |
7 | .buttonWrapper {
8 | display: flex;
9 | align-items: flex-end;
10 | background: inherit;
11 | }
12 |
13 | .button {
14 | border: none;
15 | text-decoration: none;
16 | cursor: pointer;
17 | border-radius: 50%;
18 | width: 40px;
19 | height: 40px;
20 | color: white;
21 | font-size: 20px;
22 | font-weight: bold;
23 | flex-shrink: 0;
24 | display: flex;
25 | align-items: center;
26 | justify-content: center;
27 | padding: 0;
28 | margin-bottom: 13px;
29 | }
30 | .dateListScrollable {
31 | display: flex;
32 | overflow-x: scroll;
33 | scrollbar-width: none;
34 | margin: 2px 0 2px -40px;
35 | -webkit-overflow-scrolling: touch;
36 | }
37 |
38 | .dateListScrollable::-webkit-scrollbar {
39 | -webkit-appearance: none;
40 | display: none;
41 | }
42 |
43 | .monthContainer {
44 | display: flex;
45 | flex-direction: column;
46 | cursor: pointer;
47 | padding: 2px;
48 | margin: 2px;
49 | }
50 |
51 | .monthYearLabel {
52 | align-self: flex-start;
53 | z-index: 3;
54 | font-size: 15px;
55 | font-weight: bold;
56 | position: sticky;
57 | top: 10px;
58 | left: 0;
59 | width: max-content;
60 | margin: 0 10px;
61 | }
62 |
63 | .dateDayItem{
64 | display: flex;
65 | flex-direction: column;
66 | align-items: center;
67 | cursor: pointer;
68 | margin: 0 0 0 5px;
69 | width: 45px;
70 | height: 49px;
71 | flex-shrink: 0;
72 | }
73 |
74 | .daysContainer {
75 | display: flex;
76 | z-index: 1;
77 | margin-top: 10px;
78 | }
79 |
80 | .dayLabel {
81 | font-size: 12px;
82 | margin: 4px 0 0 0;
83 | }
84 |
85 | .dateLabel {
86 | font-size: 18px;
87 | }
--------------------------------------------------------------------------------
/dist/components/DateView.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | import React, { useEffect, useState } from "react";
3 | import styles from "./DatePicker.module.css";
4 | import { addDays, addMonths, differenceInMonths, format, isSameDay, lastDayOfMonth, startOfMonth } from "date-fns";
5 |
6 | const DateView = ({
7 | startDate,
8 | lastDate,
9 | selectDate,
10 | getSelectedDay,
11 | primaryColor,
12 | labelFormat
13 | }) => {
14 | const [selectedDate, setSelectedDate] = useState(null);
15 | const firstSection = {
16 | marginLeft: '40px'
17 | };
18 | const selectedStyle = {
19 | fontWeight: "bold",
20 | width: "45px",
21 | height: "45px",
22 | borderRadius: "50%",
23 | border: `2px solid ${primaryColor}`,
24 | color: primaryColor
25 | };
26 | const labelColor = {
27 | color: primaryColor
28 | };
29 |
30 | const getStyles = day => {
31 | return isSameDay(day, selectedDate) ? selectedStyle : null;
32 | };
33 |
34 | const getId = day => {
35 | return isSameDay(day, selectedDate) ? 'selected' : "";
36 | };
37 |
38 | const renderDays = () => {
39 | const dayFormat = "E";
40 | const dateFormat = "d";
41 | const months = [];
42 | let days = [];
43 |
44 | for (let i = 0; i <= differenceInMonths(lastDate, startDate); i++) {
45 | let start, end;
46 | const month = startOfMonth(addMonths(startDate, i));
47 | start = i === 0 ? Number(format(startDate, dateFormat)) - 1 : 0;
48 | end = i === differenceInMonths(lastDate, startDate) ? Number(format(lastDate, "d")) : Number(format(lastDayOfMonth(month), "d"));
49 |
50 | for (let j = start; j < end; j++) {
51 | let currentDay = addDays(month, j);
52 | days.push( /*#__PURE__*/React.createElement("div", {
53 | id: `${getId(currentDay)}`,
54 | className: styles.dateDayItem,
55 | style: getStyles(currentDay),
56 | key: currentDay,
57 | onClick: () => onDateClick(currentDay)
58 | }, /*#__PURE__*/React.createElement("div", {
59 | className: styles.dayLabel
60 | }, format(currentDay, dayFormat)), /*#__PURE__*/React.createElement("div", {
61 | className: styles.dateLabel
62 | }, format(currentDay, dateFormat))));
63 | }
64 |
65 | months.push( /*#__PURE__*/React.createElement("div", {
66 | className: styles.monthContainer,
67 | key: month
68 | }, /*#__PURE__*/React.createElement("span", {
69 | className: styles.monthYearLabel,
70 | style: labelColor
71 | }, format(month, labelFormat || "MMMM yyyy")), /*#__PURE__*/React.createElement("div", {
72 | className: styles.daysContainer,
73 | style: i === 0 ? firstSection : null
74 | }, days)));
75 | days = [];
76 | }
77 |
78 | return /*#__PURE__*/React.createElement("div", {
79 | id: "container",
80 | className: styles.dateListScrollable
81 | }, months);
82 | };
83 |
84 | const onDateClick = day => {
85 | setSelectedDate(day);
86 |
87 | if (getSelectedDay) {
88 | getSelectedDay(day);
89 | }
90 | };
91 |
92 | useEffect(() => {
93 | if (getSelectedDay) {
94 | if (selectDate) {
95 | getSelectedDay(selectDate);
96 | } else {
97 | getSelectedDay(startDate);
98 | }
99 | }
100 | }, []);
101 | useEffect(() => {
102 | if (selectDate) {
103 | if (!isSameDay(selectedDate, selectDate)) {
104 | setSelectedDate(selectDate);
105 | setTimeout(() => {
106 | let view = document.getElementById('selected');
107 |
108 | if (view) {
109 | view.scrollIntoView({
110 | behavior: "smooth",
111 | inline: "center",
112 | block: "nearest"
113 | });
114 | }
115 | }, 20);
116 | }
117 | }
118 | }, [selectDate]);
119 | return /*#__PURE__*/React.createElement(React.Fragment, null, renderDays());
120 | };
121 |
122 | export { DateView };
--------------------------------------------------------------------------------
/dist/components/MonthView.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | import React, { useEffect, useState } from "react";
3 | import styles from "./DatePicker.module.css";
4 | import { addMonths, differenceInMonths, format, isSameDay, startOfMonth } from "date-fns";
5 |
6 | const MonthView = ({
7 | startDate,
8 | lastDate,
9 | selectDate,
10 | getSelectedDay,
11 | primaryColor,
12 | labelFormat
13 | }) => {
14 | const [selectedDate, setSelectedDate] = useState(null);
15 | const rgb = primaryColor.replace(/[^\d,]/g, '').split(',');
16 | const brightness = Math.round((parseInt(rgb[0]) * 299 + parseInt(rgb[1]) * 587 + parseInt(rgb[2]) * 114) / 1000);
17 | const textColour = brightness > 125 ? 'black' : 'white';
18 | const selectedStyle = {
19 | borderRadius: "0.7rem",
20 | background: `${primaryColor}`,
21 | color: textColour
22 | };
23 |
24 | const getStyles = day => {
25 | return isSameDay(day, selectedDate) ? selectedStyle : null;
26 | };
27 |
28 | const getId = day => {
29 | return isSameDay(day, selectedDate) ? 'selected' : "";
30 | };
31 |
32 | const renderDays = () => {
33 | const months = [];
34 |
35 | for (let i = 0; i <= differenceInMonths(lastDate, startDate); i++) {
36 | const month = startOfMonth(addMonths(startDate, i));
37 | months.push( /*#__PURE__*/React.createElement("div", {
38 | id: `${getId(month)}`,
39 | className: styles.monthContainer,
40 | key: month,
41 | style: getStyles(month),
42 | onClick: () => onDateClick(month)
43 | }, /*#__PURE__*/React.createElement("span", {
44 | className: styles.monthYearLabel
45 | }, format(month, labelFormat || "MMMM yyyy"))));
46 | }
47 |
48 | return /*#__PURE__*/React.createElement("div", {
49 | id: "container",
50 | className: styles.dateListScrollable
51 | }, months);
52 | };
53 |
54 | const onDateClick = day => {
55 | setSelectedDate(day);
56 |
57 | if (getSelectedDay) {
58 | getSelectedDay(day);
59 | }
60 | };
61 |
62 | useEffect(() => {
63 | if (getSelectedDay) {
64 | if (selectDate) {
65 | getSelectedDay(selectDate);
66 | } else {
67 | getSelectedDay(startDate);
68 | }
69 | }
70 | }, []);
71 | useEffect(() => {
72 | if (selectDate) {
73 | if (!isSameDay(selectedDate, selectDate)) {
74 | setSelectedDate(selectDate);
75 | setTimeout(() => {
76 | let view = document.getElementById('selected');
77 |
78 | if (view) {
79 | view.scrollIntoView({
80 | behavior: "smooth",
81 | inline: "center",
82 | block: "nearest"
83 | });
84 | }
85 | }, 20);
86 | }
87 | }
88 | }, [selectDate]);
89 | return /*#__PURE__*/React.createElement(React.Fragment, null, renderDays());
90 | };
91 |
92 | export { MonthView };
--------------------------------------------------------------------------------
/dist/global/helpers/hexToRgb.js:
--------------------------------------------------------------------------------
1 | export default function hexToRgb(hex) {
2 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
3 | return result ? "rgb(" + parseInt(result[1], 16) + ',' + parseInt(result[2], 16) + ',' + parseInt(result[3], 16) + ")" : null;
4 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-horizontal-datepicker",
3 | "version": "2.0.3",
4 | "homepage": "https://github.com/kush-agra/react-horizontal-datepicker",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/kush-agra/react-horizontal-datepicker.git"
8 | },
9 | "keywords": [
10 | "date-picker",
11 | "horizontal-calendar",
12 | "horizontal-datepicker",
13 | "swipeable",
14 | "side-scrolling",
15 | "calendar-strip",
16 | "web"
17 | ],
18 | "private": false,
19 | "main": "./dist/components/DatePicker.js",
20 | "license": "MIT",
21 | "author": "Kushagra Agrawal",
22 | "babel": {
23 | "presets": [
24 | "@babel/preset-react"
25 | ]
26 | },
27 | "dependencies": {
28 | "date-fns": "^2.14.0"
29 | },
30 | "peerDependencies": {
31 | "react": "^17.0.13",
32 | "react-dom": "^17.0.2",
33 | "react-scripts": "^5.0.1"
34 | },
35 | "scripts": {
36 | "publish:npm": "rm -rf dist && mkdir dist && mkdir dist/components && mkdir dist/global && babel src/components -d dist/components --copy-files && babel src/global -d dist/global --copy-files",
37 | "start": "react-scripts start",
38 | "build": "react-scripts build",
39 | "test": "react-scripts test",
40 | "eject": "react-scripts eject"
41 | },
42 | "eslintConfig": {
43 | "extends": "react-app"
44 | },
45 | "browserslist": {
46 | "production": [
47 | ">0.2%",
48 | "not dead",
49 | "not op_mini all"
50 | ],
51 | "development": [
52 | "last 1 chrome version",
53 | "last 1 firefox version",
54 | "last 1 safari version"
55 | ]
56 | },
57 | "devDependencies": {
58 | "@babel/cli": "^7.10.1",
59 | "@babel/preset-react": "^7.10.1",
60 | "react": "^16.13.1",
61 | "react-dom": "^16.13.1",
62 | "react-scripts": "^3.4.1"
63 | },
64 | "bit": {
65 | "env": {
66 | "compiler": "bit.envs/compilers/react@1.0.13"
67 | },
68 | "componentsDefaultDirectory": "components/{name}",
69 | "packageManager": "npm"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Strip
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | padding: 15px;
3 | background: white;
4 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './App.css';
3 | import { DatePicker } from "./components/DatePicker";
4 |
5 | function App() {
6 | const selectedDay = (val) =>{
7 | console.log(val)
8 | };
9 |
10 | const startDate = new Date(2010, 0, 1);
11 |
12 | return (
13 |
14 |
21 |
22 | );
23 | }
24 |
25 | export default App;
26 |
--------------------------------------------------------------------------------
/src/components/DatePicker.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | import { addDays } from "date-fns";
3 | import React from "react";
4 | import hexToRgb from "../global/helpers/hexToRgb";
5 | import styles from "./DatePicker.module.css"
6 | import { DateView } from "./DateView";
7 | import { MonthView } from './MonthView';
8 |
9 | const DatePicker = (props) => {
10 | const next = (event) => {
11 | event.preventDefault();
12 | const e = document.getElementById('container');
13 | const width = e ? e.getBoundingClientRect().width : null;
14 | e.scrollLeft += width - 60;
15 | };
16 |
17 | const prev = (event) => {
18 | event.preventDefault();
19 | const e = document.getElementById('container');
20 | const width = e ? e.getBoundingClientRect().width : null;
21 | e.scrollLeft -= width - 60;
22 | };
23 |
24 | const primaryColor = props.color? (props.color.indexOf("rgb") > 0?props.color:hexToRgb(props.color)):'rgb(54, 105, 238)';
25 |
26 | const startDate = props.startDate || new Date();
27 | const lastDate = addDays(startDate, props.days || 90);
28 |
29 | let buttonzIndex = {zIndex: 2};
30 | let buttonStyle = {background: primaryColor};
31 | let Component = DateView;
32 |
33 | if (props.type === "month") {
34 | buttonzIndex = {zIndex: 5};
35 | Component = MonthView;
36 | buttonStyle = {background: primaryColor, marginBottom: "5px"};
37 | }
38 |
39 | return (
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export { DatePicker }
--------------------------------------------------------------------------------
/src/components/DatePicker.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | width: 100%;
4 | background: inherit;
5 | }
6 |
7 | .buttonWrapper {
8 | display: flex;
9 | align-items: flex-end;
10 | background: inherit;
11 | }
12 |
13 | .button {
14 | border: none;
15 | text-decoration: none;
16 | cursor: pointer;
17 | border-radius: 50%;
18 | width: 40px;
19 | height: 40px;
20 | color: white;
21 | font-size: 20px;
22 | font-weight: bold;
23 | flex-shrink: 0;
24 | display: flex;
25 | align-items: center;
26 | justify-content: center;
27 | padding: 0;
28 | margin-bottom: 13px;
29 | }
30 | .dateListScrollable {
31 | display: flex;
32 | overflow-x: scroll;
33 | scrollbar-width: none;
34 | margin: 2px 0 2px -40px;
35 | -webkit-overflow-scrolling: touch;
36 | }
37 |
38 | .dateListScrollable::-webkit-scrollbar {
39 | -webkit-appearance: none;
40 | display: none;
41 | }
42 |
43 | .monthContainer {
44 | display: flex;
45 | flex-direction: column;
46 | cursor: pointer;
47 | padding: 2px;
48 | margin: 2px;
49 | }
50 |
51 | .monthYearLabel {
52 | align-self: flex-start;
53 | z-index: 3;
54 | font-size: 15px;
55 | font-weight: bold;
56 | position: sticky;
57 | top: 10px;
58 | left: 0;
59 | width: max-content;
60 | margin: 0 10px;
61 | }
62 |
63 | .dateDayItem {
64 | display: flex;
65 | flex-direction: column;
66 | align-items: center;
67 | cursor: pointer;
68 | margin: 0 0 0 5px;
69 | width: 45px;
70 | height: 49px;
71 | flex-shrink: 0;
72 | }
73 |
74 | .dateDayItemMarked {
75 | display: flex;
76 | flex-direction: column;
77 | align-items: center;
78 | cursor: pointer;
79 | margin: 0 0 0 5px;
80 | width: 45px;
81 | height: 70px;
82 | flex-shrink: 0;
83 | }
84 |
85 | .daysContainer {
86 | display: flex;
87 | z-index: 1;
88 | margin-top: 10px;
89 | }
90 |
91 | .dayLabel {
92 | font-size: 12px;
93 | margin: 4px 0 0 0;
94 | }
95 |
96 | .dateLabel {
97 | font-size: 18px;
98 | }
99 |
100 | .markedLabel {
101 | margin-top: 10px;
102 | }
--------------------------------------------------------------------------------
/src/components/DateView.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | import React, {useEffect, useState} from "react";
3 | import styles from "./DatePicker.module.css"
4 | import {
5 | addDays,
6 | addMonths,
7 | differenceInMonths,
8 | format,
9 | isSameDay,
10 | lastDayOfMonth,
11 | startOfMonth
12 | } from "date-fns";
13 |
14 |
15 | const DateView = ({startDate, lastDate, selectDate, getSelectedDay, primaryColor, labelFormat, marked}) => {
16 | const [selectedDate, setSelectedDate] = useState(null);
17 | const firstSection = {marginLeft: '40px'};
18 | const selectedStyle = {fontWeight:"bold",width:"45px",height:"45px",borderRadius:"50%",border:`2px solid ${primaryColor}`,color:primaryColor};
19 | const labelColor = {color: primaryColor};
20 | const markedStyle = {color: "#8c3737", padding: "2px", fontSize: 12};
21 |
22 | const getStyles = (day) => {
23 | return isSameDay(day, selectedDate)?selectedStyle:null;
24 | };
25 |
26 | const getId = (day) => {
27 | return isSameDay(day, selectedDate)?'selected':"";
28 | };
29 |
30 | const getMarked = (day) => {
31 | let markedRes = marked?.find(i => isSameDay(i.date, day));
32 | if (markedRes) {
33 | if (!markedRes?.marked) {
34 | return;
35 | }
36 |
37 | return
38 | {markedRes.text}
39 |
;
40 | }
41 |
42 | return "";
43 | };
44 |
45 | const renderDays = () => {
46 | const dayFormat = "E";
47 | const dateFormat = "d";
48 |
49 | const months = [];
50 | let days = [];
51 |
52 | // const styleItemMarked = marked ? styles.dateDayItemMarked : styles.dateDayItem;
53 |
54 | for (let i = 0; i <= differenceInMonths(lastDate, startDate); i++) {
55 | let start, end;
56 | const month = startOfMonth(addMonths(startDate, i));
57 |
58 | start = i === 0 ? Number(format(startDate, dateFormat)) - 1 : 0;
59 | end = i === differenceInMonths(lastDate, startDate) ? Number(format(lastDate, "d")) : Number(format(lastDayOfMonth(month), "d"));
60 |
61 | for (let j = start; j < end; j++) {
62 | let currentDay = addDays(month, j);
63 |
64 | days.push(
65 | onDateClick(currentDay)}
70 | >
71 |
{format(currentDay, dayFormat)}
72 |
{format(currentDay, dateFormat)}
73 | {getMarked(currentDay)}
74 |
75 | );
76 | }
77 | months.push(
78 |
81 |
82 | {format(month, labelFormat || "MMMM yyyy")}
83 |
84 |
85 | {days}
86 |
87 |
88 | );
89 | days = [];
90 |
91 | }
92 |
93 | return {months}
;
94 | }
95 |
96 | const onDateClick = day => {
97 | setSelectedDate(day);
98 | if (getSelectedDay) {
99 | getSelectedDay(day);
100 | }
101 | };
102 |
103 | useEffect(() => {
104 | if (getSelectedDay) {
105 | if (selectDate) {
106 | getSelectedDay(selectDate);
107 | } else {
108 | getSelectedDay(startDate);
109 | }
110 | }
111 | }, []);
112 |
113 | useEffect(() => {
114 | if (selectDate) {
115 | if (!isSameDay(selectedDate, selectDate)) {
116 | setSelectedDate(selectDate);
117 | setTimeout(() => {
118 | let view = document.getElementById('selected');
119 | if (view) {
120 | view.scrollIntoView({behavior: "smooth", inline: "center", block: "nearest"});
121 | }
122 | }, 20);
123 | }
124 | }
125 | }, [selectDate]);
126 |
127 | return {renderDays()}
128 | }
129 |
130 |
131 |
132 |
133 | export { DateView }
134 |
--------------------------------------------------------------------------------
/src/components/MonthView.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | import React, {useEffect, useState} from "react";
3 | import styles from "./DatePicker.module.css"
4 | import {
5 | addMonths,
6 | differenceInMonths,
7 | format,
8 | isSameDay,
9 | startOfMonth
10 | } from "date-fns";
11 |
12 |
13 | const MonthView = ({startDate, lastDate, selectDate, getSelectedDay, primaryColor, labelFormat}) => {
14 | const [selectedDate, setSelectedDate] = useState(null);
15 | const rgb = primaryColor.replace(/[^\d,]/g, '').split(',');
16 | const brightness = Math.round(((parseInt(rgb[0]) * 299) +
17 | (parseInt(rgb[1]) * 587) +
18 | (parseInt(rgb[2]) * 114)) / 1000);
19 | const textColour = (brightness > 125) ? 'black' : 'white';
20 |
21 | const selectedStyle = {borderRadius:"0.7rem",background:`${primaryColor}`, color: textColour};
22 |
23 | const getStyles = (day) => {
24 | return isSameDay(day, selectedDate)?selectedStyle:null;
25 | };
26 |
27 | const getId = (day) => {
28 | return isSameDay(day, selectedDate)?'selected':"";
29 | };
30 |
31 | const renderDays = () => {
32 |
33 | const months = [];
34 |
35 | for (let i = 0; i <= differenceInMonths(lastDate, startDate); i++) {
36 | const month = startOfMonth(addMonths(startDate, i));
37 | months.push(
38 | onDateClick(month)}
43 | >
44 |
45 | {format(month, labelFormat || "MMMM yyyy")}
46 |
47 |
48 | );
49 | }
50 |
51 | return {months}
;
52 | }
53 |
54 | const onDateClick = day => {
55 | setSelectedDate(day);
56 | if (getSelectedDay) {
57 | getSelectedDay(day);
58 | }
59 | };
60 |
61 | useEffect(() => {
62 | if (getSelectedDay) {
63 | if (selectDate) {
64 | getSelectedDay(selectDate);
65 | } else {
66 | getSelectedDay(startDate);
67 | }
68 | }
69 | }, []);
70 |
71 | useEffect(() => {
72 | if (selectDate) {
73 | if (!isSameDay(selectedDate, selectDate)) {
74 | setSelectedDate(selectDate);
75 | setTimeout(() => {
76 | let view = document.getElementById('selected');
77 | if (view) {
78 | view.scrollIntoView({behavior: "smooth", inline: "center", block: "nearest"});
79 | }
80 | }, 20);
81 | }
82 | }
83 | }, [selectDate]);
84 |
85 | return {renderDays()}
86 | }
87 |
88 |
89 |
90 |
91 | export { MonthView }
--------------------------------------------------------------------------------
/src/global/helpers/hexToRgb.js:
--------------------------------------------------------------------------------
1 | export default function hexToRgb(hex) {
2 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
3 | return result ? "rgb("
4 | + parseInt(result[1], 16) + ','
5 | + parseInt(result[2], 16) + ','
6 | + parseInt(result[3], 16) + ")" : null;
7 | }
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 |
8 |
--------------------------------------------------------------------------------