├── .editorconfig ├── .eslintrc ├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── doc.md ├── doc ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── Components │ │ ├── _category_.json │ │ ├── calendar.md │ │ ├── datePicker.md │ │ ├── definedRangeSelector.md │ │ └── rangeCalendar.md │ ├── GettingStarted │ │ ├── _category_.json │ │ └── installation.md │ ├── Hooks │ │ ├── _category_.json │ │ └── useCalendarData.md │ ├── Inprogress │ │ ├── _category_.json │ │ └── doc.md │ └── intro.md ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src │ ├── components │ │ ├── HomepageFeatures.js │ │ └── HomepageFeatures.module.css │ ├── css │ │ ├── custom.css │ │ └── react-patro.css │ ├── pages │ │ ├── index.js │ │ ├── index.module.css │ │ └── markdown-page.md │ └── theme │ │ └── ReactLiveScope │ │ └── index.js ├── static │ ├── .nojekyll │ └── img │ │ ├── docusaurus.png │ │ ├── favicon.ico │ │ ├── logo.svg │ │ ├── red-bg-logo.svg │ │ ├── transparent-bg-logo.png │ │ ├── transparent-bg-logo.svg │ │ ├── transparent-bg-repo.png │ │ ├── tutorial │ │ ├── docsVersionDropdown.png │ │ └── localeDropdown.png │ │ ├── undraw_docusaurus_mountain.svg │ │ ├── undraw_docusaurus_react.svg │ │ └── undraw_docusaurus_tree.svg └── yarn.lock ├── example ├── .env ├── .gitignore ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── react-app-env.d.ts │ ├── serviceWorker.js │ └── setupTests.js └── tsconfig.json ├── media └── img │ ├── red-bg-logo.png │ ├── repo-beta.svg │ ├── repo.png │ ├── repo.svg │ ├── transparent-bg-logo.png │ └── transparent-bg-repo.png ├── package.json ├── rollup.config.js ├── src ├── AdBsDateRenderer.tsx ├── Calendar │ ├── DateRender.tsx │ ├── Header.tsx │ ├── RangeRender.tsx │ ├── RenderReference.tsx │ ├── index.tsx │ ├── useCalendarData.ts │ ├── useSelectedData.ts │ └── util.ts ├── CalendarData │ ├── calculation.ts │ ├── data.ts │ ├── format.ts │ ├── getBsData.ts │ ├── index.ts │ ├── parser.ts │ └── validator.ts ├── DatePicker │ ├── index.tsx │ ├── useOnClickAway.ts │ └── usePopper.ts ├── NepaliDateRangePicker.tsx ├── Range │ ├── PreLabeled.tsx │ ├── index.tsx │ └── useDateRange.ts ├── assets │ ├── CalendarIcon.tsx │ └── CrossIcon.tsx ├── date-fns │ └── index.ts ├── doc.md ├── hooks │ ├── index.ts │ └── useCalendarType.ts ├── index.ts ├── nepali_date_picker.css ├── react-app-env.d.ts ├── styles.css ├── types │ └── main.d.ts └── utils.ts ├── tsconfig.json ├── why-package.md └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": ["eslint:recommended", "prettier", "react-app"], 4 | "env": { 5 | "es6": true, 6 | "node": true 7 | }, 8 | "rules": { 9 | "prettier/prettier": "error" 10 | }, 11 | "plugins": ["prettier"] 12 | } 13 | -------------------------------------------------------------------------------- /.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 | /dist 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | firebase-debug.log* -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | example -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/.prettierrc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Raralabs 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 | ![repo-logo](media/img/repo-beta.svg "react-patro") 2 | 3 | ![npm-version](https://img.shields.io/npm/v/react-patro) ![minified-size](https://img.shields.io/bundlephobia/min/react-patro) ![licence](https://img.shields.io/npm/l/react-patro) ![Pull Reqest](https://img.shields.io/badge/PRs-Welcome-blue) 4 | 5 | ## The project is still under active development. So, don't use it on production. `PRs are welcome`. 6 | 7 | --- 8 | 9 | AD and BS Calendar functions as hooks and component 10 | 11 | # [Demo](https://react-patro.netlify.app/) 12 | 13 | # Installation 14 | 15 | The package can be installed via [npm](https://github.com/npm/cli) 16 | 17 | ```cli 18 | npm install @raralsbs/react-patro --save 19 | ``` 20 | 21 | or via [yarn](https://github.com/yarnpkg/yarn) 22 | 23 | ```cli 24 | yarn add @raralsbs/react-patro 25 | ``` 26 | 27 | ## css 28 | 29 | You need to import `css` separately in the project. This is provided separately, so you can override the css as per your wish. Css customization is stil under progress. 30 | 31 | ```jsx 32 | import "react-patro/src/styles.css"; 33 | ``` 34 | 35 | ## Documentation 36 | 37 | Documentation and demo available at [here](https://react-patro.netlify.app/) 38 | 39 | ## How to start for development 40 | 41 | run the react-patro source code. This generates dist folder 42 | 43 | ```javascript 44 | npm run dev 45 | 46 | ``` 47 | 48 | then go to example folder which is basically create-react-app template where react-patro is installed from the local react-patro source code. Here you can play around with the code 49 | 50 | ```javascript 51 | cd example && npm i 52 | npm start 53 | ``` 54 | 55 | ## How to remove Ugly error message ( for development) 56 | 57 | Comment the terser `rollup.config.js` from line 33 & 8 . and rebuild the code.The should remove the code uglifying process. 58 | 59 | ## License 60 | 61 | React Patro is [MIT Licensed](LICENSE) 62 | -------------------------------------------------------------------------------- /doc.md: -------------------------------------------------------------------------------- 1 | ## TODO LIST 2 | 3 | - [x] Prepare Basic UI 4 | - [x] Month Next Previous 5 | - [x] Year Next Previous 6 | - [x] Date select and selected date in view 7 | - [x] Select date for next or previous month 8 | - [x] Dropdown for year select 9 | - [x] Implement calendar in date picker 10 | - [x] AD/BS switcher 11 | - [x] AD/BS accept both value (AD as primary) 12 | - [x] AD/BS sends both value 13 | - [x] Implement date format usage 14 | - [ ] Month => displaying, full , short, min based on params 15 | - [ ] year => displaying based on range provided 16 | - [ ] defaultValue => allow for Date String if no dateFormat is passed 17 | - [ ] Customizable styles 18 | 19 |
20 | 21 | # [Demo](https://ad-bs-datepicker.firebaseapp.com/) 22 | 23 | # React AD BS Date picker 24 | 25 | `Available Variants as of now:` 26 | 27 | 1. Calendar -> Renders Calendar Layout (The basic building block); 28 | 2. DatePicker -> Input field with Calendar on popup 29 | 3. RangePicker -> Two Calendar Layout side by side for range selection 30 | 4. DefinedRangeSelector -> TODO 31 | 5. //TODO 32 | 33 | ## Data passed/entered (Value) -Input 34 | 35 | The Calendar component can be both controlled and uncontrolled. If value is passed, defaultValue is ignored. 36 | 37 | - **`value`** : string (formatted according to dateFormat) 38 | - **`defaultValue`**: string (formatted according to dateFormat) 39 | 40 | --- 41 | 42 | ## Calendar Configuration 43 | 44 | - **`calendarType`** - "AD"| "BS" default= //TODO 45 | 46 | Though all inner operations are done in AD format, this configuration differentiates the rendering date value. If AD is passed then AD calendar is rendered and if BS is passed then BS calendar is rendered. 47 | 48 | > Based on this props, the input and ouput values also differ. 49 | 50 | --- 51 | 52 |
53 | 54 | ## Data Selection/Change - Output 55 | 56 | For calendarLayout 57 | **`onSelect`** (formattedDate: string,adDate: DateType,bsDate: DateType, dateString:Date)=>void; 58 | 59 | 1. The **formattedDate** will be string formatted according to the **`dateFormat`** provided and **`calendarType`**. If **`calendarType`** is BS then the output will be tha formatted string of BS Date and if it is AD then the output will the formatted string of AD Date. 60 | 2. The **adDate** //TODO for now object with property date,year,month is sent 61 | 3. The **bsDate** //TODO for now object with property date,year, month is sent 62 | 63 |
64 | 65 | **`onChange`** (formattedDate: string,adDate: DateType,bsDate: DateType, dateString:Date)=>void; 66 | 67 | > All the arguments are same for both onSelect and onChange. onSelect is available in Calendar while onChange is avaliale in DatePicker 68 | 69 | --- 70 | 71 | ## Formatting 72 | 73 | **`dateFormat`**: string combination of y,m and d 74 | 75 | default: "" //TODO 76 | 77 | y - Year, m - Month, d- Day 78 | 79 | Acceptable format are the permutation of "yyyy", "mm","m","d","dd" 80 | Internally the date format is lowercased so any case is acceptable. All the date received from the library will eventually be formatted according to this props. 81 | 82 | > Also make sure to send all the dates **`defaultValue`**,**`maxDate`**,**`minDate`**, **`value`** to be in the format passed in **`dateFormat`** props 83 | 84 | --- 85 | 86 | ## Disable Configuration 87 | 88 | 1. ### `maxDate`:string 89 | 90 | > The maximum Date allowed. Beyond this date, everything is disabled. Make sure to pass the date formatted according to the dateFormat Props 91 | 92 |
93 | 94 | 2. ### `minDate`:string 95 | 96 | > The minimum Date allowed. All Dates before the date are disabled. Make sure to pass the date formatted according to the dateFormat props 97 | 98 |
99 | 100 | 3. ### `disablePast`:boolean 101 | 102 | > Disables all the dates before today's date; 103 | 104 |
105 | 106 | 4. ### `disableFuture`:boolean 107 | 108 | > Disables all the dates after today's date; 109 | 110 |
111 | 112 | 5. ### `disableDate`: (formattedDate: string,adDate: DateType,bsDate: DateType, dateString:Date) => boolean; 113 | 114 | > Use your own custom function to disable The date. All the arguments received on the callback are same as that of **onSelect** props 115 | 116 | --- 117 | 118 | ## DropDown 119 | 120 | 1. ### `showMonthDropdown` : boolean | "full" | "short" | "min" 121 | 122 | **default** = false 123 | 124 | > passing full, short and min is still not supported 125 | 126 | 2. ### `showYearDropdown` : boolean | [min:number , max:number] 127 | 128 | **default** = false 129 | 130 | > passing min and max array is sitll not supported 131 | 132 | --- 133 | 134 | ## Range 135 | 136 | //TODO 137 | 138 | ## DefinedRangeSelector 139 | 140 | has some predefined ranges set by passing props. 141 | 142 | // TODO support plain array like [-15,-30,-7,-25] 143 | 144 | **`definedRanges`** : [{ label:string,offset:number|IDateoffset }] 145 | 146 | **`basedate`** : string 147 | 148 | This prop contains the predefined ranges that allows the selection of ranges 149 | 150 | - **`label`** => (unique) This label is used as **key** in the list and so is supposed to be unique. The label will the the description rendered on the screen 151 | - **`offset`** => offset can be either the object of shape _**{date, month, year}**_ or a single number. if a single number is provided then it is assumed as _**date**_ offset. The offset will be used to calculate the selection range based on baseDate provided. 152 | 153 | baseDate => The base by which the range selection is calcuated using the offset on definedRanges. default => Today. `baseDate` is supposed to be string formatted according to the dateFormat provided. 154 | 155 | for example: 156 | 157 | ```javascript 158 | 159 | const definedRanges = [ 160 | { 161 | label: "Last Year", 162 | offset: { year: -1 }, 163 | }, 164 | { 165 | label: "Last 15 days", 166 | offset: -15, 167 | }, 168 | { 169 | label: "Last 7 days", 170 | offset: -7, 171 | }, 172 | { 173 | label: "Last 25 days", 174 | offset: -25, 175 | }, 176 | { 177 | label: "Last 30 days", 178 | offset: -30, 179 | }, 180 | { 181 | label: "Last 45 days", 182 | offset: -45, 183 | }, 184 | { 185 | label: "Next week", 186 | offset: 7, 187 | }, 188 | ]; 189 | const baseDate = "2020-10-12"; 190 | 191 | /// Rendered output 192 | Last Year 2019-10-12 - 2020-10-12 193 | Last 15 days 2020-09-27 - 2020-10-12 194 | Last 7 days 2020-10-05 - 2020-10-12 195 | Last 25 days 2020-09-17 - 2020-10-12 196 | Last 30 days 2020-09-12 - 2020-10-12 197 | Last 45 days 2020-08-28 - 2020-10-12 198 | Next week 2020-10-12 - 2020-10-19 199 | /// 200 | ``` 201 | 202 | ## Terms: 203 | 204 | -parse => Convert date string to date object using the date format for example: "2020-10-12" => {year: 2020, month:10, date:12} given format ("YYYY-MM-DD"); 205 | -format => Opposite of parse. Converts date object to date string. for example: {year: 2020, month:10, date:12}=>"2020-10-12" => given format ("YYYY-MM-DD"); 206 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ## Installation 6 | 7 | ```console 8 | yarn install 9 | ``` 10 | 11 | ## Local Development 12 | 13 | ```console 14 | yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ## Build 20 | 21 | ```console 22 | yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ## Deployment 28 | 29 | ```console 30 | GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /doc/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /doc/docs/Components/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Components", 3 | "position": 3 4 | } 5 | -------------------------------------------------------------------------------- /doc/docs/Components/calendar.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Calendar 6 | 7 | ```jsx live 8 | function PlayGround(props) { 9 | // const [selectedDate, setSelectedDate] = useState("2021-07-03"); 10 | 11 | return ( 12 | date === "07-03-2021"} 26 | onSelect={(formattedDate, adDate, bsDate, date) => { 27 | // setSelectedDate(formattedDate); 28 | }} 29 | /> 30 | ); 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /doc/docs/Components/datePicker.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # DatePicker 6 | 7 | ```jsx live 8 | function PlayGround(props) { 9 | const [dateBS, setDateBS] = useState(""); 10 | 11 | return ( 12 |
13 |

BS Date Picker

14 |

Selected Date: {JSON.stringify(dateBS)}

15 | New Date Picker 16 |
17 |
18 | { 21 | console.log("val", val); 22 | setDateBS(val); 23 | }} 24 | /> 25 |
26 |
27 |
28 | ); 29 | } 30 | ``` 31 | 32 | AD Date Picker 33 | 34 | ```jsx live 35 | function PlayGround(props) { 36 | const [date, setDate] = useState(""); 37 | 38 | return ( 39 |
40 |

AD Date Picker

41 |

Selected Date: {JSON.stringify(date)}

42 | New Date Picker 43 |
44 |
45 | { 49 | setDate(val); 50 | }} 51 | /> 52 |
53 |
54 |
55 | ); 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /doc/docs/Components/definedRangeSelector.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Range Calendar 6 | 7 | ```jsx live 8 | function PlayGround(props) { 9 | const [definedRangeSelector, setDefinedRangeSelector] = useState({ 10 | from: "", 11 | to: "", 12 | }); 13 | return ( 14 |
15 |

Defined Range Selector

16 |

17 | Base Date: 2021-09-14 Selected Range: {definedRangeSelector.from} -{" "} 18 | {definedRangeSelector.to} 19 |

20 |
21 | { 27 | setDefinedRangeSelector({ from: dateFrom, to: dateTo }); 28 | }} 29 | /> 30 |
31 |
32 | ); 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /doc/docs/Components/rangeCalendar.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Range Calendar 6 | 7 | ```jsx live 8 | function PlayGround(props) { 9 | const [selectedDateRange, setSelectedDateRange] = useState({ 10 | from: "", 11 | to: "", 12 | }); 13 | return ( 14 |
15 |

Ad Date Range

16 |

17 | Selected Range: {selectedDateRange.from} - {selectedDateRange.to} 18 |

19 |
20 | { 23 | setSelectedDateRange({ from, to }); 24 | }} 25 | /> 26 |
27 |
28 | ); 29 | } 30 | ``` 31 | 32 | AD Date Picker 33 | 34 | ```jsx live 35 | function PlayGround(props) { 36 | const [selectedDateRangeBs, setSelectedDateRangeBs] = useState({ 37 | from: "", 38 | to: "", 39 | }); 40 | 41 | return ( 42 |
43 | BS DAte RAnge 44 | { 46 | setSelectedDateRangeBs({ from, to }); 47 | }} 48 | calendarType="BS" 49 | /> 50 |

51 | Selected Range: {selectedDateRangeBs.from} - {selectedDateRangeBs.to} 52 |

53 |
54 | ); 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /doc/docs/GettingStarted/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Getting started", 3 | "position": 2 4 | } 5 | -------------------------------------------------------------------------------- /doc/docs/GettingStarted/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Installation 6 | 7 | The package can be installed via [npm](https://github.com/npm/cli) 8 | 9 | ```cli 10 | npm install @raralsbs/react-patro --save 11 | ``` 12 | 13 | or via [yarn](https://github.com/yarnpkg/yarn) 14 | 15 | ```cli 16 | yarn add @raralsbs/react-patro 17 | ``` 18 | 19 | ## css 20 | 21 | You need to import `css` separately in the project. This is provided separately, so you can override the css as per your wish. Css customization is stil under progress. 22 | 23 | ```jsx 24 | import "react-patro/src/styles.css"; 25 | ``` 26 | 27 | 31 | -------------------------------------------------------------------------------- /doc/docs/Hooks/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Hooks", 3 | "position": 3 4 | } 5 | -------------------------------------------------------------------------------- /doc/docs/Hooks/useCalendarData.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # useCalendarData 6 | -------------------------------------------------------------------------------- /doc/docs/Inprogress/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Documentation In progress", 3 | "position": 4 4 | } 5 | -------------------------------------------------------------------------------- /doc/docs/Inprogress/doc.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # React AD BS Date picker 6 | 7 | `Available Variants as of now:` 8 | 9 | 1. Calendar -> Renders Calendar Layout (The basic building block); 10 | 2. DatePicker -> Input field with Calendar on popup 11 | 3. RangePicker -> Two Calendar Layout side by side for range selection 12 | 4. DefinedRangeSelector -> TODO 13 | 5. //TODO 14 | 15 | ## Data passed/entered (Value) -Input 16 | 17 | The Calendar component can be both controlled and uncontrolled. If value is passed, defaultValue is ignored. 18 | 19 | - **`value`** : string (formatted according to dateFormat) 20 | - **`defaultValue`**: string (formatted according to dateFormat) 21 | 22 | --- 23 | 24 | ## Calendar Configuration 25 | 26 | - **`calendarType`** - "AD"| "BS" default= //TODO 27 | 28 | Though all inner operations are done in AD format, this configuration differentiates the rendering date value. If AD is passed then AD calendar is rendered and if BS is passed then BS calendar is rendered. 29 | 30 | > Based on this props, the input and ouput values also differ. 31 | 32 | --- 33 | 34 |
35 | 36 | ## Data Selection/Change - Output 37 | 38 | For calendarLayout 39 | **`onSelect`** (formattedDate: string,adDate: DateType,bsDate: DateType, dateString:Date)=>void; 40 | 41 | 1. The **formattedDate** will be string formatted according to the **`dateFormat`** provided and **`calendarType`**. If **`calendarType`** is BS then the output will be tha formatted string of BS Date and if it is AD then the output will the formatted string of AD Date. 42 | 2. The **adDate** //TODO for now object with property date,year,month is sent 43 | 3. The **bsDate** //TODO for now object with property date,year, month is sent 44 | 45 |
46 | 47 | **`onChange`** (formattedDate: string,adDate: DateType,bsDate: DateType, dateString:Date)=>void; 48 | 49 | > All the arguments are same for both onSelect and onChange. onSelect is available in Calendar while onChange is avaliale in DatePicker 50 | 51 | --- 52 | 53 | ## Formatting 54 | 55 | **`dateFormat`**: string combination of y,m and d 56 | 57 | default: "" //TODO 58 | 59 | y - Year, m - Month, d- Day 60 | 61 | Acceptable format are the permutation of "yyyy", "mm","m","d","dd" 62 | Internally the date format is lowercased so any case is acceptable. All the date received from the library will eventually be formatted according to this props. 63 | 64 | > Also make sure to send all the dates **`defaultValue`**,**`maxDate`**,**`minDate`**, **`value`** to be in the format passed in **`dateFormat`** props 65 | 66 | --- 67 | 68 | ## Disable Configuration 69 | 70 | 1. ### `maxDate`:string 71 | 72 | > The maximum Date allowed. Beyond this date, everything is disabled. Make sure to pass the date formatted according to the dateFormat Props 73 | 74 |
75 | 76 | 2. ### `minDate`:string 77 | 78 | > The minimum Date allowed. All Dates before the date are disabled. Make sure to pass the date formatted according to the dateFormat props 79 | 80 |
81 | 82 | 3. ### `disablePast`:boolean 83 | 84 | > Disables all the dates before today's date; 85 | 86 |
87 | 88 | 4. ### `disableFuture`:boolean 89 | 90 | > Disables all the dates after today's date; 91 | 92 |
93 | 94 | 5. ### `disableDate`: (formattedDate: string,adDate: DateType,bsDate: DateType, dateString:Date) => boolean; 95 | 96 | > Use your own custom function to disable The date. All the arguments received on the callback are same as that of **onSelect** props 97 | 98 | --- 99 | 100 | ## DropDown 101 | 102 | 1. ### `showMonthDropdown` : boolean | "full" | "short" | "min" 103 | 104 | **default** = false 105 | 106 | > passing full, short and min is still not supported 107 | 108 | 2. ### `showYearDropdown` : boolean | [min:number , max:number] 109 | 110 | **default** = false 111 | 112 | > passing min and max array is sitll not supported 113 | 114 | --- 115 | 116 | ## Range 117 | 118 | //TODO 119 | 120 | ## DefinedRangeSelector 121 | 122 | has some predefined ranges set by passing props. 123 | 124 | // TODO support plain array like [-15,-30,-7,-25] 125 | 126 | **`definedRanges`** : [{ label:string,offset:number|IDateoffset }] 127 | 128 | **`basedate`** : string 129 | 130 | This prop contains the predefined ranges that allows the selection of ranges 131 | 132 | - **`label`** => (unique) This label is used as **key** in the list and so is supposed to be unique. The label will the the description rendered on the screen 133 | - **`offset`** => offset can be either the object of shape _**{date, month, year}**_ or a single number. if a single number is provided then it is assumed as _**date**_ offset. The offset will be used to calculate the selection range based on baseDate provided. 134 | 135 | baseDate => The base by which the range selection is calcuated using the offset on definedRanges. default => Today. `baseDate` is supposed to be string formatted according to the dateFormat provided. 136 | 137 | for example: 138 | 139 | ```javascript 140 | 141 | const definedRanges = [ 142 | { 143 | label: "Last Year", 144 | offset: { year: -1 }, 145 | }, 146 | { 147 | label: "Last 15 days", 148 | offset: -15, 149 | }, 150 | { 151 | label: "Last 7 days", 152 | offset: -7, 153 | }, 154 | { 155 | label: "Last 25 days", 156 | offset: -25, 157 | }, 158 | { 159 | label: "Last 30 days", 160 | offset: -30, 161 | }, 162 | { 163 | label: "Last 45 days", 164 | offset: -45, 165 | }, 166 | { 167 | label: "Next week", 168 | offset: 7, 169 | }, 170 | ]; 171 | const baseDate = "2020-10-12"; 172 | 173 | /// Rendered output 174 | Last Year 2019-10-12 - 2020-10-12 175 | Last 15 days 2020-09-27 - 2020-10-12 176 | Last 7 days 2020-10-05 - 2020-10-12 177 | Last 25 days 2020-09-17 - 2020-10-12 178 | Last 30 days 2020-09-12 - 2020-10-12 179 | Last 45 days 2020-08-28 - 2020-10-12 180 | Next week 2020-10-12 - 2020-10-19 181 | /// 182 | ``` 183 | 184 | ## Terms: 185 | 186 | -parse => Convert date string to date object using the date format for example: "2020-10-12" => {year: 2020, month:10, date:12} given format ("YYYY-MM-DD"); 187 | -format => Opposite of parse. Converts date object to date string. for example: {year: 2020, month:10, date:12}=>"2020-10-12" => given format ("YYYY-MM-DD"); 188 | -------------------------------------------------------------------------------- /doc/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Overview 6 | 7 | React-patro is a collection of functions, hooks and components for both AD and BS calendar. These functions and hooks can be used independently to create your own designs of calendar though we try to provide our own simplistic implementation as well which is totally customization. You can perform date conversions, manipulate date formats and render a datepicker and calendar. 8 | -------------------------------------------------------------------------------- /doc/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | const lightCodeTheme = require("prism-react-renderer/themes/github"); 2 | const darkCodeTheme = require("prism-react-renderer/themes/dracula"); 3 | 4 | /** @type {import('@docusaurus/types').DocusaurusConfig} */ 5 | module.exports = { 6 | title: "React Patro", 7 | tagline: "AD and BS Calendar functions as hooks and component", 8 | url: "https://react-patro.netlify.com", 9 | baseUrl: "/", 10 | onBrokenLinks: "throw", 11 | onBrokenMarkdownLinks: "warn", 12 | favicon: "img/favicon.ico", 13 | organizationName: "Rara Labs", 14 | projectName: "react-patro", 15 | themeConfig: { 16 | navbar: { 17 | title: "React Patro", 18 | logo: { 19 | alt: "react-patro logo", 20 | src: "img/logo.svg", 21 | }, 22 | items: [ 23 | { 24 | type: "doc", 25 | docId: "intro", 26 | position: "left", 27 | label: "Tutorial", 28 | }, 29 | { 30 | href: "https://github.com/raralabs/react-patro", 31 | label: "GitHub", 32 | position: "right", 33 | }, 34 | ], 35 | }, 36 | footer: { 37 | style: "dark", 38 | 39 | copyright: `Copyright © ${new Date().getFullYear()} Rara Labs. Built with Docusaurus.`, 40 | }, 41 | prism: { 42 | theme: lightCodeTheme, 43 | darkTheme: darkCodeTheme, 44 | }, 45 | }, 46 | presets: [ 47 | [ 48 | "@docusaurus/preset-classic", 49 | { 50 | docs: { 51 | sidebarPath: require.resolve("./sidebars.js"), 52 | editUrl: "https://github.com/raralabs/react-patro/edit/master/doc/", 53 | }, 54 | theme: { 55 | customCss: require.resolve("./src/css/custom.css"), 56 | }, 57 | }, 58 | ], 59 | ], 60 | themes: ["@docusaurus/theme-live-codeblock"], 61 | }; 62 | -------------------------------------------------------------------------------- /doc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "doc", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "2.0.0-beta.3", 18 | "@docusaurus/preset-classic": "2.0.0-beta.3", 19 | "@docusaurus/theme-live-codeblock": "^2.0.0-beta.3", 20 | "@mdx-js/react": "^1.6.21", 21 | "@svgr/webpack": "^5.5.0", 22 | "clsx": "^1.1.1", 23 | "file-loader": "^6.2.0", 24 | "prism-react-renderer": "^1.2.1", 25 | "react": "file:../node_modules/react", 26 | "react-dom": "file:../node_modules/react-dom", 27 | "url-loader": "^4.1.1", 28 | "react-patro": "file:../dist/" 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.5%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /doc/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | module.exports = { 13 | // By default, Docusaurus generates a sidebar from the docs folder structure 14 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 15 | 16 | // But you can create a sidebar manually 17 | /* 18 | tutorialSidebar: [ 19 | { 20 | type: 'category', 21 | label: 'Tutorial', 22 | items: ['hello'], 23 | }, 24 | ], 25 | */ 26 | }; 27 | -------------------------------------------------------------------------------- /doc/src/components/HomepageFeatures.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import clsx from "clsx"; 3 | import styles from "./HomepageFeatures.module.css"; 4 | 5 | const FeatureList = [ 6 | { 7 | title: "Ready to use functions", 8 | description: ( 9 | <> 10 | React patro provides ready to use functions for date manipulations for 11 | both bs and ad dates. You can simply import them from react-patro and 12 | use them per your use. 13 | 14 | ), 15 | }, 16 | { 17 | title: "React hooks for customization", 18 | description: ( 19 | <> 20 | React patro provides performant hooks for populating your own calendar 21 | design and carry out other ad to bs conversion 22 | 23 | ), 24 | }, 25 | { 26 | title: "AD & BS Calendar functionality", 27 | description: ( 28 | <> 29 | Now you don't need to use two different packages for ad and bs calendar 30 | rendering. 31 | 32 | ), 33 | }, 34 | ]; 35 | 36 | function Feature({ Svg, title, description }) { 37 | return ( 38 |
39 |
40 |

{title}

41 |

{description}

42 |
43 |
44 | ); 45 | } 46 | 47 | export default function HomepageFeatures() { 48 | return ( 49 |
50 |
51 |
52 | {FeatureList.map((props, idx) => ( 53 | 54 | ))} 55 |
56 |
57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /doc/src/components/HomepageFeatures.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | 3 | .features { 4 | display: flex; 5 | align-items: center; 6 | padding: 2rem 0; 7 | width: 100%; 8 | } 9 | 10 | .featureSvg { 11 | height: 200px; 12 | width: 200px; 13 | } 14 | -------------------------------------------------------------------------------- /doc/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: #990000; 11 | --ifm-color-primary-dark: rgb(33, 175, 144); 12 | --ifm-color-primary-darker: rgb(31, 165, 136); 13 | --ifm-color-primary-darkest: rgb(26, 136, 112); 14 | --ifm-color-primary-light: rgb(70, 203, 174); 15 | --ifm-color-primary-lighter: rgb(102, 212, 189); 16 | --ifm-color-primary-lightest: rgb(146, 224, 208); 17 | --ifm-code-font-size: 95%; 18 | } 19 | 20 | .docusaurus-highlight-code-line { 21 | background-color: rgba(0, 0, 0, 0.1); 22 | display: block; 23 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 24 | padding: 0 var(--ifm-pre-padding); 25 | } 26 | 27 | html[data-theme="dark"] .docusaurus-highlight-code-line { 28 | background-color: rgba(0, 0, 0, 0.3); 29 | } 30 | 31 | .hero--primary { 32 | background: #990000aa; 33 | text-align: left; 34 | } 35 | -------------------------------------------------------------------------------- /doc/src/css/react-patro.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --cl-primary: #3851a3; 3 | --cl-primary2: rgba(56, 81, 163, 0.2); 4 | --cl-danger: #f15c61; 5 | --cl-success: #3fbeb2; 6 | --cl-secondary: #878f9d; 7 | --font-xs: 0.64rem; 8 | --font-sm: 0.8rem; 9 | --font-md: 1rem; 10 | --font-lg: 1.25rem; 11 | --font-xl: 1.563rem; 12 | --font-xxl: 2rem; 13 | --font-r: 0.875rem; 14 | 15 | --gray90: #1d2530; 16 | --gray80: #343c46; 17 | --gray60: #636972; 18 | --gray50: #91979f; 19 | --gray20: #afb5bb; 20 | --gray10: #cbd0d6; 21 | --gray5: #eef2f7; 22 | --debit: #143e9f; 23 | --link: #143e9f; 24 | --credit: #fc814a; 25 | --white: #fff; 26 | --danger: #ff6b6b; 27 | } 28 | 29 | .font-sm { 30 | font-size: var(--font-sm); 31 | } 32 | .font-md { 33 | font-size: var(--font-md); 34 | } 35 | .gray-60 { 36 | color: var(--gray60); 37 | } 38 | .gray-20 { 39 | color: var(--gray20); 40 | } 41 | .flex { 42 | display: flex; 43 | } 44 | .input-wrapper { 45 | position: relative; 46 | display: inline-block; 47 | width: 100%; 48 | min-width: 0; 49 | padding: 4px 11px; 50 | color: rgba(0, 0, 0, 0.65); 51 | font-size: 14px; 52 | line-height: 1.5715; 53 | background-color: #fff; 54 | background-image: none; 55 | border: 1px solid #d9d9d9; 56 | border-radius: 2px; 57 | -webkit-transition: all 0.3s; 58 | transition: all 0.3s; 59 | display: -ms-inline-flexbox; 60 | display: inline-flex; 61 | } 62 | .input-wrapper input { 63 | padding: 0; 64 | border: none; 65 | outline: none; 66 | } 67 | .hand-cursor { 68 | cursor: pointer !important; 69 | } 70 | 71 | .rl-nepali-calendar__month-select { 72 | color: black; 73 | outline: none; 74 | } 75 | .rl-nepali-datepicker-icon { 76 | width: 16px; 77 | filter: invert(59%) sepia(19%) saturate(232%) hue-rotate(179deg) 78 | brightness(93%) contrast(86%); 79 | } 80 | 81 | .rl-nepali-datepicker-wrapper { 82 | position: relative; 83 | /* width: fit-content; */ 84 | } 85 | 86 | .rl-nepali-datepicker-input { 87 | width: 100%; 88 | } 89 | 90 | .popovercalendar .ant-popover-inner-content { 91 | padding: 0px; 92 | } 93 | 94 | .today-btn { 95 | color: var(--cl-primary); 96 | font-weight: 600; 97 | } 98 | 99 | .rl-nepali-datepicker-content { 100 | position: absolute; 101 | z-index: 4; 102 | box-shadow: 0px 4px 10px rgba(19, 31, 54, 0.25); 103 | background-color: white; 104 | top: 24px; 105 | right: 0px; 106 | } 107 | 108 | .rl-nepali-datepicker-wrapper ul { 109 | list-style-type: none; 110 | } 111 | 112 | .rl-nepali-date-panel-wrapper { 113 | display: flex; 114 | flex-direction: row; 115 | flex-wrap: nowrap; 116 | } 117 | 118 | .rl-nepali-date-referenc-list { 119 | background-color: white; 120 | width: 120px; 121 | padding-top: 20px; 122 | font-weight: normal; 123 | font-size: 12.8px; 124 | line-height: 15px; 125 | /* identical to box height */ 126 | /* Theme_Blue */ 127 | color: var(--cl-primary); 128 | overflow-y: scroll; 129 | padding-left: 10px; 130 | } 131 | .reference-item { 132 | margin-bottom: 20px; 133 | cursor: pointer; 134 | } 135 | 136 | .rl-nepali-date-panel { 137 | width: 320px; 138 | z-index: 4; 139 | background-color: white; 140 | } 141 | 142 | .rl-nepali-date-body { 143 | padding: 8px 12px; 144 | } 145 | 146 | .rl-nepali-date-panel .left-actions div, 147 | .rl-nepali-date-panel .right-actions div { 148 | color: #ddd; 149 | } 150 | 151 | .rl-nepali-date-panel .left-actions div:hover, 152 | .rl-nepali-date-panel .right-actions div:hover { 153 | color: white; 154 | } 155 | 156 | .rl-nepali-date-panel .rl-nepalo-date-body { 157 | padding: 8px 12px; 158 | } 159 | 160 | /* Month header */ 161 | 162 | .month-header { 163 | /* width: 100%; */ 164 | background: var(--cl-primary); 165 | text-align: center; 166 | padding: 12px 8px; 167 | color: white; 168 | display: flex; 169 | flex-direction: row; 170 | align-items: center; 171 | justify-content: space-between; 172 | } 173 | 174 | .month-header .left-actions, 175 | .month-header .right-actions { 176 | display: flex; 177 | flex-direction: row; 178 | align-items: center; 179 | } 180 | 181 | .month-header .left-actions :first-child, 182 | .month-header .right-actions :first-child { 183 | margin-right: 12px; 184 | } 185 | 186 | .rl-nepali-date-content { 187 | width: 100%; 188 | } 189 | 190 | .rl-nepali-date-content thead th { 191 | font-size: 12px; 192 | } 193 | 194 | .rl-nepali-date-content th, 195 | .rl-nepali-date-content td { 196 | color: black; 197 | font-weight: 400; 198 | } 199 | 200 | .rl-picker-cell { 201 | /* border-radius: 2px; */ 202 | padding: 2px; 203 | cursor: pointer; 204 | } 205 | 206 | .rl-picker-cell .rl-picker-cell-inner { 207 | position: relative; 208 | /* z-index: 2; */ 209 | margin: auto; 210 | min-width: 30px; 211 | width: inherit; 212 | text-align: center; 213 | height: 30px; 214 | line-height: 100%; 215 | border-radius: 2px; 216 | -webkit-transition: background 0.3s, border 0.3s; 217 | transition: background 0.3s, border 0.3s; 218 | font-size: 14; 219 | display: flex; 220 | align-items: center; 221 | justify-content: center; 222 | } 223 | 224 | .rl-picker-cell .rl-picker-cell-inner .BS { 225 | font-size: 14px; 226 | } 227 | 228 | .rl-picker-cell .rl-picker-cell-inner .AD { 229 | position: absolute; 230 | right: 2px; 231 | bottom: 0px; 232 | font-size: 7px; 233 | } 234 | 235 | .rl-picker-cell:not(.disabled).in-range { 236 | background-color: var(--cl-primary2); 237 | } 238 | 239 | .rl-picker-cell:not(.disabled):hover .rl-picker-cell-inner { 240 | background-color: var(--cl-primary2); 241 | } 242 | 243 | .rl-picker-cell:not(.disabled).active .rl-picker-cell-inner { 244 | background-color: var(--cl-primary); 245 | color: white; 246 | } 247 | 248 | .rl-picker-cell:not(.disabled).today .rl-picker-cell-inner { 249 | border-color: var(--cl-primary); 250 | border: 1px solid; 251 | } 252 | 253 | .rl-picker-cell:not(.disabled).other-month .rl-picker-cell-inner { 254 | color: rgba(0, 0, 0, 0.4); 255 | /* background-color:var(--cl-secondary) ; */ 256 | } 257 | 258 | .rl-picker-cell.disabled { 259 | color: var(--cl-secondary); 260 | cursor: not-allowed; 261 | background-color: #ddd; 262 | } 263 | 264 | /* INLINE DROPDOWN CSS START*/ 265 | 266 | .inline-dropdown { 267 | position: relative; 268 | display: inline-block; 269 | z-index: 2; 270 | padding: 0; 271 | margin: 0; 272 | outline: 0; 273 | text-align: left; 274 | } 275 | 276 | .inline-dropdown:focus { 277 | pointer-events: none; 278 | } 279 | 280 | .inline-dropdown:focus .inline-dropdown-container { 281 | opacity: 1; 282 | visibility: visible; 283 | } 284 | 285 | .inline-dropdown-container { 286 | width: auto; 287 | margin: 3px 0 0 0; 288 | padding: 10px; 289 | border-radius: 3px; 290 | border-radius: 3px; 291 | pointer-events: auto; 292 | position: absolute; 293 | z-index: 5; 294 | opacity: 0; 295 | visibility: hidden; 296 | transition: visibility 1s; 297 | height: 200px; 298 | overflow-y: scroll; 299 | background-color: white; 300 | box-shadow: 0px 4px 10px rgba(19, 31, 54, 0.25); 301 | border-radius: 4px; 302 | color: black; 303 | } 304 | 305 | .inline-dropdown-container .inline-dropdown-item { 306 | cursor: pointer; 307 | padding-left: 8px; 308 | padding-right: 8px; 309 | } 310 | 311 | .inline-dropdown-container .inline-dropdown-item:hover { 312 | background-color: var(--cl-primary2); 313 | } 314 | 315 | .inline-dropdown-container .inline-dropdown-item.selected { 316 | background-color: var(--cl-primary); 317 | color: white; 318 | } 319 | 320 | /* INLINE DROPDOWN CSS END */ 321 | 322 | .rl-range-calendar { 323 | display: flex; 324 | flex-wrap: nowrap; 325 | width: fit-content; 326 | } 327 | 328 | .rl-nepali-rangepicker-wrapper .input-split { 329 | background-color: #fff; 330 | } 331 | 332 | .rl-nepali-rangepicker-wrapper .input-right { 333 | border-left-width: 0; 334 | } 335 | 336 | .rl-nepali-rangepicker-wrapper .input-right:hover, 337 | .rl-nepali-rangepicker-wrapper .input-right:focus { 338 | border-left-width: 1px; 339 | } 340 | 341 | .rl-daterange-toggler { 342 | display: flex; 343 | flex-wrap: nowrap; 344 | width: fit-content; 345 | /* background-color: var(--cl-success); */ 346 | } 347 | 348 | .rl-daterange-toggler .rl-daterange-toggler-content { 349 | display: flex; 350 | flex-wrap: nowrap; 351 | width: fit-content; 352 | margin-left: 8px; 353 | } 354 | 355 | .rl-daterange-toggler .rl-daterange-toggler-content .label-body { 356 | margin-left: 12px; 357 | margin-right: 12px; 358 | } 359 | 360 | .rl-daterange-toggler .rl-daterange-toggler-content .switch { 361 | width: 20px; 362 | height: 20px; 363 | border-radius: 50%; 364 | background-color: var(--cl-primary2); 365 | display: flex; 366 | justify-content: center; 367 | align-items: center; 368 | } 369 | 370 | .rl-daterange-toggler .selector-main { 371 | display: flex; 372 | } 373 | 374 | .rl-daterange-toggler .down-arrow { 375 | font-size: 110%; 376 | line-height: 0.9rem; 377 | margin-left: 8px; 378 | } 379 | 380 | .selector-content-wrapper .selector-content { 381 | display: flex; 382 | flex-direction: column; 383 | } 384 | 385 | .selector-content-wrapper { 386 | display: flex; 387 | } 388 | 389 | .cross-icon { 390 | position: absolute; 391 | z-index: 1; 392 | background: var(--cl-secondary); 393 | font-size: 10px; 394 | line-height: 100%; 395 | display: flex; 396 | justify-content: center; 397 | align-items: center; 398 | border-radius: 50%; 399 | color: white; 400 | opacity: 0; 401 | width: 10px; 402 | height: 10px; 403 | top: 50%; 404 | transform: translateY(-50%); 405 | right: 5px; 406 | padding: 0px; 407 | margin: 0px; 408 | font-size: 12px; 409 | padding: 2px; 410 | opacity: 1; 411 | } 412 | .rl-defined-range-selector { 413 | z-index: 4; 414 | background-color: white; 415 | padding: 4px 6px; 416 | font-weight: 400; 417 | width: fit-content; 418 | display: flex; 419 | } 420 | 421 | .rl-defined-range-selector .rl-defined-range-label-wrapper { 422 | display: flex; 423 | flex-direction: column; 424 | border-right: 1px solid var(--gray20); 425 | margin-right: 10px; 426 | width: 300px; 427 | } 428 | .rl-defined-range-selector 429 | .rl-defined-range-label-wrapper 430 | .rl-defined-range-label { 431 | padding: 12px; 432 | } 433 | .rl-defined-range-selector 434 | .rl-defined-range-label-wrapper 435 | .rl-defined-range-label:hover { 436 | background: var(--cl-primary2); 437 | cursor: pointer; 438 | } 439 | 440 | .rl-defined-range-label-selected { 441 | background: var(--cl-primary2) !important; 442 | } 443 | -------------------------------------------------------------------------------- /doc/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import clsx from "clsx"; 3 | import Layout from "@theme/Layout"; 4 | import Link from "@docusaurus/Link"; 5 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 6 | import styles from "./index.module.css"; 7 | import HomepageFeatures from "../components/HomepageFeatures"; 8 | import "../css/react-patro.css"; 9 | function HomepageHeader() { 10 | const { siteConfig } = useDocusaurusContext(); 11 | return ( 12 |
13 |
14 |

{siteConfig.title}

15 |

{siteConfig.tagline}

16 |
17 | 21 | Tutorial 22 | 23 |
24 |
25 |
26 | ); 27 | } 28 | 29 | export default function Home() { 30 | const { siteConfig } = useDocusaurusContext(); 31 | return ( 32 | 36 | 37 |
38 | 39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /doc/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | 3 | /** 4 | * CSS files with the .module.css suffix will be treated as CSS modules 5 | * and scoped locally. 6 | */ 7 | 8 | .heroBanner { 9 | padding: 4rem 0; 10 | text-align: center; 11 | position: relative; 12 | overflow: hidden; 13 | background: "#990000"; 14 | } 15 | 16 | @media screen and (max-width: 966px) { 17 | .heroBanner { 18 | padding: 2rem; 19 | } 20 | } 21 | 22 | .buttons { 23 | display: flex; 24 | align-items: center; 25 | justify-content: center; 26 | } 27 | -------------------------------------------------------------------------------- /doc/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /doc/src/theme/ReactLiveScope/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import React from "react"; 9 | import { 10 | DatePicker, 11 | NepaliCalendar, 12 | RangeCalendar, 13 | DefinedRangeCalendar, 14 | } from "react-patro"; 15 | 16 | // Add react-live imports you need here 17 | const ReactLiveScope = { 18 | React, 19 | ...React, 20 | NepaliCalendar, 21 | DatePicker, 22 | RangeCalendar, 23 | DefinedRangeCalendar, 24 | }; 25 | 26 | export default ReactLiveScope; 27 | -------------------------------------------------------------------------------- /doc/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/doc/static/.nojekyll -------------------------------------------------------------------------------- /doc/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/doc/static/img/docusaurus.png -------------------------------------------------------------------------------- /doc/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/doc/static/img/favicon.ico -------------------------------------------------------------------------------- /doc/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /doc/static/img/red-bg-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /doc/static/img/transparent-bg-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/doc/static/img/transparent-bg-logo.png -------------------------------------------------------------------------------- /doc/static/img/transparent-bg-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /doc/static/img/transparent-bg-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/doc/static/img/transparent-bg-repo.png -------------------------------------------------------------------------------- /doc/static/img/tutorial/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/doc/static/img/tutorial/docsVersionDropdown.png -------------------------------------------------------------------------------- /doc/static/img/tutorial/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/doc/static/img/tutorial/localeDropdown.png -------------------------------------------------------------------------------- /example/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV = development -------------------------------------------------------------------------------- /example/.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 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "homepage": ".", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "start": "node ../node_modules/react-scripts/bin/react-scripts.js start", 8 | "build": "node ../node_modules/react-scripts/bin/react-scripts.js build", 9 | "test": "node ../node_modules/react-scripts/bin/react-scripts.js test", 10 | "eject": "node ../node_modules/react-scripts/bin/react-scripts.js eject" 11 | }, 12 | "dependencies": { 13 | "@testing-library/jest-dom": "file:../node_modules/@testing-library/jest-dom", 14 | "@testing-library/react": "file:../node_modules/@testing-library/react", 15 | "@testing-library/user-event": "file:../node_modules/@testing-library/user-event", 16 | "@types/jest": "file:../node_modules/@types/jest", 17 | "@types/node": "file:../node_modules/@types/node", 18 | "@types/react": "file:../node_modules/@types/react", 19 | "@types/react-dom": "file:../node_modules/@types/react-dom", 20 | "react": "file:../node_modules/react", 21 | "react-dom": "file:../node_modules/react-dom", 22 | "react-scripts": "file:../node_modules/react-scripts", 23 | "react-patro": "file:.." 24 | }, 25 | "devDependencies": { 26 | "@babel/plugin-syntax-object-rest-spread": "^7.8.3" 27 | }, 28 | "eslintConfig": { 29 | "extends": "react-app" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/example/public/favicon.ico -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/example/public/logo192.png -------------------------------------------------------------------------------- /example/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/example/public/logo512.png -------------------------------------------------------------------------------- /example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /example/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /example/src/App.css: -------------------------------------------------------------------------------- 1 | 2 | :root { 3 | --cl-primary: #3851a3; 4 | --cl-primary2: rgba(56, 81, 163, 0.2); 5 | --cl-danger: #f15c61; 6 | --cl-success: #3fbeb2; 7 | --cl-secondary: #878f9d; 8 | --font-xs: 0.64rem; 9 | --font-sm: 0.8rem; 10 | --font-md: 1rem; 11 | --font-lg: 1.25rem; 12 | --font-xl: 1.563rem; 13 | --font-xxl: 2rem; 14 | --font-r: 0.875rem; 15 | 16 | --gray90: #1d2530; 17 | --gray80: #343c46; 18 | --gray60: #636972; 19 | --gray50: #91979f; 20 | --gray20: #afb5bb; 21 | --gray10: #cbd0d6; 22 | --gray5: #eef2f7; 23 | --debit: #143e9f; 24 | --link: #143e9f; 25 | --credit: #fc814a; 26 | --white: #fff; 27 | --danger: #ff6b6b; 28 | } 29 | 30 | .font-sm { 31 | font-size: var(--font-sm); 32 | } 33 | .font-md { 34 | font-size: var(--font-md); 35 | } 36 | .gray-60 { 37 | color: var(--gray60); 38 | } 39 | .gray-20 { 40 | color: var(--gray20); 41 | } 42 | .flex { 43 | display: flex; 44 | } 45 | .input-wrapper { 46 | position: relative; 47 | display: inline-block; 48 | width: 100%; 49 | min-width: 0; 50 | padding: 4px 11px; 51 | color: rgba(0, 0, 0, 0.65); 52 | font-size: 14px; 53 | line-height: 1.5715; 54 | background-color: #fff; 55 | background-image: none; 56 | border: 1px solid #d9d9d9; 57 | border-radius: 2px; 58 | -webkit-transition: all 0.3s; 59 | transition: all 0.3s; 60 | display: -ms-inline-flexbox; 61 | display: inline-flex; 62 | } 63 | .input-wrapper input { 64 | padding: 0; 65 | border: none; 66 | outline: none; 67 | } 68 | .hand-cursor { 69 | cursor: pointer !important; 70 | } 71 | 72 | .rl-nepali-calendar__month-select { 73 | color: black; 74 | outline: none; 75 | } 76 | .rl-nepali-datepicker-icon { 77 | width: 16px; 78 | filter: invert(59%) sepia(19%) saturate(232%) hue-rotate(179deg) 79 | brightness(93%) contrast(86%); 80 | } 81 | 82 | .rl-nepali-datepicker-wrapper { 83 | position: relative; 84 | /* width: fit-content; */ 85 | } 86 | 87 | .rl-nepali-datepicker-input { 88 | width: 100%; 89 | } 90 | 91 | .popovercalendar .ant-popover-inner-content { 92 | padding: 0px; 93 | } 94 | 95 | .today-btn { 96 | color: var(--cl-primary); 97 | font-weight: 600; 98 | } 99 | 100 | .rl-nepali-datepicker-content { 101 | position: absolute; 102 | z-index: 4; 103 | box-shadow: 0px 4px 10px rgba(19, 31, 54, 0.25); 104 | background-color: white; 105 | top: 24px; 106 | right: 0px; 107 | } 108 | 109 | .rl-nepali-datepicker-wrapper ul { 110 | list-style-type: none; 111 | } 112 | 113 | .rl-nepali-date-panel-wrapper { 114 | display: flex; 115 | flex-direction: row; 116 | flex-wrap: nowrap; 117 | } 118 | 119 | .rl-nepali-date-referenc-list { 120 | background-color: white; 121 | width: 120px; 122 | padding-top: 20px; 123 | font-weight: normal; 124 | font-size: 12.8px; 125 | line-height: 15px; 126 | /* identical to box height */ 127 | /* Theme_Blue */ 128 | color: var(--cl-primary); 129 | overflow-y: scroll; 130 | padding-left: 10px; 131 | } 132 | .reference-item { 133 | margin-bottom: 20px; 134 | cursor: pointer; 135 | } 136 | 137 | .rl-nepali-date-panel { 138 | width: 320px; 139 | z-index: 4; 140 | background-color: white; 141 | } 142 | 143 | .rl-nepali-date-body { 144 | padding: 8px 12px; 145 | } 146 | 147 | .rl-nepali-date-panel .left-actions div, 148 | .rl-nepali-date-panel .right-actions div { 149 | color: #ddd; 150 | } 151 | 152 | .rl-nepali-date-panel .left-actions div:hover, 153 | .rl-nepali-date-panel .right-actions div:hover { 154 | color: white; 155 | } 156 | 157 | .rl-nepali-date-panel .rl-nepalo-date-body { 158 | padding: 8px 12px; 159 | } 160 | 161 | /* Month header */ 162 | 163 | .month-header { 164 | /* width: 100%; */ 165 | background: var(--cl-primary); 166 | text-align: center; 167 | padding: 12px 8px; 168 | color: white; 169 | display: flex; 170 | flex-direction: row; 171 | align-items: center; 172 | justify-content: space-between; 173 | } 174 | 175 | .month-header .left-actions, 176 | .month-header .right-actions { 177 | display: flex; 178 | flex-direction: row; 179 | align-items: center; 180 | } 181 | 182 | .month-header .left-actions :first-child, 183 | .month-header .right-actions :first-child { 184 | margin-right: 12px; 185 | } 186 | 187 | .rl-nepali-date-content { 188 | width: 100%; 189 | } 190 | 191 | .rl-nepali-date-content thead th { 192 | font-size: 12px; 193 | } 194 | 195 | .rl-nepali-date-content th, 196 | .rl-nepali-date-content td { 197 | color: black; 198 | font-weight: 400; 199 | } 200 | 201 | .rl-picker-cell { 202 | /* border-radius: 2px; */ 203 | padding: 2px; 204 | cursor: pointer; 205 | } 206 | 207 | .rl-picker-cell .rl-picker-cell-inner { 208 | position: relative; 209 | /* z-index: 2; */ 210 | margin: auto; 211 | min-width: 30px; 212 | width: inherit; 213 | text-align: center; 214 | height: 30px; 215 | line-height: 100%; 216 | border-radius: 2px; 217 | -webkit-transition: background 0.3s, border 0.3s; 218 | transition: background 0.3s, border 0.3s; 219 | font-size: 14; 220 | display: flex; 221 | align-items: center; 222 | justify-content: center; 223 | } 224 | 225 | .rl-picker-cell .rl-picker-cell-inner .BS { 226 | font-size: 14px; 227 | } 228 | 229 | .rl-picker-cell .rl-picker-cell-inner .AD { 230 | position: absolute; 231 | right: 2px; 232 | bottom: 0px; 233 | font-size: 7px; 234 | } 235 | 236 | .rl-picker-cell:not(.disabled).in-range { 237 | background-color: var(--cl-primary2); 238 | } 239 | 240 | .rl-picker-cell:not(.disabled):hover .rl-picker-cell-inner { 241 | background-color: var(--cl-primary2); 242 | } 243 | 244 | .rl-picker-cell:not(.disabled).active .rl-picker-cell-inner { 245 | background-color: var(--cl-primary); 246 | color: white; 247 | } 248 | 249 | .rl-picker-cell:not(.disabled).today .rl-picker-cell-inner { 250 | border-color: var(--cl-primary); 251 | border: 1px solid; 252 | } 253 | 254 | .rl-picker-cell:not(.disabled).other-month .rl-picker-cell-inner { 255 | color: rgba(0, 0, 0, 0.4); 256 | /* background-color:var(--cl-secondary) ; */ 257 | } 258 | 259 | .rl-picker-cell.disabled { 260 | color: var(--cl-secondary); 261 | cursor: not-allowed; 262 | background-color: #ddd; 263 | } 264 | 265 | /* INLINE DROPDOWN CSS START*/ 266 | 267 | .inline-dropdown { 268 | position: relative; 269 | display: inline-block; 270 | z-index: 2; 271 | padding: 0; 272 | margin: 0; 273 | outline: 0; 274 | text-align: left; 275 | } 276 | 277 | .inline-dropdown:focus { 278 | pointer-events: none; 279 | } 280 | 281 | .inline-dropdown:focus .inline-dropdown-container { 282 | opacity: 1; 283 | visibility: visible; 284 | } 285 | 286 | .inline-dropdown-container { 287 | width: auto; 288 | margin: 3px 0 0 0; 289 | padding: 10px; 290 | border-radius: 3px; 291 | border-radius: 3px; 292 | pointer-events: auto; 293 | position: absolute; 294 | z-index: 5; 295 | opacity: 0; 296 | visibility: hidden; 297 | transition: visibility 1s; 298 | height: 200px; 299 | overflow-y: scroll; 300 | background-color: white; 301 | box-shadow: 0px 4px 10px rgba(19, 31, 54, 0.25); 302 | border-radius: 4px; 303 | color: black; 304 | } 305 | 306 | .inline-dropdown-container .inline-dropdown-item { 307 | cursor: pointer; 308 | padding-left: 8px; 309 | padding-right: 8px; 310 | } 311 | 312 | .inline-dropdown-container .inline-dropdown-item:hover { 313 | background-color: var(--cl-primary2); 314 | } 315 | 316 | .inline-dropdown-container .inline-dropdown-item.selected { 317 | background-color: var(--cl-primary); 318 | color: white; 319 | } 320 | 321 | /* INLINE DROPDOWN CSS END */ 322 | 323 | .rl-range-calendar { 324 | display: flex; 325 | flex-wrap: nowrap; 326 | width: fit-content; 327 | } 328 | 329 | .rl-nepali-rangepicker-wrapper .input-split { 330 | background-color: #fff; 331 | } 332 | 333 | .rl-nepali-rangepicker-wrapper .input-right { 334 | border-left-width: 0; 335 | } 336 | 337 | .rl-nepali-rangepicker-wrapper .input-right:hover, 338 | .rl-nepali-rangepicker-wrapper .input-right:focus { 339 | border-left-width: 1px; 340 | } 341 | 342 | .rl-daterange-toggler { 343 | display: flex; 344 | flex-wrap: nowrap; 345 | width: fit-content; 346 | /* background-color: var(--cl-success); */ 347 | } 348 | 349 | .rl-daterange-toggler .rl-daterange-toggler-content { 350 | display: flex; 351 | flex-wrap: nowrap; 352 | width: fit-content; 353 | margin-left: 8px; 354 | } 355 | 356 | .rl-daterange-toggler .rl-daterange-toggler-content .label-body { 357 | margin-left: 12px; 358 | margin-right: 12px; 359 | } 360 | 361 | .rl-daterange-toggler .rl-daterange-toggler-content .switch { 362 | width: 20px; 363 | height: 20px; 364 | border-radius: 50%; 365 | background-color: var(--cl-primary2); 366 | display: flex; 367 | justify-content: center; 368 | align-items: center; 369 | } 370 | 371 | .rl-daterange-toggler .selector-main { 372 | display: flex; 373 | } 374 | 375 | .rl-daterange-toggler .down-arrow { 376 | font-size: 110%; 377 | line-height: 0.9rem; 378 | margin-left: 8px; 379 | } 380 | 381 | .selector-content-wrapper .selector-content { 382 | display: flex; 383 | flex-direction: column; 384 | } 385 | 386 | .selector-content-wrapper { 387 | display: flex; 388 | } 389 | 390 | .cross-icon { 391 | position: absolute; 392 | z-index: 1; 393 | background: var(--cl-secondary); 394 | font-size: 10px; 395 | line-height: 100%; 396 | display: flex; 397 | justify-content: center; 398 | align-items: center; 399 | border-radius: 50%; 400 | color: white; 401 | opacity: 0; 402 | width: 10px; 403 | height: 10px; 404 | top: 50%; 405 | transform:translateY(-50%); 406 | right: 5px; 407 | padding: 0px; 408 | margin: 0px; 409 | font-size: 12px; 410 | padding: 2px; 411 | opacity: 1; 412 | 413 | } 414 | .rl-defined-range-selector { 415 | z-index: 4; 416 | background-color: white; 417 | padding: 4px 6px; 418 | font-weight: 400; 419 | width: fit-content; 420 | display: flex; 421 | } 422 | 423 | .rl-defined-range-selector .rl-defined-range-label-wrapper { 424 | display: flex; 425 | flex-direction: column; 426 | border-right: 1px solid var(--gray20); 427 | margin-right: 10px; 428 | width: 300px; 429 | } 430 | .rl-defined-range-selector 431 | .rl-defined-range-label-wrapper 432 | .rl-defined-range-label { 433 | padding: 12px; 434 | } 435 | .rl-defined-range-selector 436 | .rl-defined-range-label-wrapper 437 | .rl-defined-range-label:hover { 438 | background: var(--cl-primary2); 439 | cursor: pointer; 440 | } 441 | 442 | .rl-defined-range-label-selected { 443 | background: var(--cl-primary2) !important; 444 | } 445 | -------------------------------------------------------------------------------- /example/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | // import CustomDateRangeToggler from "./nepali_date_picker/custom_daterange_toggler"; 4 | 5 | // import AdBsDateRenderer from "nepalicalendar/NepaliDatePicker/AdBsDateRenderer"; 6 | // import NepaliCalendar from "nepalicalendar/Calendar"; 7 | // import DatePicker from "nepalicalendar/DatePicker"; 8 | // import NepaliCalendarRange from "nepalicalendar/Range"; 9 | // import PreLabeledRange from "nepalicalendar/Range/PreLabeled"; 10 | 11 | import { 12 | DatePicker, 13 | NepaliCalendar, 14 | RangeCalendar, 15 | DefinedRangeCalendar, 16 | } from "react-patro"; 17 | 18 | // import { 19 | // ad2bs, 20 | // bs2ad, 21 | // isBsDateValid, 22 | // isInValidRange, 23 | // } from "nepalicalendar/CalendarData"; 24 | // import parseDate from "nepalicalendar/CalendarData/parser"; 25 | // import format from "nepalicalendar/CalendarData/format"; 26 | // import { isAdDateValid } from "nepalicalendar/CalendarData/validator"; 27 | 28 | import "./App.css"; 29 | 30 | const App = () => { 31 | const [date, setDate] = useState(""); 32 | const [dateBS, setDateBS] = useState("01-01-2021"); 33 | 34 | const [selectedDate, setSelectedDate] = useState("2021-07-03"); 35 | 36 | const [selectedDateBS, setSelectedDateBS] = useState("2078-10-12"); 37 | 38 | const [selectedDateRange, setSelectedDateRange] = useState({ 39 | from: "", 40 | to: "", 41 | }); 42 | const [selectedDateRangeBs, setSelectedDateRangeBs] = useState({ 43 | from: "", 44 | to: "", 45 | }); 46 | 47 | const [definedRangeSelector, setDefinedRangeSelector] = useState({ 48 | from: "", 49 | to: "", 50 | }); 51 | 52 | // //converter 53 | // window.ad2bs = ad2bs; 54 | // window.bs2ad = bs2ad; 55 | 56 | // // formater and parser 57 | // window.parse = parseDate; 58 | // window.format = format; 59 | 60 | // ////Range VAlidator 61 | // window.isBsDateValid = isBsDateValid; 62 | // window.isAdDateValid = isAdDateValid; 63 | // window.isInValidRange = isInValidRange; 64 | 65 | return ( 66 |
67 |
68 |

AD Calendar

69 |

Selected Date AD: {JSON.stringify(selectedDate)};

70 |
71 | date === "07-03-2021"} 85 | onSelect={(formattedDate, adDate, bsDate, date) => { 86 | setSelectedDate(formattedDate); 87 | }} 88 | /> 89 |
90 |

BS Calendar

91 |

Selected Date BS: {JSON.stringify(selectedDateBS)};

92 |
93 | date === "07-03-2021"} 107 | onSelect={(formattedDate, adDate, bsDate, date) => { 108 | setSelectedDateBS(formattedDate); 109 | }} 110 | /> 111 |
112 |

BS Date Picker

113 |

Selected Date: {JSON.stringify(dateBS)}

114 | New Date Picker 115 |
116 | 117 | 118 |
119 | kjs;lkajs;fkja;jfs 120 | { 127 | console.log("val",val); 128 | setDateBS(val); 129 | }} 130 | > 131 | 132 | 133 |
134 | 135 | 136 |
137 | 138 |

AD Date Picker

139 |

Selected Date: {JSON.stringify(date)}

140 | New Date Picker 141 |
142 |
143 | { 147 | setDate(val); 148 | }} 149 | /> 150 |
151 |
152 |

Defined Range Selector

153 |

154 | Base Date: 2021-09-14 Selected Range: {definedRangeSelector.from} -{" "} 155 | {definedRangeSelector.to} 156 |

157 | 158 |
159 | { 165 | setDefinedRangeSelector({ from: dateFrom, to: dateTo }); 166 | }} 167 | /> 168 |
169 |

Ad Date Range

170 |

171 | Selected Range: {selectedDateRange.from} - {selectedDateRange.to} 172 |

173 |
174 | { 177 | setSelectedDateRange({ from, to }); 178 | }} 179 | /> 180 |
181 | BS DAte RAnge 182 | { 184 | setSelectedDateRangeBs({ from, to }); 185 | }} 186 | calendarType="BS" 187 | /> 188 |

189 | Selected Range: {selectedDateRangeBs.from} - {selectedDateRangeBs.to} 190 |

191 |
RENDERED DATE IN AD/BS
192 | {/*
193 | {Array(20) 194 | .fill("") 195 | .map((it, ind) => { 196 | return ; //TODO 197 | })} 198 |
*/} 199 |
200 |
201 | ); 202 | }; 203 | 204 | export default App; 205 | -------------------------------------------------------------------------------- /example/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import * as serviceWorker from "./serviceWorker"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://bit.ly/CRA-PWA 17 | serviceWorker.unregister(); 18 | -------------------------------------------------------------------------------- /example/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /example/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' }, 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /example/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "module": "esnext", 5 | "lib": ["dom", "esnext"], 6 | "moduleResolution": "node", 7 | "jsx": "react", 8 | "sourceMap": true, 9 | "declaration": true, 10 | "esModuleInterop": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "suppressImplicitAnyIndexErrors": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "allowSyntheticDefaultImports": true, 19 | "target": "es5", 20 | "allowJs": true, 21 | "skipLibCheck": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "resolveJsonModule": true, 25 | "isolatedModules": true, 26 | "noEmit": true 27 | }, 28 | "include": ["src"], 29 | "exclude": ["node_modules", "build"] 30 | } 31 | -------------------------------------------------------------------------------- /media/img/red-bg-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/media/img/red-bg-logo.png -------------------------------------------------------------------------------- /media/img/repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/media/img/repo.png -------------------------------------------------------------------------------- /media/img/repo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /media/img/transparent-bg-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/media/img/transparent-bg-logo.png -------------------------------------------------------------------------------- /media/img/transparent-bg-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/media/img/transparent-bg-repo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@raralabs/react-patro", 3 | "version": "1.0.1", 4 | "author": "Raralabs", 5 | "license": "MIT", 6 | "description": "AD and BS Calendar functions as hooks and component", 7 | "main": "dist/index.js", 8 | "module": "dist/index.esm", 9 | "source": "src/index.tsx", 10 | "typings": "dist/index.d.ts", 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/raralabs/react-patro.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/raralabs/react-patro/issues" 17 | }, 18 | "engines": { 19 | "node": ">=10" 20 | }, 21 | "keywords": [ 22 | "ad2bs", 23 | "bs2ad", 24 | "nepali-calendar", 25 | "datepicker", 26 | "ad", 27 | "bs", 28 | "react-datepicker" 29 | ], 30 | "scripts": { 31 | "build": "rollup -c", 32 | "dev": "rollup -cw", 33 | "prepare": "run-s build", 34 | "lint": "prettier --check .", 35 | "format": "prettier --write ." 36 | }, 37 | "dependencies": { 38 | "@popperjs/core": "^2.9.2", 39 | "date-fns": "^2.22.1" 40 | }, 41 | "devDependencies": { 42 | "@babel/core": "^7.9.0", 43 | "@babel/preset-react": "^7.14.5", 44 | "@babel/preset-typescript": "^7.9.0", 45 | "@rollup/plugin-node-resolve": "^13.0.2", 46 | "@testing-library/jest-dom": "^5.11.4", 47 | "@testing-library/react": "^11.1.0", 48 | "@testing-library/user-event": "^12.1.10", 49 | "@types/jest": "^26.0.15", 50 | "@types/node": "^16.0.0", 51 | "@types/react": "^17.0.2", 52 | "@types/react-dom": "^17.0.0", 53 | "@typescript-eslint/eslint-plugin": "^2.34.0", 54 | "@typescript-eslint/parser": "^2.34.0", 55 | "eslint": "^6.8.0", 56 | "eslint-config-prettier": "^8.3.0", 57 | "eslint-plugin-prettier": "^3.4.0", 58 | "eslint-plugin-react": "^7.19.0", 59 | "husky": "^7.0.1", 60 | "npm-run-all": "^4.1.5", 61 | "prettier": "^2.3.2", 62 | "react": "^17.0.1", 63 | "react-dom": "^17.0.1", 64 | "react-scripts": "^3.4.1", 65 | "rollup": "^2.53.2", 66 | "rollup-plugin-babel": "^4.4.0", 67 | "rollup-plugin-peer-deps-external": "^2.2.4", 68 | "rollup-plugin-terser": "^7.0.2", 69 | "rollup-plugin-typescript2": "^0.30.0", 70 | "tslib": "^1.14.1", 71 | "typescript": "^4.1.2" 72 | }, 73 | "peerDependencies": { 74 | "react": ">=16.8.0", 75 | "react-dom": ">=16.8.0" 76 | }, 77 | "husky": { 78 | "hooks": { 79 | "pre-commit": "npm run format" 80 | } 81 | }, 82 | "eslintConfig": { 83 | "extends": "react-app" 84 | }, 85 | "files": [ 86 | "dist", 87 | "package.json", 88 | "src/styles.css" 89 | ], 90 | "publishConfig": { 91 | "access": "public", 92 | "registry": "https://registry.npmjs.org/" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | //uses peer dependency 2 | import external from "rollup-plugin-peer-deps-external"; 3 | //pairs rollup with typescript 4 | import typescript from "rollup-plugin-typescript2"; 5 | // resolves third party modules and add in the source 6 | import resolve from "@rollup/plugin-node-resolve"; 7 | //uglifying and minizing the budle 8 | // import { terser } from "rollup-plugin-terser"; 9 | // babel 10 | import babel from "rollup-plugin-babel"; 11 | 12 | export default [ 13 | { 14 | input: "./src/index.ts", 15 | output: [ 16 | { 17 | file: "dist/index.esm.js", 18 | format: "esm", 19 | }, 20 | { 21 | file: "dist/index.js", 22 | format: "cjs", 23 | }, 24 | ], 25 | plugins: [ 26 | babel({ 27 | exclude: "node_modules/**", 28 | presets: ["@babel/preset-react"], 29 | }), 30 | typescript(), 31 | external(), 32 | resolve(), 33 | // terser(), 34 | ], 35 | }, 36 | ]; 37 | -------------------------------------------------------------------------------- /src/AdBsDateRenderer.tsx: -------------------------------------------------------------------------------- 1 | import { ad2bs } from "./CalendarData"; 2 | import { isDateValidWithFormat } from "./CalendarData/validator"; 3 | import { parseDate } from "./date-fns"; 4 | import { useCalendarType } from "./hooks"; 5 | 6 | type AdBsDateRendererProps = { 7 | adDate: string; 8 | adDateFormat: string; 9 | }; 10 | 11 | const padDateMonth = (val: string | number) => { 12 | return `${val}`.padStart(2, "0"); 13 | }; 14 | const AdBsDateRenderer = ({ 15 | adDate, 16 | adDateFormat = "DD-MM-YYYY", 17 | }: AdBsDateRendererProps) => { 18 | const adDateObj = parseDate(adDate, adDateFormat); 19 | 20 | const bsDateObj = ad2bs( 21 | adDateObj.getFullYear(), 22 | adDateObj.getMonth() + 1, 23 | adDateObj.getDate() 24 | ); 25 | const calendarType = useCalendarType("BS"); 26 | if (adDate == null || adDate === "") { 27 | return ""; 28 | } 29 | if (isDateValidWithFormat(adDate, adDateFormat)) { 30 | return calendarType === "AD" 31 | ? adDate 32 | : `${padDateMonth(bsDateObj.date)}-${padDateMonth(bsDateObj.month)}-${ 33 | bsDateObj.year 34 | }`; 35 | } else { 36 | return "Invalid Date"; 37 | } 38 | }; 39 | 40 | export default AdBsDateRenderer; 41 | -------------------------------------------------------------------------------- /src/Calendar/DateRender.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { DateRange, DisableProps, IDateObject } from "types/main"; 3 | import useCalendarData from "./useCalendarData"; 4 | import { checkIsInRange, checkIsSelected, checkIsDisabled } from "./util"; 5 | 6 | interface DateRendererProps extends DisableProps { 7 | isAD: boolean; 8 | selectedData: IDateObject; 9 | shouldPressOK: boolean; 10 | onChangeDate: (adDate: IDateObject, bsDate: IDateObject) => void; 11 | showExtra: boolean; 12 | changeMonth: (n: number) => void; 13 | range?: DateRange | null; 14 | dateFormat: string; 15 | year: number; 16 | month: number; 17 | } 18 | 19 | const DateRenderer = (props: DateRendererProps) => { 20 | const { 21 | isAD, 22 | selectedData, 23 | shouldPressOK, 24 | onChangeDate, 25 | showExtra, 26 | year, 27 | month, 28 | changeMonth, 29 | range, 30 | disableDate, 31 | disableFuture, 32 | disablePast, 33 | maxDate, 34 | minDate, 35 | dateFormat, 36 | } = props; 37 | const weekData = useCalendarData(year, month, isAD); 38 | return ( 39 | <> 40 | {weekData.map(({ data, key }, week) => { 41 | return ( 42 | 43 | {data.map((d, i) => { 44 | const isDisabled = checkIsDisabled( 45 | d.adDate, 46 | { 47 | disableDate, 48 | disableFuture, 49 | disablePast, 50 | maxDate, 51 | minDate, 52 | }, 53 | dateFormat, 54 | d.bsDate 55 | ); 56 | 57 | const isSelected = 58 | d.isCurrentMonth && checkIsSelected(selectedData, d.adDate); 59 | const isInRange = range ? checkIsInRange(d.adDate, range) : false; 60 | 61 | return ( 62 | { 66 | if (isDisabled) { 67 | return; 68 | } 69 | if (!d.isCurrentMonth) { 70 | if (week === 0) { 71 | changeMonth(-1); 72 | } else { 73 | changeMonth(1); 74 | } 75 | } 76 | if (shouldPressOK) { 77 | onChangeDate(d.adDate, d.bsDate); 78 | } 79 | }} 80 | className={`rl-picker-cell ${d.isToday ? "today" : ""} ${ 81 | isSelected ? "active" : "" 82 | } ${!d.isCurrentMonth ? "other-month" : ""} ${ 83 | isDisabled ? "disabled" : "" 84 | } ${isInRange ? "in-range" : ""}`} 85 | > 86 |
87 |
{d.render.main}
88 | {showExtra &&
{d.render.sub}
} 89 |
90 | 91 | ); 92 | })} 93 | 94 | ); 95 | })} 96 | 97 | ); 98 | }; 99 | 100 | export default DateRenderer; 101 | -------------------------------------------------------------------------------- /src/Calendar/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ShowDropdownType, ShowYearDropdownType } from "../types/main"; 3 | import { 4 | getAdRangeForBsCalendar, 5 | getBsRangeForAdCalendar, 6 | getMonthNames, 7 | getNepaliNumber, 8 | getValidYears, 9 | } from "../CalendarData"; 10 | 11 | type OffsetChange = (a: number) => void; 12 | 13 | type HeaderProps = { 14 | year: number; 15 | month: number; 16 | changeYear: OffsetChange; //TODO 17 | changeMonth: OffsetChange; 18 | isAD: boolean; 19 | showMonthDropdown: ShowDropdownType; 20 | showYearDropdown: ShowYearDropdownType; 21 | showExtra: boolean; 22 | }; 23 | 24 | const Header = ({ 25 | year, 26 | month, 27 | changeYear, 28 | changeMonth, 29 | isAD, 30 | showMonthDropdown, 31 | showYearDropdown, 32 | showExtra, 33 | }: HeaderProps) => { 34 | const maxAD = 2035; 35 | const maxBS = 2092; 36 | // because month from props is received in readable format 1= Baishakh 37 | // but the component manipulates in array format 0= Baisakh 38 | const monthIndex = month - 1; 39 | 40 | const allNepaliMonth = getMonthNames("np", "full"); 41 | const allEnglishMonth = getMonthNames("en", "full"); 42 | 43 | const allMonth = isAD ? allEnglishMonth : allNepaliMonth; 44 | 45 | const currentMonthName = allMonth ? allMonth[monthIndex] : ""; 46 | 47 | const currentYear = isAD ? year : getNepaliNumber(year ?? 0); 48 | 49 | const allYears = isAD ? getValidYears("en", "AD") : getValidYears("en", "BS"); 50 | 51 | const alternateCalendarTypeRange = isAD 52 | ? getBsRangeForAdCalendar(year, month) 53 | : getAdRangeForBsCalendar(year, month); 54 | 55 | const { from, to } = alternateCalendarTypeRange; 56 | 57 | const reachedMaxYear = isAD ? year >= maxAD : year >= maxBS; 58 | 59 | return ( 60 |
61 |
62 |
63 | 79 | 95 |
96 | 97 |
98 |
99 | {!showMonthDropdown ? ( 100 | {currentMonthName}   101 | ) : ( 102 | 118 | )} 119 | 120 |
121 |
122 | {!showYearDropdown ? ( 123 | {currentYear || 0}   124 | ) : ( 125 | 141 | )} 142 |
143 |
144 | {showExtra && ( 145 |
146 |
147 | {from.monthName}/{to.monthName} - 148 |
149 |
155 | {from.year} 156 | {from.year !== to.year ? `/${String(to.year).slice(-2)}` : ""} 157 |
158 |
159 | )} 160 |
161 | 162 |
163 | 180 | 197 |
198 |
199 |
200 | ); 201 | }; 202 | 203 | export default Header; 204 | -------------------------------------------------------------------------------- /src/Calendar/RangeRender.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { DateRange, DisableProps, IDateObject } from "../types/main"; 3 | import useCalendarData from "./useCalendarData"; 4 | import { 5 | checkIsInRange, 6 | checkIsSelected, 7 | checkIsDisabled, 8 | checkIsRangeBoundary, 9 | } from "./util"; 10 | 11 | interface DateRendererProps extends DisableProps { 12 | isAD: boolean; 13 | selectedData: IDateObject; 14 | shouldPressOK: boolean; 15 | onChangeDate: (adDate: IDateObject, bsDate: IDateObject) => void; 16 | showExtra: boolean; 17 | changeMonth: (n: number) => void; 18 | range?: DateRange | null; 19 | dateFormat: string; 20 | year: number; 21 | month: number; 22 | } 23 | 24 | const DateRenderer = (props: DateRendererProps) => { 25 | const { 26 | isAD, 27 | selectedData, 28 | shouldPressOK, 29 | onChangeDate, 30 | showExtra, 31 | year, 32 | month, 33 | changeMonth, 34 | range, 35 | disableDate, 36 | disableFuture, 37 | disablePast, 38 | maxDate, 39 | minDate, 40 | dateFormat, 41 | } = props; 42 | const weekData = useCalendarData(year, month, isAD); 43 | 44 | return ( 45 | <> 46 | {weekData.map(({ data, key }, week) => { 47 | return ( 48 | 49 | {data.map((d, i) => { 50 | const isDisabled = checkIsDisabled( 51 | d.adDate, 52 | { 53 | disableDate, 54 | disableFuture, 55 | disablePast, 56 | maxDate, 57 | minDate, 58 | }, 59 | dateFormat, 60 | d.bsDate, 61 | isAD 62 | ); 63 | 64 | const isSelected = 65 | d.isCurrentMonth && checkIsSelected(selectedData, d.adDate); 66 | const isInRange = range 67 | ? checkIsInRange(isAD ? d.adDate : d.bsDate, range) 68 | : false; 69 | 70 | const isRangeBoundary = range 71 | ? checkIsRangeBoundary(isAD ? d.adDate : d.bsDate, range) 72 | : false; 73 | 74 | return ( 75 | { 79 | if (isDisabled) { 80 | return; 81 | } 82 | if (!d.isCurrentMonth) { 83 | if (week === 0) { 84 | changeMonth(-1); 85 | } else { 86 | changeMonth(1); 87 | } 88 | } 89 | if (shouldPressOK) { 90 | onChangeDate(d.adDate, d.bsDate); 91 | } 92 | }} 93 | className={`rl-picker-cell ${d.isToday ? "today" : ""} ${ 94 | isRangeBoundary || isSelected ? "active" : "" 95 | } ${!d.isCurrentMonth ? "other-month" : ""} ${ 96 | isDisabled ? "disabled" : "" 97 | } ${isInRange ? "in-range" : ""}`} 98 | > 99 |
100 |
{d.render.main}
101 | {showExtra &&
{d.render.sub}
} 102 |
103 | 104 | ); 105 | })} 106 | 107 | ); 108 | })} 109 | 110 | ); 111 | }; 112 | 113 | export default DateRenderer; 114 | -------------------------------------------------------------------------------- /src/Calendar/RenderReference.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { DateType } from "../types/main"; 3 | 4 | //TODO 5 | interface RenderReferenceProps { 6 | referenceDate: string; // TODO, 7 | rangeReference: number[]; 8 | onChangeDate: (date: DateType) => void; 9 | zeroDayName: string; 10 | } 11 | const RenderReference: React.FC = ({ 12 | referenceDate, 13 | rangeReference, 14 | onChangeDate, 15 | zeroDayName, 16 | }) => { 17 | const _referenceRenderer = (referenceDate: string, ranges: number[] = []) => { 18 | const options = ranges.map((day_diff = 0) => { 19 | return ( 20 |
{ 23 | onChangeDate({ 24 | year: 2054, 25 | month: 2, 26 | day: 1, 27 | }); 28 | }} 29 | > 30 | {day_diff === 0 ? zeroDayName || "Today" : `Within ${day_diff} Days`} 31 |
32 | ); 33 | }); 34 | return options; 35 | }; 36 | return ( 37 |
38 | {_referenceRenderer(referenceDate, rangeReference)} 39 |
40 | ); 41 | }; 42 | export default RenderReference; 43 | -------------------------------------------------------------------------------- /src/Calendar/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | 3 | import Header from "./Header"; 4 | import RangeRender from "./RangeRender"; 5 | import useSelectedData from "./useSelectedData"; 6 | import { getMonthOffset, checkDatePropsValidity } from "./util"; 7 | import { getDateFromObject } from "../date-fns"; 8 | import { getWeekNames, formatBsDate } from "../CalendarData"; 9 | 10 | import { DateRange, INepaliCalendar, IDateObject } from "../types/main"; 11 | 12 | const NepaliCalendar = (props: INepaliCalendar) => { 13 | const { 14 | defaultValue, 15 | dateFormat, 16 | value, 17 | onSelect, 18 | shouldPressOK = true, 19 | showExtra = true, 20 | showMonthDropdown = false, 21 | showYearDropdown = false, 22 | calendarType, 23 | disableDate, 24 | disablePast, 25 | disableFuture, 26 | maxDate, 27 | minDate, 28 | range, 29 | children, 30 | } = props; 31 | 32 | useEffect(() => { 33 | if (value !== undefined && defaultValue !== undefined) { 34 | console.warn("If value is provided defaultValue is ignored."); 35 | } 36 | }, [value, defaultValue]); 37 | 38 | useEffect(() => { 39 | if (dateFormat) 40 | checkDatePropsValidity( 41 | { maxDate, defaultValue, minDate, value }, 42 | dateFormat, 43 | calendarType 44 | ); 45 | }, [dateFormat, defaultValue, maxDate, minDate, value, calendarType]); 46 | 47 | const isAD = calendarType === "AD"; 48 | 49 | // //always in ad 50 | const [selectedData, setSelectedData] = useSelectedData( 51 | dateFormat, 52 | isAD, 53 | value, 54 | defaultValue 55 | ); 56 | 57 | const [calendarData, setCalendarData] = useState({ 58 | date: selectedData.date, 59 | month: selectedData.month, 60 | year: selectedData.year, 61 | }); 62 | 63 | const changeMonth = (offset: number) => { 64 | const data = getMonthOffset(calendarData, offset, calendarType); 65 | if (data) { 66 | const { year, month, date } = data; 67 | 68 | setCalendarData({ year, month, date: date }); 69 | } 70 | }; 71 | 72 | const onSelectDate = (adDate: IDateObject, bsDate: IDateObject) => { 73 | const date = getDateFromObject(adDate); 74 | const formattedDate = formatBsDate(isAD ? adDate : bsDate, dateFormat); 75 | 76 | if (!value) setSelectedData({ ...adDate }); 77 | typeof onSelect === "function" && 78 | onSelect(formattedDate, adDate, bsDate, date); 79 | }; 80 | 81 | //TODO range check 82 | const changeYear = (offset: number) => { 83 | const month = calendarData.month; 84 | 85 | const offsetValue = Number(offset); 86 | if (isNaN(offsetValue)) { 87 | throw new TypeError( 88 | `Expected type of offset for change year function is number instead received ${typeof offset}` 89 | ); 90 | } 91 | const year = calendarData.year + offsetValue; 92 | 93 | const date = calendarData.date; 94 | setCalendarData({ year, month, date }); 95 | }; 96 | 97 | const allDays = 98 | calendarType === "BS" 99 | ? getWeekNames("np", "short") 100 | : getWeekNames("en", "short"); 101 | 102 | const dateRange: DateRange | null = range 103 | ? { from: range?.from, to: range?.to, format: range?.format ?? dateFormat } 104 | : null; 105 | 106 | return ( 107 |
108 |
109 |
119 |
120 | 121 | 122 | 123 | {allDays && 124 | allDays.map((val, ind) => { 125 | return ; 126 | })} 127 | 128 | 129 | 130 | 147 | 148 |
{val}
149 | {children} 150 |
151 |
152 |
153 | ); 154 | }; 155 | 156 | export default NepaliCalendar; 157 | -------------------------------------------------------------------------------- /src/Calendar/useCalendarData.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getBsInfoOfoffsetDate, 3 | getStartingDayOfBsMonth, 4 | getTotalDaysInBsMonth, 5 | } from "./../CalendarData/index"; 6 | // import { useEffect } from "react"; 7 | import { getNepaliNumber } from "../CalendarData"; 8 | import { getInfoOfOffsetDate } from "../date-fns"; 9 | import { checkIsToday, getSubDate } from "./util"; 10 | 11 | const useCalendarData = (year: number, month: number, isAD: boolean) => { 12 | const startingDayOfMonth = getStartingDayOfMonth(year, month, isAD) || 7; 13 | const totalDayInThisMonth = getTotalDaysOfMonth(year, month, isAD); 14 | 15 | const getOffsetDateInfo = ( 16 | year: number, 17 | month: number, 18 | offset?: { year?: number; month?: number } 19 | ) => 20 | isAD 21 | ? getInfoOfOffsetDate(year, month, offset) 22 | : getBsInfoOfoffsetDate(year, month, offset); 23 | 24 | const { 25 | year: prevYear, 26 | month: prevMonth, 27 | totalDays: totalDaysInPrevMonth, 28 | } = getOffsetDateInfo(year, month, { month: -1 }); 29 | 30 | const { year: nextYear, month: nextMonth } = getOffsetDateInfo(year, month, { 31 | month: 1, 32 | }); 33 | 34 | const data = (year: number, month: number, isAD: boolean, week: number) => { 35 | return Array(7) 36 | .fill("") 37 | .map((ie2: any, day: number) => { 38 | let cellDate = week * 7 + day - startingDayOfMonth + 1; 39 | 40 | let isCurrentMonth = true; 41 | let mainDate = { 42 | date: cellDate, 43 | month: month, 44 | year: year, 45 | }; 46 | 47 | if (cellDate <= 0) { 48 | cellDate = totalDaysInPrevMonth + cellDate; 49 | 50 | isCurrentMonth = false; 51 | mainDate = { 52 | date: cellDate, 53 | month: prevMonth, 54 | year: prevYear, 55 | }; 56 | } else if (cellDate > totalDayInThisMonth) { 57 | cellDate = cellDate - totalDayInThisMonth; 58 | 59 | isCurrentMonth = false; 60 | mainDate = { 61 | date: cellDate, 62 | month: nextMonth, 63 | year: nextYear, 64 | }; 65 | } 66 | 67 | const subMainDate = getSubDate(mainDate, isAD); 68 | 69 | const adDate = isAD ? mainDate : subMainDate; 70 | const bsDate = isAD ? subMainDate : mainDate; 71 | 72 | const isToday = checkIsToday(adDate); 73 | 74 | const renderMainDate = isAD 75 | ? cellDate || 0 76 | : getNepaliNumber(cellDate || 0); 77 | const renderSubDate = isAD 78 | ? getNepaliNumber(subMainDate.date) 79 | : subMainDate.date; 80 | 81 | return { 82 | main: { 83 | date: mainDate.date, 84 | month: mainDate.month, 85 | year: mainDate.year, 86 | }, 87 | isToday, 88 | isCurrentMonth, 89 | render: { main: renderMainDate, sub: renderSubDate }, 90 | adDate, 91 | bsDate, 92 | }; 93 | }); 94 | }; 95 | 96 | const weekData = (year: number, month: number, isAD: boolean) => 97 | Array(6) 98 | .fill("") 99 | .map((it1, week) => { 100 | return { key: `WEEK-${week}`, data: data(year, month, isAD, week) }; 101 | }); 102 | 103 | return weekData(year, month, isAD); 104 | }; 105 | 106 | function getTotalDaysOfMonth( 107 | year: number, 108 | month: number, 109 | isAD: boolean 110 | ): number { 111 | if (isAD) return new Date(year, month, 0).getDate(); 112 | else { 113 | return getTotalDaysInBsMonth(year, month); 114 | } 115 | } 116 | 117 | //TODO check 118 | function getStartingDayOfMonth( 119 | year: number, 120 | month: number, 121 | isAD: boolean 122 | ): number { 123 | if (isAD) { 124 | const monthIndex = month - 1; 125 | return new Date(year, monthIndex, 1).getDay(); 126 | } else { 127 | return getStartingDayOfBsMonth(year, month); 128 | } 129 | } 130 | 131 | export default useCalendarData; 132 | -------------------------------------------------------------------------------- /src/Calendar/useSelectedData.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { IDateObject } from "../types/main"; 3 | 4 | import { getDateObj } from "../date-fns"; 5 | import { parseBsDate, bs2ad } from "../CalendarData"; 6 | import { isDateValidWithFormat } from "../CalendarData/validator"; 7 | import { getTodaysDate } from "./util"; 8 | 9 | //Regardless of isAD value the data should always contain Ad data by converting bs Data to ad if isAD = false 10 | const useSelectedData = ( 11 | dateFormat: string, 12 | isAD: boolean, 13 | value: string | null | undefined, 14 | defaultDate: string | null | undefined 15 | ) => { 16 | const initialDate = value ?? defaultDate; 17 | const { ad: todaysDateInAd, bs: todaysDateInBs } = getTodaysDate(); 18 | 19 | let currentDate = isAD ? todaysDateInAd : todaysDateInBs; 20 | 21 | let selectedData: IDateObject = { 22 | date: currentDate.date, 23 | month: currentDate.month, 24 | year: currentDate.year, 25 | }; 26 | if (initialDate && isDateValidWithFormat(initialDate, dateFormat)) { 27 | selectedData = parseBsDate(initialDate, dateFormat); 28 | } 29 | const [data, setData] = useState(selectedData); 30 | 31 | useEffect(() => { 32 | if (value) { 33 | if (isAD) { 34 | const adDateObj = getDateObj(value, dateFormat); 35 | if (adDateObj) setData(adDateObj); 36 | } else { 37 | const bsDateObj = parseBsDate(value, dateFormat); 38 | const adDateObj = bs2ad( 39 | bsDateObj.year, 40 | bsDateObj.month, 41 | bsDateObj.date 42 | ); 43 | 44 | if (adDateObj) setData(adDateObj); 45 | } 46 | } 47 | }, [value, dateFormat, isAD]); 48 | 49 | return [data, setData] as const; 50 | }; 51 | 52 | export default useSelectedData; 53 | -------------------------------------------------------------------------------- /src/Calendar/util.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ad2bs, 3 | bs2ad, 4 | formatBsDate, 5 | isInValidRange, 6 | parseBsDate, 7 | } from "./../CalendarData/index"; 8 | import { isInBetween } from "../utils"; 9 | import { 10 | CalendarType, 11 | DateRange, 12 | IDateObject, 13 | DisableProps, 14 | } from "../types/main"; 15 | import { parseDate, getDateFromObject, dateFormatter } from "../date-fns"; 16 | import { isDateValidWithFormat } from "../CalendarData/validator"; 17 | import { isAfter, isBefore } from "../CalendarData/calculation"; 18 | 19 | type ADBSDateType = { 20 | ad: IDateObject; 21 | bs: IDateObject; 22 | }; 23 | 24 | const getTodaysDate = (): ADBSDateType => { 25 | const todayDate = new Date(); 26 | 27 | const adYear = todayDate.getFullYear(); 28 | const adMonth = todayDate.getMonth() + 1; 29 | const adDate = todayDate.getDate(); 30 | 31 | const { 32 | date: bsDate, 33 | month: bsMonth, 34 | year: bsYear, 35 | } = ad2bs(adYear, adMonth, adDate); 36 | 37 | const ad = { 38 | date: adDate, 39 | month: adMonth, 40 | year: adYear, 41 | }; 42 | 43 | const bs = { 44 | date: bsDate, 45 | month: bsMonth, 46 | year: bsYear, 47 | }; 48 | 49 | return { bs, ad }; 50 | }; 51 | 52 | function checkIsSelected( 53 | selectedData: IDateObject, 54 | adDate: IDateObject 55 | ): boolean { 56 | if ( 57 | selectedData.date && 58 | adDate.date === selectedData.date && 59 | adDate.year === selectedData.year && 60 | adDate.month === selectedData.month 61 | ) { 62 | return true; 63 | } else return false; 64 | } 65 | 66 | function checkIsToday(adDate: IDateObject): boolean { 67 | const { ad: todayDateAD } = getTodaysDate(); 68 | if ( 69 | todayDateAD.date === adDate.date && 70 | todayDateAD.month === adDate.month && 71 | todayDateAD.year === adDate.year 72 | ) { 73 | return true; 74 | } 75 | return false; 76 | } 77 | 78 | export function checkIsDisabled( 79 | adDateObj: IDateObject, 80 | disableConfig: DisableProps, 81 | dateFormat: string, 82 | bsDateObj: IDateObject, 83 | isAD = true 84 | ): boolean { 85 | const { disableDate, maxDate, minDate, disablePast, disableFuture } = 86 | disableConfig; 87 | 88 | const adDate = getDateFromObject({ 89 | year: adDateObj.year, 90 | month: adDateObj.month, 91 | date: adDateObj.date, 92 | }); 93 | 94 | const providedDateString = formatBsDate( 95 | isAD ? adDateObj : bsDateObj, 96 | dateFormat 97 | ); 98 | const formattedAdDate = dateFormatter(adDate, dateFormat); 99 | 100 | const today = new Date(); 101 | if ( 102 | typeof disableDate === "function" && 103 | disableDate(formattedAdDate, adDateObj, bsDateObj, adDate) 104 | ) 105 | return true; 106 | if (adDate > today && disableFuture) { 107 | return true; 108 | } 109 | if (adDate < today && disablePast) { 110 | return true; 111 | } 112 | 113 | if (maxDate && isAfter(providedDateString, maxDate, dateFormat)) { 114 | return true; 115 | } 116 | 117 | if (minDate && isBefore(providedDateString, minDate, dateFormat)) { 118 | return true; 119 | } 120 | 121 | return false; 122 | } 123 | 124 | export function checkIsInRange( 125 | adDateObj: IDateObject, 126 | range: DateRange 127 | ): boolean { 128 | const adDate = getDateFromObject(adDateObj); 129 | const from = range?.from; 130 | const to = range?.to; 131 | const dateFormat = range?.format; 132 | 133 | if (from && to && dateFormat) { 134 | const fromDate = parseDate(from, dateFormat); 135 | const toDate = parseDate(to, dateFormat); 136 | if (fromDate > toDate) { 137 | throw new RangeError( 138 | `from date is greater than to Date. Received from =${JSON.stringify( 139 | range.from 140 | )} && to = ${JSON.stringify(range.to)}` 141 | ); 142 | } 143 | if (isInBetween(adDate, fromDate, toDate)) { 144 | return true; 145 | } 146 | } 147 | return false; 148 | } 149 | const isYearMonthDateSame = (dateLeft: Date, dateRight: Date): boolean => { 150 | if ( 151 | dateLeft.getFullYear() === dateRight.getFullYear() && 152 | dateLeft.getMonth() === dateRight.getMonth() && 153 | dateLeft.getDate() === dateRight.getDate() 154 | ) 155 | return true; 156 | return false; 157 | }; 158 | 159 | export function checkIsRangeBoundary( 160 | adDateObj: IDateObject, 161 | range?: DateRange 162 | ): boolean { 163 | const adDate = getDateFromObject(adDateObj); 164 | const from = range?.from; 165 | const to = range?.to; 166 | const dateFormat = range?.format; 167 | 168 | if (dateFormat) { 169 | const fromDate = from ? parseDate(from, dateFormat) : null; 170 | const toDate = to ? parseDate(to, dateFormat) : null; 171 | 172 | if (fromDate && toDate && fromDate > toDate) { 173 | throw new RangeError( 174 | `from date is greater than to Date. Received from =${JSON.stringify( 175 | range?.from 176 | )} && to = ${JSON.stringify(range?.to)}` 177 | ); 178 | } 179 | 180 | if ( 181 | (fromDate && isYearMonthDateSame(adDate, fromDate)) || 182 | (toDate && isYearMonthDateSame(adDate, toDate)) 183 | ) { 184 | return true; 185 | } 186 | } 187 | return false; 188 | } 189 | 190 | //TODO check change its name 191 | const getMonthOffset = ( 192 | dateObj: { year: number; month: number; date: number }, 193 | offset: number, 194 | calendarType: CalendarType 195 | ): IDateObject => { 196 | const { year, month, date } = dateObj; 197 | const offsetValue = Number(offset); 198 | 199 | if (isNaN(offsetValue)) { 200 | throw new TypeError( 201 | `Expected type of offset for change year function is number instead received ${typeof offset}` 202 | ); 203 | } 204 | 205 | const monthOffset = month + offset; 206 | const yearOffset = monthOffset ? Math.floor(monthOffset / 13) : -1; 207 | 208 | const nextYear = year + yearOffset; 209 | const nextMonth = monthOffset % 12 || 12; 210 | const nextDate = date; 211 | 212 | //TODO only specified for BS. so causes issue for AD, 213 | isInValidRange( 214 | { year: nextYear, month: nextMonth, date: nextDate }, 215 | calendarType, 216 | true 217 | ); 218 | 219 | return { year: nextYear, month: nextMonth, date: nextDate }; 220 | }; 221 | 222 | export const getSubDate = ( 223 | mainDate: IDateObject, 224 | isAD: boolean 225 | ): IDateObject => { 226 | if (isAD) { 227 | const { 228 | date: bsDate, 229 | month: bsMonth, 230 | year: bsYear, 231 | } = ad2bs(mainDate.year, mainDate.month, mainDate.date); 232 | return { 233 | year: bsYear, 234 | month: bsMonth, 235 | date: bsDate, 236 | }; 237 | } else { 238 | const { 239 | date: adDate, 240 | month: adMonth, 241 | year: adYear, 242 | } = bs2ad(mainDate.year, mainDate.month, mainDate.date); 243 | 244 | return { 245 | year: adYear, 246 | month: adMonth, 247 | date: adDate, 248 | }; 249 | } 250 | }; 251 | 252 | type AllDateProps = { 253 | maxDate?: string | null; 254 | minDate?: string | null; 255 | defaultValue?: string | null; 256 | value?: string | null; 257 | }; 258 | 259 | export const checkDatePropsValidity = ( 260 | allDateProps: AllDateProps, 261 | dateFormat: string, 262 | calendarType: CalendarType 263 | ) => { 264 | type AllDateString = keyof typeof allDateProps; 265 | const checker: AllDateString[] = [ 266 | "maxDate", 267 | "minDate", 268 | "defaultValue", 269 | "value", 270 | ]; 271 | 272 | function throwTypeError(date: string, propName: string, dateFormat: string) { 273 | throw new TypeError( 274 | `Invalid Date Format. Expected Date Format ${dateFormat} instead got ${date} for prop ${propName} passed in Calendar. ` 275 | ); 276 | } 277 | checker.forEach((prop) => { 278 | const value = allDateProps[prop]; 279 | 280 | if (value) { 281 | if (!isDateValidWithFormat(value, dateFormat)) { 282 | throwTypeError(value, prop, dateFormat); 283 | return; 284 | } else { 285 | const dateObj = parseBsDate(value, dateFormat); 286 | isInValidRange(dateObj, calendarType, true); 287 | } 288 | } 289 | }); 290 | }; 291 | 292 | export { getTodaysDate, checkIsToday, checkIsSelected, getMonthOffset }; 293 | -------------------------------------------------------------------------------- /src/CalendarData/calculation.ts: -------------------------------------------------------------------------------- 1 | import { ad2bs, bs2ad } from "./index"; 2 | import { IDateObject } from "../types/main"; 3 | import parse from "./parser"; 4 | 5 | type DateOffset = { 6 | year?: number; 7 | month?: number; 8 | date?: number; 9 | }; 10 | 11 | export function getNewBsDate(offset: DateOffset, dateObj: IDateObject) { 12 | const { year, month, date } = dateObj; 13 | 14 | const ad = bs2ad(year, month, date); 15 | 16 | const monthIndex = ad.month - 1; 17 | const adDate = new Date( 18 | ad.year + (offset?.year ?? 0), 19 | monthIndex + (offset?.month ?? 0), 20 | ad.date + (offset?.date ?? 0) 21 | ); 22 | 23 | const bs = ad2bs( 24 | adDate.getFullYear(), 25 | adDate.getMonth() + 1, 26 | adDate.getDate() 27 | ); 28 | return bs; 29 | } 30 | 31 | export function getNewAdDate(offset: DateOffset, dateObj: IDateObject) { 32 | const { year, month, date } = dateObj; 33 | 34 | const monthIndex = month - 1; 35 | const adDate = new Date( 36 | year + (offset?.year ?? 0), 37 | monthIndex + (offset?.month ?? 0), 38 | date + (offset?.date ?? 0) 39 | ); 40 | 41 | const ad = { 42 | year: adDate.getFullYear(), 43 | month: adDate.getMonth() + 1, 44 | date: adDate.getDate(), 45 | }; 46 | 47 | return ad; 48 | } 49 | 50 | type CompareBoolean = 1 | -1 | 0; 51 | //1 => if left is greater 52 | //-1 => if right is greter 53 | //0 => if same 54 | export function compareDates( 55 | date1: string, 56 | date2: string, 57 | dateFormat: string 58 | ): CompareBoolean { 59 | const date1Obj = parse(date1, dateFormat); 60 | const date2Obj = parse(date2, dateFormat); 61 | 62 | if (date1Obj.year > date2Obj.year) return 1; 63 | if (date2Obj.year > date1Obj.year) return -1; 64 | 65 | if (date1Obj.month > date2Obj.month) return 1; 66 | if (date2Obj.month > date1Obj.month) return -1; 67 | 68 | if (date1Obj.date > date2Obj.date) return 1; 69 | if (date2Obj.date > date1Obj.date) return -1; 70 | 71 | return 0; 72 | } 73 | 74 | export function areDateEqual( 75 | date1: string, 76 | date2: string, 77 | dateFormat: string 78 | ): boolean { 79 | const compareBoolean = compareDates(date1, date2, dateFormat); 80 | return compareBoolean === 0; 81 | } 82 | export function isAfter( 83 | date1: string, 84 | date2: string, 85 | dateFormat: string 86 | ): boolean { 87 | const compareBoolean = compareDates(date1, date2, dateFormat); 88 | return compareBoolean === 1; 89 | } 90 | export function isBefore( 91 | date1: string, 92 | date2: string, 93 | dateFormat: string 94 | ): boolean { 95 | const compareBoolean = compareDates(date1, date2, dateFormat); 96 | return compareBoolean === -1; 97 | } 98 | -------------------------------------------------------------------------------- /src/CalendarData/format.ts: -------------------------------------------------------------------------------- 1 | import { IDateObject } from "../types/main"; 2 | type DateFormat = { 3 | yyyy: string; 4 | mm: string; 5 | m: string; 6 | dd: string; 7 | d: string; 8 | }; 9 | 10 | function format(dateObj: IDateObject, format: string): string { 11 | format = String(format)?.toLocaleLowerCase() || "yyyy-mm-dd"; // default format 12 | 13 | const str: DateFormat = { 14 | yyyy: String(dateObj.year), 15 | mm: String(dateObj.month).padStart(2, "0"), 16 | m: String(dateObj.month), 17 | dd: String(dateObj.date).padStart(2, "0"), 18 | d: String(dateObj.date), 19 | }; 20 | 21 | const formatted = format.replace(/(yyyy|dd|d|mm|m)/g, function (part) { 22 | return str[part as keyof DateFormat]; 23 | }); 24 | 25 | return formatted; 26 | } 27 | export default format; 28 | -------------------------------------------------------------------------------- /src/CalendarData/getBsData.ts: -------------------------------------------------------------------------------- 1 | import { calendar_data, minBsYear, maxBsYear } from "./data"; 2 | //TODO 3 | 4 | type EachBSYear = keyof typeof calendar_data; 5 | 6 | export const getTotalDaysInBsMonth = (year: number, month: number): number => { 7 | try { 8 | const allYears = Object.keys(calendar_data); 9 | 10 | const monthIndex = +month - 1; 11 | if (year && allYears.includes(year + "") && monthIndex <= 12) { 12 | return calendar_data[year as EachBSYear][monthIndex]; 13 | } else 14 | throw new RangeError( 15 | `Expeceted first paramater as year within range ${minBsYear}-${maxBsYear} and second parameter as month within range 0-12. But got year as ${year} and month as ${month} in getNumberOfDaysInMonth function` 16 | ); 17 | } catch (err) { 18 | throw new Error("Error"); 19 | // console.(err); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/CalendarData/index.ts: -------------------------------------------------------------------------------- 1 | import { getTotalDaysInAdMonth } from "../date-fns"; 2 | import { isInBetween } from "../utils"; 3 | 4 | import { CalendarType } from "../types/main"; 5 | import parser from "./parser"; 6 | import format from "./format"; 7 | import { isInValidRange, isMonthValid, isBsDateValid } from "./validator"; 8 | import { getTotalDaysInBsMonth } from "./getBsData"; 9 | import * as BSdata from "./data"; 10 | 11 | const parseBsDate = parser; 12 | const formatBsDate = format; 13 | 14 | export { isInValidRange, isMonthValid, isBsDateValid }; 15 | 16 | export { formatBsDate, parseBsDate }; 17 | export { getTotalDaysInBsMonth }; 18 | 19 | const dataType = ["np", "rm", "en"]; 20 | const lengthType = ["full", "short", "min"]; 21 | 22 | type NameType = "monthName" | "dayName"; 23 | type Language = "np" | "rm" | "en"; 24 | type Length = "full" | "short" | "min"; 25 | 26 | type NameReturns = { 27 | full: string[]; 28 | short: string[]; 29 | min: string[]; 30 | }; 31 | export function getNames( 32 | type: NameType, 33 | lang: Language, 34 | length?: T 35 | ): T extends Length ? string[] : string[] | NameReturns { 36 | const nameType = ["monthName", "dayName"]; 37 | if (nameType.includes(type)) { 38 | if (dataType.includes(lang)) { 39 | if (length) { 40 | if (lengthType.includes(length)) { 41 | return BSdata[lang][type][length]; 42 | } 43 | console.error( 44 | `Second parameter in getMonthNames should specify length of month which should be one of ${lengthType}` 45 | ); 46 | } 47 | } 48 | console.error( 49 | `First Parameter in getMonthNames should specify the name Format which should be one of ${dataType}` 50 | ); 51 | return BSdata[lang][type] as any; 52 | } else { 53 | throw new Error(`Please specify type as one of ${nameType}`); 54 | } 55 | } 56 | 57 | export const getMonthNames = ( 58 | lang: Language = "np", 59 | length: Length 60 | ): string[] => { 61 | const allMonths = getNames("monthName", lang, length); 62 | return allMonths; 63 | }; 64 | 65 | export const getWeekNames = (lang: Language = "np", length: Length) => { 66 | const allMonths = getNames("dayName", lang, length); 67 | if (Array.isArray(allMonths)) return allMonths; 68 | return null; 69 | }; 70 | 71 | //TODo 72 | export function getValidYears( 73 | lang: Language, 74 | calendarType: CalendarType 75 | ): number[] | string[] { 76 | if (calendarType === "BS") { 77 | if (dataType.includes(lang)) { 78 | const allYears = Object.keys(BSdata.calendar_data); 79 | if (lang === "np" || lang === "rm") { 80 | const nepYears = allYears.map((a) => getNepaliNumber(a)); 81 | return nepYears; 82 | } else { 83 | return allYears; 84 | } 85 | } else { 86 | console.error( 87 | `Expected paramters for getValidYears is one of the : ${dataType}` 88 | ); 89 | const allYears = Object.keys(BSdata.calendar_data); 90 | if (lang === "np" || lang === "rm") { 91 | const nepYears = allYears.map((a) => getNepaliNumber(a)); 92 | return nepYears; 93 | } 94 | return []; 95 | } 96 | } else { 97 | let arr = []; 98 | for (let i = BSdata.minAdYear; i <= BSdata.maxAdYear; i++) { 99 | arr.push(i); 100 | } 101 | //TODO 102 | return arr; 103 | } 104 | } 105 | 106 | // type EachNumeral = keyof typeof BSdata.nums; 107 | // type Numeral = typeof BSdata.nums[EachNumeral]; 108 | export const getNepaliNumber = (n: number | string) => { 109 | if (isNaN(+n)) { 110 | throw new TypeError(`Expected Number instead got ${typeof n} value=${n}`); 111 | } 112 | let nep = ""; 113 | const str = String(n); 114 | for (let i = 0; i < str.length; i++) { 115 | nep += BSdata.npNumsArray[str[i] as any]; 116 | } 117 | return nep; 118 | }; 119 | 120 | //TODO 121 | type EachBSYear = keyof typeof BSdata.calendar_data; 122 | // type Numeral = typeof BSdata.calendar_data[EachNumeral]; 123 | const cache = { 124 | // stores the number of days to the end of corresponding year from the base date i.e. date.base_bs 125 | //TODO memoize 126 | getCumulativeTotal: () => { 127 | const years = Object.keys(BSdata.calendar_data); 128 | const startingYear = +years[0]; 129 | const totalYears = years.length; 130 | let obj: any = {}; //TODO 131 | 132 | for (let i = 0; i < totalYears; i++) { 133 | const yearIndex = startingYear + i; 134 | obj[yearIndex] = 135 | (i === 0 ? 0 : obj[yearIndex - 1]) + 136 | BSdata.calendar_data[yearIndex as EachBSYear].slice(-1)[0]; 137 | } 138 | return obj; 139 | }, 140 | }; 141 | 142 | /** 143 | * Returns the number of days from the base_bs day 144 | * @param {Date} date - AD date in the format 145 | */ 146 | const countBSDaysFromBaseDateUsingAdDate = ( 147 | year: number, 148 | month: number, 149 | day: number 150 | ) => { 151 | const { base_ad } = BSdata; 152 | 153 | const dateObj = { year, month: month - 1, day }; 154 | 155 | const date1 = new Date(base_ad.year, base_ad.month, base_ad.day); 156 | const date2 = new Date(dateObj.year, dateObj.month, dateObj.day); 157 | 158 | const timeDiff = date2.getTime() - date1.getTime(); 159 | 160 | const dayCount = Math.ceil(timeDiff / (1000 * 3600 * 24)); 161 | 162 | return dayCount; 163 | }; 164 | /** 165 | * Returns the Index of week 0 for sunday and so on.. 166 | * @param {number} daysCount - No. of days from the base bs 167 | */ 168 | const getDayIndex = (daysCount: number) => 169 | ((daysCount % 7) + BSdata.base_bs.dayOfWeek) % 7; 170 | 171 | //month =1 for Baisakh 172 | //TODO error checking 173 | export const ad2bs = (years: number, months: number, date: number) => { 174 | const { base_bs, calendar_data } = BSdata; 175 | const dayCount = countBSDaysFromBaseDateUsingAdDate(years, months, date); 176 | 177 | const cumulativeData = cache.getCumulativeTotal(); 178 | 179 | const values = Object.values(cumulativeData); 180 | const yearIndex = values.findIndex((value) => (value as number) >= dayCount); 181 | 182 | let year = +base_bs.year + yearIndex; 183 | let offsetDays = 184 | yearIndex === 0 ? dayCount : dayCount - cumulativeData[year - 1]; 185 | 186 | let month = 0; 187 | 188 | while (calendar_data[year as EachBSYear][month] <= offsetDays) { 189 | //check 190 | offsetDays -= calendar_data[year as EachBSYear][month]; 191 | month++; 192 | } 193 | if (+month === 12) { 194 | month = 0; 195 | year = year + 1; 196 | } 197 | 198 | return { 199 | year, 200 | month: month + 1, //1 for Baisakh 201 | date: offsetDays + 1, 202 | day: getDayIndex(dayCount), 203 | }; 204 | }; 205 | 206 | //month =1 for Baisakh 207 | //TODO error checking 208 | export const bs2ad = (year: number, month: number, day: number) => { 209 | const { base_ad, calendar_data } = BSdata; 210 | 211 | const cumulativeData = cache.getCumulativeTotal(); 212 | 213 | let prevMonthCumulativeTotal = 0; 214 | const prevYearCumulativeTotal = cumulativeData[+year - 1]; 215 | for (let i = 0; i < +month - 1; i++) { 216 | prevMonthCumulativeTotal += calendar_data[+year as EachBSYear][i]; 217 | } 218 | 219 | const countDays = 220 | prevYearCumulativeTotal + prevMonthCumulativeTotal + +day - 1; 221 | 222 | const date1 = new Date(base_ad.year, base_ad.month, base_ad.day); 223 | date1.setDate(date1.getDate() + countDays); 224 | 225 | const ad = { 226 | year: date1.getFullYear(), 227 | month: date1.getMonth() + 1, 228 | date: date1.getDate(), 229 | day: date1.getDay(), 230 | }; 231 | return ad; 232 | }; 233 | 234 | export const getStartingDayOfBsMonth = (year: number, month: number) => { 235 | const monthIndex = month - 1; 236 | const cumulativeData = cache.getCumulativeTotal(); 237 | const prevYearTotal = cumulativeData[year - 1] || 0; 238 | let days = 0; 239 | for (let i = 0; i < monthIndex; i++) { 240 | days += BSdata.calendar_data[year as EachBSYear][i]; 241 | } 242 | const daysCount = prevYearTotal + days; 243 | return getDayIndex(daysCount); 244 | }; 245 | 246 | type DateDetail = { 247 | date: number; 248 | year: number; 249 | month: number; 250 | monthName: string; 251 | }; 252 | type RangeDetail = { 253 | from: DateDetail; 254 | to: DateDetail; 255 | }; 256 | export const getBsRangeForAdCalendar = ( 257 | year: number, 258 | month: number 259 | ): RangeDetail => { 260 | const allNepaliMonth = getMonthNames("np", "full"); 261 | 262 | const bsfirst = ad2bs(year, month, 1); 263 | const lastDate = getTotalDaysInAdMonth(year, month); 264 | const bsLast = ad2bs(year, month, lastDate); 265 | 266 | const firstMonth = allNepaliMonth[bsfirst.month - 1]; 267 | const lastMonth = allNepaliMonth[bsLast.month - 1]; 268 | 269 | return { 270 | from: { ...bsfirst, monthName: firstMonth }, 271 | to: { ...bsLast, monthName: lastMonth }, 272 | }; 273 | }; 274 | export const getAdRangeForBsCalendar = ( 275 | year: number, 276 | month: number 277 | ): RangeDetail => { 278 | const allEnglishMonth = getMonthNames("en", "short"); 279 | 280 | const adfirst = bs2ad(year, month, 1); 281 | const lastDate = getTotalDaysInBsMonth(year, month); 282 | const adLast = bs2ad(year, month, lastDate); 283 | 284 | const firstMonth = allEnglishMonth[adfirst.month - 1]; 285 | const lastMonth = allEnglishMonth[adLast.month - 1]; 286 | 287 | return { 288 | from: { ...adfirst, monthName: firstMonth }, 289 | to: { ...adLast, monthName: lastMonth }, 290 | }; 291 | }; 292 | 293 | type DateInfo = { 294 | month: number; 295 | year: number; 296 | totalDays: number; 297 | }; 298 | 299 | //TODo Error 300 | export const getBsInfoOfoffsetDate = ( 301 | year: number, 302 | month: number, 303 | offset?: { year?: number; month?: number } 304 | ): DateInfo => { 305 | const yearOffset = year + (offset?.year ?? 0); 306 | const monthOffset = month + (offset?.month ?? 0); 307 | 308 | const newMonth = monthOffset % 12 || 12; 309 | const newYear = yearOffset + Math.floor(monthOffset / 12); 310 | 311 | const newMonthIndex = newMonth - 1; 312 | 313 | if (isInBetween(newYear, BSdata.minBsYear, BSdata.maxBsYear)) { 314 | const totalDays = 315 | BSdata.calendar_data[newYear as EachBSYear][newMonthIndex]; 316 | return { 317 | month: newMonth, 318 | year: newYear, 319 | totalDays, 320 | }; 321 | } else throw new Error("Error"); 322 | }; 323 | -------------------------------------------------------------------------------- /src/CalendarData/parser.ts: -------------------------------------------------------------------------------- 1 | import { IDateObject, IDateFormat } from "../types/main"; 2 | 3 | function parseDate(input: string, format: string): IDateObject { 4 | format = String(format)?.toLocaleLowerCase() || "yyyy-mm-dd"; // default format 5 | const parts = input.match(/(\d+)/g); 6 | if (parts) { 7 | let i = 0; 8 | const fmt: IDateFormat = {}; 9 | 10 | format.replace(/(yyyy|dd|d|mm|m)/g, function (part) { 11 | fmt[part as keyof IDateFormat] = i++; 12 | return ""; 13 | }); 14 | 15 | const yearIndex = fmt["yyyy"]; 16 | const monthIndex = fmt["mm"] ?? fmt["m"]; 17 | const dateIndex = fmt["dd"] ?? fmt["d"]; 18 | if (yearIndex === undefined) { 19 | throw new TypeError( 20 | `Year isn't Provided in the given date input or the format doesn't contain correct combination of 'yyyy'. Please Acceptable formats are the pemutation of 'yyyy', 'mm' and 'dd' instead got ${format} ` 21 | ); 22 | } 23 | if (monthIndex === undefined) { 24 | throw new TypeError( 25 | `Month isn't Provided in the given date input or the format doesn't contain correct combination of 'mm' | 'm'. Please Acceptable formats are the pemutation of 'yyyy', 'mm' and 'dd' instead got ${format} ` 26 | ); 27 | } 28 | if (dateIndex === undefined) { 29 | throw new TypeError( 30 | `Date isn't Provided in the given date input or the format doesn't contain correct combination of 'dd' | 'd'. Please Acceptable formats are the pemutation of 'yyyy', 'mm' and 'dd' instead got ${format} ` 31 | ); 32 | } 33 | 34 | if ( 35 | yearIndex !== undefined && 36 | monthIndex !== undefined && 37 | dateIndex !== undefined 38 | ) { 39 | const year = +parts[yearIndex]; 40 | const month = +parts[monthIndex]; 41 | const date = +parts[dateIndex]; 42 | 43 | return { year, month, date }; 44 | } else { 45 | throw new Error("error"); 46 | } 47 | } else { 48 | throw new TypeError("Passed Input isn't a valid date. Please check again"); 49 | } 50 | } 51 | export default parseDate; 52 | -------------------------------------------------------------------------------- /src/CalendarData/validator.ts: -------------------------------------------------------------------------------- 1 | import { getTotalDaysInBsMonth } from "./getBsData"; 2 | import { CalendarType, IDateObject } from "../types/main"; 3 | import { isInBetween } from "../utils"; 4 | import { 5 | minBsYear, 6 | maxBsYear, 7 | minAdYear, 8 | maxAdYear, 9 | minAdDate, 10 | minAdMonth, 11 | } from "./data"; 12 | import { getTotalDaysInAdMonth } from "../date-fns"; 13 | 14 | export const isMonthValid = ( 15 | month: number | string, 16 | throwError?: boolean 17 | ): boolean => { 18 | if (isNaN(+month)) { 19 | if (throwError) { 20 | throw new TypeError( 21 | `Month can be in between 1-2 instead got ${month} of type ${typeof month}` 22 | ); 23 | } 24 | return false; 25 | } 26 | if (+month >= 1 && +month <= 12) { 27 | return true; 28 | } 29 | 30 | if (throwError) 31 | throw new Error(`Month can be in between 1-12 instead got ${+month}`); 32 | 33 | return false; 34 | }; 35 | 36 | export const isBsDateValid = ( 37 | dateObj: IDateObject, 38 | throwError?: boolean 39 | ): boolean => { 40 | const { year, month, date } = dateObj; 41 | 42 | //Check if year is Valid 43 | if (isInBetween(year, minBsYear, maxBsYear)) { 44 | //Check if month is valid 45 | if (isMonthValid(month, throwError)) { 46 | const totalDays = getTotalDaysInBsMonth(year, month); 47 | //Check if Date is Valid 48 | if (dateObj.date <= totalDays) { 49 | return true; 50 | } else { 51 | if (throwError) 52 | throw new RangeError( 53 | `Total Days of the month ${month} of year ${year} BS is ${totalDays} but instead got ${date} as Date. in the given dateObject ${JSON.stringify( 54 | dateObj 55 | )}` 56 | ); 57 | } 58 | return false; 59 | } else return false; 60 | } else { 61 | if (throwError) { 62 | throw new RangeError( 63 | `Year ${minBsYear} BS - ${maxBsYear} BS is only supported. instead got ${year} in getMonthOffset function` 64 | ); 65 | } 66 | return false; 67 | } 68 | }; 69 | 70 | export const isAdDateValid = ( 71 | dateObj: IDateObject, 72 | throwError?: boolean 73 | ): boolean => { 74 | const { year, month, date } = dateObj; 75 | 76 | //Check if year is Valid 77 | if (isInBetween(year, minAdYear, maxAdYear)) { 78 | if (year === minAdYear) { 79 | if (month < minAdMonth) { 80 | if (throwError) { 81 | throw new RangeError( 82 | `Minimum valid Ad Date is year=${minAdYear} month=${minAdMonth} date=${minAdDate} instead got month=${month} for year ${year}` 83 | ); 84 | } 85 | return false; 86 | } 87 | if (month === minAdMonth) { 88 | if (date < minAdDate) { 89 | if (throwError) { 90 | throw new RangeError( 91 | `Minimum valid Ad Date is year=${minAdYear} month=${minAdMonth} date=${minAdDate} instead got date=${date} for year ${year} and month ${month}` 92 | ); 93 | } 94 | return false; 95 | } 96 | } 97 | } 98 | //Check if month is valid 99 | if (isMonthValid(month, throwError)) { 100 | const totalDays = getTotalDaysInAdMonth(year, month); 101 | //Check if Date is Valid 102 | if (date <= totalDays) { 103 | return true; 104 | } else { 105 | if (throwError) 106 | throw new RangeError( 107 | `Total Days of the month ${month} of year ${year} BS is ${totalDays} but instead got ${date} as Date. in the given dateObject ${JSON.stringify( 108 | dateObj 109 | )}` 110 | ); 111 | } 112 | return false; 113 | } else return false; 114 | } else { 115 | if (throwError) { 116 | throw new RangeError( 117 | `Year ${minBsYear} BS - ${maxBsYear} BS is only supported. instead got ${year} in getMonthOffset function` 118 | ); 119 | } 120 | return false; 121 | } 122 | }; 123 | 124 | export const isInValidRange = ( 125 | dateObj: IDateObject, 126 | calendarType: CalendarType, 127 | throwError: boolean = false 128 | ): boolean => { 129 | if (calendarType === "BS") { 130 | return isBsDateValid(dateObj, throwError); 131 | } else { 132 | return isAdDateValid(dateObj, throwError); 133 | } 134 | }; 135 | 136 | type DateFormat = { 137 | yyyy?: number; 138 | mm?: number; 139 | m?: number; 140 | dd?: number; 141 | d?: number; 142 | }; 143 | 144 | export function isDateValidWithFormat( 145 | input: string, 146 | format: string, 147 | throwError?: boolean 148 | ): boolean { 149 | if (!format || !input) { 150 | if (throwError) { 151 | throw new Error( 152 | `Date provided or Format isn't supported. Got date=${input} and format =${format}` 153 | ); 154 | } 155 | return false; 156 | } 157 | format = String(format)?.toLocaleLowerCase(); // default format 158 | const parts = input.match(/(\d+)/g); 159 | 160 | console.log({ parts }); 161 | if (parts) { 162 | let i = 0; 163 | const fmt: DateFormat = {}; 164 | 165 | format.replace(/(yyyy|dd|d|mm|m)/g, function (part) { 166 | fmt[part as keyof DateFormat] = i++; 167 | return ""; 168 | }); 169 | 170 | const yearIndex = fmt["yyyy"]; 171 | const monthIndex = fmt["mm"] ?? fmt["m"]; 172 | const dateIndex = fmt["dd"] ?? fmt["d"]; 173 | 174 | if (yearIndex === undefined) { 175 | if (throwError) 176 | throw new TypeError( 177 | `Year isn't Provided in the given date input or the format doesn't contain correct combination of 'yyyy'. Acceptable formats are the pemutation of 'yyyy', 'mm' and 'dd' instead got ${format} ` 178 | ); 179 | return false; 180 | } 181 | if (monthIndex === undefined) { 182 | if (throwError) 183 | throw new TypeError( 184 | `Month isn't Provided in the given date input or the format doesn't contain correct combination of 'mm' | 'm'. Acceptable formats are the pemutation of 'yyyy', 'mm','m','d', and 'dd' instead got ${format} ` 185 | ); 186 | return false; 187 | } 188 | if (dateIndex === undefined) { 189 | if (throwError) 190 | throw new TypeError( 191 | `Date isn't Provided in the given date input or the format doesn't contain correct combination of 'dd' | 'd'. Acceptable formats are the pemutation of 'yyyy', 'mm','m', 'd', and 'dd' instead got ${format} ` 192 | ); 193 | return false; 194 | } 195 | const year = parts[yearIndex]; 196 | const month = parts[monthIndex]; 197 | const date = parts[dateIndex]; 198 | 199 | if (!year || !month || !date) { 200 | if (throwError) { 201 | throw new Error("Invalid date"); 202 | } 203 | return false; 204 | } 205 | 206 | return true; 207 | } else { 208 | if (throwError) 209 | throw new TypeError( 210 | "Passed Input isn't a valid date. Please check again. Acceptable formats are the pemutation of 'yyyy', 'mm','m','d' and 'dd' " 211 | ); 212 | return false; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/DatePicker/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | 3 | import NepaliCalendar from "../Calendar"; 4 | import CalendarIcon from "../assets/CalendarIcon"; 5 | import CrossIcon from "../assets/CrossIcon"; 6 | 7 | import { usePopper } from "./usePopper"; 8 | import useCalendarType from "../hooks/useCalendarType"; 9 | 10 | import { dateFormatter, getDateFromObject } from "../date-fns"; 11 | import { 12 | isAdDateValid, 13 | isDateValidWithFormat, 14 | isBsDateValid, 15 | } from "../CalendarData/validator"; 16 | import { parseBsDate } from "../CalendarData"; 17 | // import { ad2bs, bs2ad, formatBsDate, parseBsDate } from "../CalendarData"; 18 | import { IDatePicker } from "../types/main"; 19 | import { getTodaysDate } from "Calendar/util"; 20 | 21 | const random_id = `rl-nepali-${Math.random()}`; 22 | 23 | function addDelimeter(enteredstring: string) { 24 | if (enteredstring.slice(-1) === "-") return enteredstring; 25 | if (enteredstring.length === 2) return enteredstring + "-"; 26 | if (enteredstring.length === 5) return enteredstring + "-"; 27 | return enteredstring; 28 | } 29 | 30 | const DatePicker = (props: IDatePicker) => { 31 | const { 32 | value, 33 | size = "small", 34 | onChange, 35 | isClearable = true, 36 | dateFormat = "yyyy-mm-dd", 37 | calendarType: calendarTypeFromProps, 38 | showMonthDropdown, 39 | placehoder, 40 | hideOnSelect = true, 41 | showCalenderOnlyWhenIconIsClicked, 42 | onSelect, 43 | children, 44 | showDelimiter = true, 45 | ...otherProps 46 | } = props; 47 | 48 | const calendarType = useCalendarType(calendarTypeFromProps); 49 | 50 | const isAD = calendarType === "AD"; 51 | 52 | //This is the data that is sent to the NepaliCalendar 53 | const [selectedDate, setSelectedDate] = useState(value); 54 | 55 | //This is what is shown in input field; 56 | const [entetereDate, setEnteredDate] = useState(value); 57 | //Eventually selectedDate and enteredDate is supposed to be equal but not neccesary. When the user is typing on the input field only enteredDate is selected and when s/he stops or blurs or presses enter then only the states are synced. 58 | 59 | useEffect(() => { 60 | setSelectedDate(value); 61 | }, [value]); 62 | 63 | const { popupRef, inputRef, isVisible, setIsVisible, containerRef } = 64 | usePopper(); 65 | 66 | const syncEnteredwithSelected = () => { 67 | if (isDateValidWithFormat(entetereDate, dateFormat)) { 68 | const parsedDateObj = parseBsDate(entetereDate, dateFormat); 69 | if (isAD && isAdDateValid(parsedDateObj)) { 70 | setSelectedDate(entetereDate); 71 | onChange(entetereDate); 72 | } 73 | if (isBsDateValid(parsedDateObj)) { 74 | setSelectedDate(entetereDate); 75 | onChange(entetereDate); 76 | } else { 77 | const x = getTodaysDate(); 78 | const ad = dateFormatter(getDateFromObject(x.ad), dateFormat); 79 | setSelectedDate(ad); 80 | onChange(ad); 81 | setEnteredDate(ad); 82 | } 83 | } else { 84 | const today = getTodaysDate(); 85 | 86 | if (entetereDate.length <= 3) { 87 | if (entetereDate.slice(-1) === "-") { 88 | const sliced = entetereDate.slice(0, entetereDate.length - 1); 89 | if (isAD) { 90 | today.ad.date = +sliced; 91 | } else { 92 | today.bs.date = +sliced; 93 | } 94 | } else { 95 | if (isAD) { 96 | today.ad.date = +entetereDate; 97 | } else { 98 | today.bs.date = +entetereDate; 99 | } 100 | } 101 | } 102 | const ad = dateFormatter( 103 | getDateFromObject(isAD ? today.ad : today.bs), 104 | dateFormat 105 | ); 106 | setSelectedDate(ad); 107 | onChange(ad); 108 | setEnteredDate(ad); 109 | } 110 | }; 111 | return ( 112 |
117 | {isClearable && ( 118 | { 121 | typeof onChange === "function" && onChange(""); 122 | setSelectedDate(""); 123 | setEnteredDate(""); 124 | }} 125 | /> 126 | )} 127 |
128 | 131 | !showCalenderOnlyWhenIconIsClicked && setIsVisible(true) 132 | } 133 | className={`rl-nepali-datepicker-input ${size}`} 134 | defaultValue={value} 135 | value={entetereDate} 136 | placeholder={`${placehoder ?? dateFormat} (${calendarType})`} 137 | onChange={(e) => { 138 | if (showDelimiter) { 139 | setEnteredDate(addDelimeter(e.target.value)); 140 | } else { 141 | setEnteredDate(e.target.value); 142 | } 143 | }} 144 | onBlur={() => { 145 | syncEnteredwithSelected(); 146 | }} 147 | onKeyDown={(e) => { 148 | if (e.key === "Backspace") { 149 | if (entetereDate.slice(-1) === "-") 150 | setEnteredDate(entetereDate.slice(0, entetereDate.length - 2)); 151 | } 152 | if (e.key === "Enter") { 153 | syncEnteredwithSelected(); 154 | setIsVisible(false); 155 | inputRef.current?.blur(); 156 | } 157 | }} 158 | /> 159 | 160 | setIsVisible(true)} 162 | className="rl-nepali-datepicker-icon hand-cursor" 163 | /> 164 |
165 | {isVisible && ( 166 |
167 | { 177 | hideOnSelect && setIsVisible(false); 178 | setEnteredDate(formattedDate); 179 | typeof onChange === "function" && 180 | onChange(formattedDate, adDate, bsDate, dateString); 181 | }} 182 | {...otherProps} 183 | > 184 | {children} 185 | 186 |
187 | )} 188 |
189 | ); 190 | }; 191 | 192 | export default DatePicker; 193 | -------------------------------------------------------------------------------- /src/DatePicker/useOnClickAway.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | //TODO make more specific 3 | function useOnClickAway(containerRef: React.RefObject) { 4 | const [clickedAway, setIsClickedAway] = useState(null); 5 | useEffect(() => { 6 | const handleClickOutside = (event: any) => { 7 | if ( 8 | containerRef.current && 9 | !containerRef.current.contains(event.target) 10 | ) { 11 | setIsClickedAway(true); 12 | } else { 13 | setIsClickedAway(false); 14 | } 15 | }; 16 | 17 | document.addEventListener("click", handleClickOutside, true); 18 | return () => { 19 | document.removeEventListener("click", handleClickOutside, true); 20 | }; 21 | }, [containerRef]); 22 | 23 | return clickedAway; 24 | } 25 | export default useOnClickAway; 26 | -------------------------------------------------------------------------------- /src/DatePicker/usePopper.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState, useLayoutEffect } from "react"; 2 | import { createPopper } from "@popperjs/core"; 3 | 4 | function usePopper() { 5 | const [isVisible, setIsVisible] = useState(false); 6 | 7 | const popupRef = useRef(null); 8 | const inputRef = useRef(null); 9 | const containerRef = useRef(null); 10 | 11 | useEffect(() => { 12 | const handleClickOutside = (event: any) => { 13 | if ( 14 | containerRef.current && 15 | !containerRef.current.contains(event.target) 16 | ) { 17 | setIsVisible(false); 18 | } 19 | }; 20 | 21 | document.addEventListener("click", handleClickOutside, true); 22 | return () => { 23 | document.removeEventListener("click", handleClickOutside, true); 24 | }; 25 | }, []); 26 | 27 | //useEffect causes flickering of popper from bottom-start to bottom-end 28 | useLayoutEffect(() => { 29 | if (isVisible) { 30 | const input = inputRef.current; 31 | const tooltip = popupRef.current; 32 | if (input && tooltip) 33 | createPopper(input, tooltip, { 34 | placement: "bottom-end", 35 | modifiers: [ 36 | { 37 | name: "offset", 38 | options: { 39 | offset: [0, 8], 40 | }, 41 | }, 42 | ], 43 | }); 44 | } 45 | }, [isVisible, popupRef]); 46 | 47 | return { 48 | popupRef, 49 | inputRef, 50 | isVisible, 51 | containerRef, 52 | setIsVisible, 53 | }; 54 | } 55 | 56 | export { usePopper }; 57 | -------------------------------------------------------------------------------- /src/NepaliDateRangePicker.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface RangePickerTypes { 4 | value: string; 5 | size: "large" | "small"; 6 | separator: " " | "-" | "/"; 7 | calendarType: "AD" | "BS"; 8 | } 9 | const RangePicker: React.FC = ({ 10 | value, 11 | size, 12 | separator, 13 | calendarType, 14 | }) => { 15 | return
Date Range Picker
; 16 | }; 17 | export default RangePicker; 18 | 19 | // class NepaliDateRangePicker extends Component { 20 | 21 | // static propTypes = { 22 | // /** Date Value in AD DD-MM-YYYY */ 23 | // value: PropTypes.string, 24 | // /** Size of input */ 25 | // size: PropTypes.oneOf(['large', 'small']), 26 | // /** Separator for input suggestion */ 27 | // separator: PropTypes.oneOf([' ', '-', '/']), 28 | 29 | // /** Override calendar type initially "AD" or "BS" */ 30 | // calendarType: PropTypes.oneOf(['AD', 'BS']), 31 | 32 | // /** Gives [AD DATE FROM, AD DATE TO] as params */ 33 | // onChange: PropTypes.func, 34 | 35 | // /** Logic to disable date, arguement is current date moment, return true or false */ 36 | // disableDate: PropTypes.func 37 | 38 | // } 39 | // constructor(props) { 40 | // super(props) 41 | 42 | // this.state = { 43 | 44 | // } 45 | // } 46 | 47 | // render() { 48 | // return
Date Range Picker
49 | // } 50 | // } 51 | -------------------------------------------------------------------------------- /src/Range/PreLabeled.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from "react"; 2 | import { CalendarType, IDateOffset } from "../types/main"; 3 | 4 | import NepaliCalendarForRange from "../Calendar"; 5 | import useDateRange from "./useDateRange"; 6 | import { 7 | compareDates, 8 | getNewAdDate, 9 | getNewBsDate, 10 | } from "../CalendarData/calculation"; 11 | import { isDateValidWithFormat } from "../CalendarData/validator"; 12 | import { formatBsDate, parseBsDate } from "CalendarData"; 13 | import { getTodaysDate } from "Calendar/util"; 14 | 15 | type DefinedRanges = { label: string; offset: number | IDateOffset }; 16 | interface IDefinedRange { 17 | from: string; 18 | to: string; 19 | onChange: (dateFrom: string | null, dateTo: string | null) => void; 20 | dateFormat: string; 21 | calendarType: CalendarType; 22 | definedRanges: DefinedRanges[]; 23 | baseDate: string; 24 | } 25 | 26 | const _defined = [ 27 | { 28 | label: "Last Year", 29 | offset: { year: -1 }, 30 | }, 31 | { 32 | label: "Last 15 days", 33 | offset: -15, 34 | }, 35 | { 36 | label: "Last 7 days", 37 | offset: -7, 38 | }, 39 | { 40 | label: "Last 25 days", 41 | offset: -25, 42 | }, 43 | { 44 | label: "Last 30 days", 45 | offset: -30, 46 | }, 47 | { 48 | label: "Last 45 days", 49 | offset: -45, 50 | }, 51 | { 52 | label: "Next week", 53 | offset: 7, 54 | }, 55 | ]; 56 | const baseDate = "2021-07-14"; 57 | const baseDateObj = { year: 2021, month: 7, date: 14 }; 58 | 59 | const NepaliCalendarRange = (props: IDefinedRange) => { 60 | const { 61 | from, 62 | to, 63 | onChange, 64 | dateFormat = "yyyy-mm-dd", 65 | calendarType, 66 | definedRanges = _defined, 67 | } = props; 68 | 69 | const { selectedDate, onDateSelect, setSelectedDate } = useDateRange( 70 | from, 71 | to, 72 | dateFormat 73 | ); 74 | 75 | const [selectedDefinition, setSelectionDefinition] = useState(""); 76 | 77 | const { from: selectedDateFrom, to: selectedDateTo } = selectedDate; 78 | 79 | const onChangeRef = useRef(onChange); 80 | useEffect(() => { 81 | const onChange = onChangeRef.current; 82 | typeof onChange === "function" && 83 | onChange(selectedDate.from, selectedDate.to); 84 | }, [onChangeRef, selectedDate.from, selectedDate.to]); 85 | 86 | const isAD = calendarType === "AD"; 87 | 88 | return ( 89 |
90 |
91 | {definedRanges.map((e) => { 92 | const offsetObj = 93 | typeof e.offset === "number" ? { date: e.offset } : e.offset; 94 | 95 | const newDate = isAD 96 | ? getNewAdDate({ ...offsetObj }, baseDateObj) 97 | : getNewBsDate({ ...offsetObj }, baseDateObj); 98 | const formattedNewDate = formatBsDate(newDate, dateFormat); 99 | 100 | const comparison = compareDates( 101 | formattedNewDate, 102 | baseDate, 103 | dateFormat 104 | ); 105 | 106 | const fromDate = comparison < 0 ? formattedNewDate : baseDate; 107 | const toDate = comparison < 0 ? baseDate : formattedNewDate; 108 | const range = `${fromDate} - ${toDate}`; 109 | 110 | const isSelected = selectedDefinition === e.label; 111 | 112 | return ( 113 |
{ 120 | setSelectionDefinition(e.label); 121 | setSelectedDate({ from: fromDate, to: toDate }); 122 | }} 123 | > 124 |
{e.label}
125 |
{range}
126 |
127 | ); 128 | })} 129 |
130 | 131 | { 151 | onDateSelect(adDate); 152 | }} 153 | dateFormat={dateFormat} 154 | calendarType={calendarType} 155 | /> 156 |
157 | ); 158 | }; 159 | 160 | export default NepaliCalendarRange; 161 | -------------------------------------------------------------------------------- /src/Range/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from "react"; 2 | import { ICalendarRange } from "../types/main"; 3 | 4 | import NepaliCalendarForRange from "../Calendar"; 5 | import useDateRange from "./useDateRange"; 6 | import { getNewAdDate, getNewBsDate } from "CalendarData/calculation"; 7 | import { getTodaysDate } from "Calendar/util"; 8 | import { formatBsDate } from "CalendarData"; 9 | 10 | const NepaliCalendarRange = (props: ICalendarRange) => { 11 | const { from, to, onChange, dateFormat = "yyyy-mm-dd", calendarType } = props; 12 | const isAD = calendarType === "AD"; 13 | 14 | const { selectedDate, onDateSelect } = useDateRange( 15 | from, 16 | to, 17 | dateFormat, 18 | isAD 19 | ); 20 | 21 | const { from: selectedDateFrom, to: selectedDateTo } = selectedDate; 22 | 23 | //Use Directly on on Select instead 24 | const onChangeRef = useRef(onChange); 25 | useEffect(() => { 26 | const onChange = onChangeRef.current; 27 | typeof onChange === "function" && 28 | onChange(selectedDate.from, selectedDate.to); 29 | }, [onChangeRef, selectedDate.from, selectedDate.to]); 30 | 31 | const today = getTodaysDate(); 32 | const nextMonthDateObj = isAD 33 | ? getNewAdDate({ month: 1 }, today.ad) 34 | : getNewBsDate({ month: 1 }, today.bs); 35 | const formattedNextMonthDate = formatBsDate(nextMonthDateObj, dateFormat); 36 | 37 | return ( 38 |
39 | { 43 | onDateSelect(isAD ? adDate : bsDate); 44 | }} 45 | dateFormat={dateFormat} 46 | calendarType={calendarType} 47 | /> 48 | 49 | { 53 | onDateSelect(isAD ? adDate : bsDate); 54 | }} 55 | dateFormat={dateFormat} 56 | calendarType={calendarType} 57 | /> 58 |
59 | ); 60 | }; 61 | 62 | export default NepaliCalendarRange; 63 | -------------------------------------------------------------------------------- /src/Range/useDateRange.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef, useCallback } from "react"; 2 | import { IDateObject } from "../types/main"; 3 | import { formatBsDate } from "../CalendarData"; 4 | import { isDateValidWithFormat } from "../CalendarData/validator"; 5 | import { parseDate, getDateFromObject } from "../date-fns"; 6 | 7 | type SelectedDateRange = { 8 | from: string | null; 9 | to: string | null; 10 | }; 11 | type RangeType = "from" | "to"; 12 | 13 | const useDateRange = ( 14 | dateFrom: string, 15 | dateTo: string, 16 | dateFormat: string, 17 | isAD = true 18 | ) => { 19 | const [selectedDate, setSelectedDate] = useState({ 20 | from: dateFrom, 21 | to: dateFrom, 22 | }); 23 | 24 | useEffect(() => { 25 | if (dateFrom && isDateValidWithFormat(dateFrom, dateFormat)) { 26 | setSelectedDate((selectedDate) => ({ ...selectedDate, from: dateFrom })); 27 | } 28 | }, [dateFrom, dateFormat]); 29 | 30 | useEffect(() => { 31 | if (dateTo && isDateValidWithFormat(dateTo, dateFormat)) { 32 | setSelectedDate((selectedDate) => ({ ...selectedDate, to: dateTo })); 33 | } 34 | }, [dateTo, dateFormat]); 35 | 36 | //specifies whose turn to update from or to 37 | const turnRef = useRef("from"); 38 | 39 | const onDateSelect = useCallback( 40 | (adDate: IDateObject) => { 41 | const turn = turnRef.current; 42 | const date = getDateFromObject(adDate); 43 | const formattedDate = formatBsDate(adDate, dateFormat); 44 | 45 | setSelectedDate((selectedDate) => { 46 | const { from: selectedDateFrom, to: selectedDateTo } = selectedDate; 47 | if (turn === "from") { 48 | const dateTo = selectedDateTo 49 | ? parseDate(selectedDateTo, dateFormat) 50 | : null; 51 | if (dateTo && dateTo < date) { 52 | return { from: formattedDate, to: null }; 53 | } 54 | } 55 | if (turn === "to") { 56 | const dateFrom = selectedDateFrom 57 | ? parseDate(selectedDateFrom, dateFormat) 58 | : null; 59 | if (dateFrom && dateFrom > date) { 60 | return { to: formattedDate, from: null }; 61 | } 62 | } 63 | const newSelection = { ...selectedDate, [turn]: formattedDate }; 64 | 65 | return { ...newSelection }; 66 | }); 67 | 68 | if (turn === "from") turnRef.current = "to"; 69 | else { 70 | turnRef.current = "from"; 71 | } 72 | }, 73 | [dateFormat] 74 | ); 75 | 76 | return { selectedDate, onDateSelect, setSelectedDate }; 77 | }; 78 | 79 | export default useDateRange; 80 | -------------------------------------------------------------------------------- /src/assets/CalendarIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | type CalendarIconProps = { 4 | onClick: () => void; 5 | className: string; 6 | }; 7 | const CalendarIcon: React.FC = (props) => { 8 | return ( 9 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | 22 | export default CalendarIcon; 23 | -------------------------------------------------------------------------------- /src/assets/CrossIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | type CrossIconProps = { 4 | className?: string; 5 | onClick?: () => void; 6 | visible?: boolean; 7 | }; 8 | function CrossIcon({ 9 | className = "", 10 | onClick, 11 | visible = false, 12 | }: CrossIconProps) { 13 | return visible ? ( 14 |
{ 16 | typeof onClick === "function" && onClick(); 17 | }} 18 | title="Clear" 19 | className={`cross-icon hand-cursor ${className}`} 20 | > 21 | ✕ 22 |
23 | ) : null; 24 | } 25 | export default CrossIcon; 26 | -------------------------------------------------------------------------------- /src/date-fns/index.ts: -------------------------------------------------------------------------------- 1 | import { IDateObject } from "../types/main"; 2 | import parse from "date-fns/parse"; 3 | import format from "date-fns/format"; 4 | import { isDateValidWithFormat } from "../CalendarData/validator"; 5 | 6 | //Since this lib will presumably require only year, month and date 7 | // converting all small to Capital M as the date-fns require month to be "M" 8 | // Allowing flexbility for user of this library to make use of m, y and d , anyway they want 9 | const dateFormatFormatter = (dateFormat: string) => { 10 | return dateFormat.toLowerCase()?.replaceAll("m", "M"); 11 | }; 12 | 13 | export const parseDate = (initialDate: string, dateFormat: string): Date => { 14 | const formatString = dateFormatFormatter(dateFormat); 15 | return parse(initialDate, formatString, new Date()); 16 | }; 17 | 18 | export const getDateFromObject = (obj: IDateObject): Date => { 19 | const date = obj.year + "-" + obj.month + "-" + obj.date; 20 | return new Date(date); 21 | }; 22 | 23 | export const dateFormatter = (date: Date, dateFormat: string): string => { 24 | const formatString = dateFormatFormatter(dateFormat); 25 | 26 | return format(date, formatString); 27 | }; 28 | 29 | export const getInfoOfOffsetDate = ( 30 | year: number, 31 | month: number, 32 | offsetObj?: { year?: number; month?: number } 33 | ) => { 34 | // const currentDate = new Date(year, month, 1); 35 | 36 | const offsetDate = new Date( 37 | year + +(offsetObj?.year ?? 0), 38 | month + +(offsetObj?.month ?? 0), 39 | 0 40 | ); 41 | 42 | const totalDays = offsetDate.getDate(); 43 | 44 | return { 45 | year: offsetDate.getFullYear(), 46 | month: offsetDate.getMonth() + 1, 47 | totalDays: totalDays, 48 | }; 49 | }; 50 | export const getDateObj = ( 51 | date: string, 52 | dateFormat: string 53 | ): IDateObject | null => { 54 | if (date && isDateValidWithFormat(date, dateFormat)) { 55 | const dateString = parseDate(date, dateFormat); 56 | 57 | return { 58 | date: dateString.getDate(), 59 | month: dateString.getMonth() + 1, 60 | year: dateString.getFullYear(), 61 | }; 62 | } 63 | return null; 64 | }; 65 | 66 | export const changeDateFromOneFormatToAnother = ( 67 | date: string, 68 | previousDateFormat: string, 69 | newDateFormat: string 70 | ): string => { 71 | const previousDate = parseDate(date, previousDateFormat); 72 | 73 | const newDate = dateFormatter(previousDate, newDateFormat); 74 | return newDate; 75 | }; 76 | 77 | export function getTotalDaysInAdMonth(year: number, month: number): number { 78 | return new Date(year, month, 0).getDate(); 79 | } 80 | 81 | type offsetType = { 82 | year?: number; 83 | month?: number; 84 | day?: number; 85 | }; 86 | -------------------------------------------------------------------------------- /src/doc.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raralabs/react-patro/0e727cf0f65e5e96bb08626e93f21aec85ac3cab/src/doc.md -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import useCalendarType from "./useCalendarType"; 2 | 3 | export { useCalendarType }; 4 | -------------------------------------------------------------------------------- /src/hooks/useCalendarType.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { CalendarType } from "../types/main"; 3 | 4 | //TODO generalize for global cases. 5 | // Maybe used in future for global context.. Maybe... 6 | function useCalendarType(calendarTypeFromProps: CalendarType): CalendarType { 7 | const [calendarType] = useState(calendarTypeFromProps ?? "BS"); 8 | 9 | return calendarType; 10 | } 11 | 12 | export default useCalendarType; 13 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import NepaliCalendar from "./Calendar"; 2 | import DatePicker from "./DatePicker"; 3 | import RangeCalendar from "./Range"; 4 | import DefinedRangeCalendar from "./Range/PreLabeled"; 5 | 6 | // import { 7 | // ad2bs, 8 | // bs2ad, 9 | // isBsDateValid, 10 | // isInValidRange, 11 | // } from "./CalendarData"; 12 | // import parseDate from "./CalendarData/parser"; 13 | // import format from "./CalendarData/format"; 14 | // import { isAdDateValid } from "./CalendarData/validator"; 15 | 16 | export { DatePicker, NepaliCalendar, RangeCalendar, DefinedRangeCalendar }; 17 | -------------------------------------------------------------------------------- /src/nepali_date_picker.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --cl-primary: #3851a3; 3 | --cl-primary2: rgba(56, 81, 163, 0.2); 4 | --cl-danger: #f15c61; 5 | --cl-success: #3fbeb2; 6 | --cl-secondary: #878f9d; 7 | --font-xs: 0.64rem; 8 | --font-sm: 0.8rem; 9 | --font-md: 1rem; 10 | --font-lg: 1.25rem; 11 | --font-xl: 1.563rem; 12 | --font-xxl: 2rem; 13 | --font-r: 0.875rem; 14 | 15 | --gray90: #1d2530; 16 | --gray80: #343c46; 17 | --gray60: #636972; 18 | --gray50: #91979f; 19 | --gray20: #afb5bb; 20 | --gray10: #cbd0d6; 21 | --gray5: #eef2f7; 22 | --debit: #143e9f; 23 | --link: #143e9f; 24 | --credit: #fc814a; 25 | --white: #fff; 26 | --danger: #ff6b6b; 27 | } 28 | 29 | .font-sm { 30 | font-size: var(--font-sm); 31 | } 32 | .font-md { 33 | font-size: var(--font-md); 34 | } 35 | .gray-60 { 36 | color: var(--gray60); 37 | } 38 | .gray-20 { 39 | color: var(--gray20); 40 | } 41 | .flex { 42 | display: flex; 43 | } 44 | .input-wrapper { 45 | position: relative; 46 | display: inline-block; 47 | width: 100%; 48 | min-width: 0; 49 | padding: 4px 11px; 50 | color: rgba(0, 0, 0, 0.65); 51 | font-size: 14px; 52 | line-height: 1.5715; 53 | background-color: #fff; 54 | background-image: none; 55 | border: 1px solid #d9d9d9; 56 | border-radius: 2px; 57 | -webkit-transition: all 0.3s; 58 | transition: all 0.3s; 59 | display: -ms-inline-flexbox; 60 | display: inline-flex; 61 | } 62 | .input-wrapper input { 63 | padding: 0; 64 | border: none; 65 | outline: none; 66 | } 67 | .hand-cursor { 68 | cursor: pointer !important; 69 | } 70 | 71 | .rl-nepali-calendar__month-select { 72 | color: black; 73 | outline: none; 74 | } 75 | .rl-nepali-datepicker-icon { 76 | width: 16px; 77 | filter: invert(59%) sepia(19%) saturate(232%) hue-rotate(179deg) 78 | brightness(93%) contrast(86%); 79 | } 80 | 81 | .rl-nepali-datepicker-wrapper { 82 | position: relative; 83 | /* width: fit-content; */ 84 | } 85 | 86 | .rl-nepali-datepicker-input { 87 | width: 100%; 88 | } 89 | 90 | .popovercalendar .ant-popover-inner-content { 91 | padding: 0px; 92 | } 93 | 94 | .today-btn { 95 | color: var(--cl-primary); 96 | font-weight: 600; 97 | } 98 | 99 | .rl-nepali-datepicker-content { 100 | position: absolute; 101 | z-index: 4; 102 | box-shadow: 0px 4px 10px rgba(19, 31, 54, 0.25); 103 | background-color: white; 104 | top: 24px; 105 | right: 0px; 106 | } 107 | 108 | .rl-nepali-datepicker-wrapper ul { 109 | list-style-type: none; 110 | } 111 | 112 | .rl-nepali-date-panel-wrapper { 113 | display: flex; 114 | flex-direction: row; 115 | flex-wrap: nowrap; 116 | } 117 | 118 | .rl-nepali-date-referenc-list { 119 | background-color: white; 120 | width: 120px; 121 | padding-top: 20px; 122 | font-weight: normal; 123 | font-size: 12.8px; 124 | line-height: 15px; 125 | /* identical to box height */ 126 | /* Theme_Blue */ 127 | color: var(--cl-primary); 128 | overflow-y: scroll; 129 | padding-left: 10px; 130 | } 131 | .reference-item { 132 | margin-bottom: 20px; 133 | cursor: pointer; 134 | } 135 | 136 | .rl-nepali-date-panel { 137 | width: 320px; 138 | z-index: 4; 139 | background-color: white; 140 | } 141 | 142 | .rl-nepali-date-body { 143 | padding: 8px 12px; 144 | } 145 | 146 | .rl-nepali-date-panel .left-actions div, 147 | .rl-nepali-date-panel .right-actions div { 148 | color: #ddd; 149 | } 150 | 151 | .rl-nepali-date-panel .left-actions div:hover, 152 | .rl-nepali-date-panel .right-actions div:hover { 153 | color: white; 154 | } 155 | 156 | .rl-nepali-date-panel .rl-nepalo-date-body { 157 | padding: 8px 12px; 158 | } 159 | 160 | /* Month header */ 161 | 162 | .month-header { 163 | /* width: 100%; */ 164 | background: var(--cl-primary); 165 | text-align: center; 166 | padding: 12px 8px; 167 | color: white; 168 | display: flex; 169 | flex-direction: row; 170 | align-items: center; 171 | justify-content: space-between; 172 | } 173 | 174 | .month-header .left-actions, 175 | .month-header .right-actions { 176 | display: flex; 177 | flex-direction: row; 178 | align-items: center; 179 | } 180 | 181 | .month-header .left-actions :first-child, 182 | .month-header .right-actions :first-child { 183 | margin-right: 12px; 184 | } 185 | 186 | .rl-nepali-date-content { 187 | width: 100%; 188 | } 189 | 190 | .rl-nepali-date-content thead th { 191 | font-size: 12px; 192 | } 193 | 194 | .rl-nepali-date-content th, 195 | .rl-nepali-date-content td { 196 | color: black; 197 | font-weight: 400; 198 | } 199 | 200 | .rl-picker-cell { 201 | /* border-radius: 2px; */ 202 | padding: 2px; 203 | cursor: pointer; 204 | } 205 | 206 | .rl-picker-cell .rl-picker-cell-inner { 207 | position: relative; 208 | /* z-index: 2; */ 209 | margin: auto; 210 | min-width: 30px; 211 | width: inherit; 212 | text-align: center; 213 | height: 30px; 214 | line-height: 100%; 215 | border-radius: 2px; 216 | -webkit-transition: background 0.3s, border 0.3s; 217 | transition: background 0.3s, border 0.3s; 218 | font-size: 14; 219 | display: flex; 220 | align-items: center; 221 | justify-content: center; 222 | } 223 | 224 | .rl-picker-cell .rl-picker-cell-inner .BS { 225 | font-size: 14px; 226 | } 227 | 228 | .rl-picker-cell .rl-picker-cell-inner .AD { 229 | position: absolute; 230 | right: 2px; 231 | bottom: 0px; 232 | font-size: 7px; 233 | } 234 | 235 | .rl-picker-cell:not(.disabled).in-range { 236 | background-color: var(--cl-primary2); 237 | } 238 | 239 | .rl-picker-cell:not(.disabled):hover .rl-picker-cell-inner { 240 | background-color: var(--cl-primary2); 241 | } 242 | 243 | .rl-picker-cell:not(.disabled).active .rl-picker-cell-inner { 244 | background-color: var(--cl-primary); 245 | color: white; 246 | } 247 | 248 | .rl-picker-cell:not(.disabled).today .rl-picker-cell-inner { 249 | border-color: var(--cl-primary); 250 | border: 1px solid; 251 | } 252 | 253 | .rl-picker-cell:not(.disabled).other-month .rl-picker-cell-inner { 254 | color: rgba(0, 0, 0, 0.4); 255 | /* background-color:var(--cl-secondary) ; */ 256 | } 257 | 258 | .rl-picker-cell.disabled { 259 | color: var(--cl-secondary); 260 | cursor: not-allowed; 261 | background-color: #ddd; 262 | } 263 | 264 | /* INLINE DROPDOWN CSS START*/ 265 | 266 | .inline-dropdown { 267 | position: relative; 268 | display: inline-block; 269 | z-index: 2; 270 | padding: 0; 271 | margin: 0; 272 | outline: 0; 273 | text-align: left; 274 | } 275 | 276 | .inline-dropdown:focus { 277 | pointer-events: none; 278 | } 279 | 280 | .inline-dropdown:focus .inline-dropdown-container { 281 | opacity: 1; 282 | visibility: visible; 283 | } 284 | 285 | .inline-dropdown-container { 286 | width: auto; 287 | margin: 3px 0 0 0; 288 | padding: 10px; 289 | border-radius: 3px; 290 | border-radius: 3px; 291 | pointer-events: auto; 292 | position: absolute; 293 | z-index: 5; 294 | opacity: 0; 295 | visibility: hidden; 296 | transition: visibility 1s; 297 | height: 200px; 298 | overflow-y: scroll; 299 | background-color: white; 300 | box-shadow: 0px 4px 10px rgba(19, 31, 54, 0.25); 301 | border-radius: 4px; 302 | color: black; 303 | } 304 | 305 | .inline-dropdown-container .inline-dropdown-item { 306 | cursor: pointer; 307 | padding-left: 8px; 308 | padding-right: 8px; 309 | } 310 | 311 | .inline-dropdown-container .inline-dropdown-item:hover { 312 | background-color: var(--cl-primary2); 313 | } 314 | 315 | .inline-dropdown-container .inline-dropdown-item.selected { 316 | background-color: var(--cl-primary); 317 | color: white; 318 | } 319 | 320 | /* INLINE DROPDOWN CSS END */ 321 | 322 | .rl-range-calendar { 323 | display: flex; 324 | flex-wrap: nowrap; 325 | width: fit-content; 326 | } 327 | 328 | .rl-nepali-rangepicker-wrapper .input-split { 329 | background-color: #fff; 330 | } 331 | 332 | .rl-nepali-rangepicker-wrapper .input-right { 333 | border-left-width: 0; 334 | } 335 | 336 | .rl-nepali-rangepicker-wrapper .input-right:hover, 337 | .rl-nepali-rangepicker-wrapper .input-right:focus { 338 | border-left-width: 1px; 339 | } 340 | 341 | .rl-daterange-toggler { 342 | display: flex; 343 | flex-wrap: nowrap; 344 | width: fit-content; 345 | /* background-color: var(--cl-success); */ 346 | } 347 | 348 | .rl-daterange-toggler .rl-daterange-toggler-content { 349 | display: flex; 350 | flex-wrap: nowrap; 351 | width: fit-content; 352 | margin-left: 8px; 353 | } 354 | 355 | .rl-daterange-toggler .rl-daterange-toggler-content .label-body { 356 | margin-left: 12px; 357 | margin-right: 12px; 358 | } 359 | 360 | .rl-daterange-toggler .rl-daterange-toggler-content .switch { 361 | width: 20px; 362 | height: 20px; 363 | border-radius: 50%; 364 | background-color: var(--cl-primary2); 365 | display: flex; 366 | justify-content: center; 367 | align-items: center; 368 | } 369 | 370 | .rl-daterange-toggler .selector-main { 371 | display: flex; 372 | } 373 | 374 | .rl-daterange-toggler .down-arrow { 375 | font-size: 110%; 376 | line-height: 0.9rem; 377 | margin-left: 8px; 378 | } 379 | 380 | .selector-content-wrapper .selector-content { 381 | display: flex; 382 | flex-direction: column; 383 | } 384 | 385 | .selector-content-wrapper { 386 | display: flex; 387 | } 388 | 389 | .cross-icon { 390 | position: absolute; 391 | z-index: 1; 392 | background: var(--cl-secondary); 393 | font-size: 10px; 394 | line-height: 100%; 395 | display: flex; 396 | justify-content: center; 397 | align-items: center; 398 | border-radius: 50%; 399 | color: white; 400 | opacity: 0; 401 | width: 10px; 402 | height: 10px; 403 | top: 5px; 404 | right: 5px; 405 | padding: 0px; 406 | margin: 0px; 407 | font-size: 12px; 408 | padding: 2px; 409 | } 410 | .cross-icon { 411 | opacity: 1; 412 | } 413 | 414 | .rl-defined-range-selector { 415 | z-index: 4; 416 | background-color: white; 417 | padding: 4px 6px; 418 | font-weight: 400; 419 | width: fit-content; 420 | display: flex; 421 | } 422 | 423 | .rl-defined-range-selector .rl-defined-range-label-wrapper { 424 | display: flex; 425 | flex-direction: column; 426 | border-right: 1px solid var(--gray20); 427 | margin-right: 10px; 428 | width: 300px; 429 | } 430 | .rl-defined-range-selector 431 | .rl-defined-range-label-wrapper 432 | .rl-defined-range-label { 433 | padding: 12px; 434 | } 435 | .rl-defined-range-selector 436 | .rl-defined-range-label-wrapper 437 | .rl-defined-range-label:hover { 438 | background: var(--cl-primary2); 439 | cursor: pointer; 440 | } 441 | 442 | .rl-defined-range-label-selected { 443 | background: var(--cl-primary2) !important; 444 | } 445 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --cl-primary: #3851a3; 3 | --cl-primary2: rgba(56, 81, 163, 0.2); 4 | --cl-danger: #f15c61; 5 | --cl-success: #3fbeb2; 6 | --cl-secondary: #878f9d; 7 | --font-xs: 0.64rem; 8 | --font-sm: 0.8rem; 9 | --font-md: 1rem; 10 | --font-lg: 1.25rem; 11 | --font-xl: 1.563rem; 12 | --font-xxl: 2rem; 13 | --font-r: 0.875rem; 14 | 15 | --gray90: #1d2530; 16 | --gray80: #343c46; 17 | --gray60: #636972; 18 | --gray50: #91979f; 19 | --gray20: #afb5bb; 20 | --gray10: #cbd0d6; 21 | --gray5: #eef2f7; 22 | --debit: #143e9f; 23 | --link: #143e9f; 24 | --credit: #fc814a; 25 | --white: #fff; 26 | --danger: #ff6b6b; 27 | } 28 | 29 | .font-sm { 30 | font-size: var(--font-sm); 31 | } 32 | .font-md { 33 | font-size: var(--font-md); 34 | } 35 | .gray-60 { 36 | color: var(--gray60); 37 | } 38 | .gray-20 { 39 | color: var(--gray20); 40 | } 41 | .flex { 42 | display: flex; 43 | } 44 | .input-wrapper { 45 | position: relative; 46 | display: inline-block; 47 | width: 100%; 48 | min-width: 0; 49 | padding: 4px 11px; 50 | color: rgba(0, 0, 0, 0.65); 51 | font-size: 14px; 52 | line-height: 1.5715; 53 | background-color: #fff; 54 | background-image: none; 55 | border: 1px solid #d9d9d9; 56 | border-radius: 2px; 57 | -webkit-transition: all 0.3s; 58 | transition: all 0.3s; 59 | display: -ms-inline-flexbox; 60 | display: inline-flex; 61 | } 62 | .input-wrapper input { 63 | padding: 0; 64 | border: none; 65 | outline: none; 66 | } 67 | .hand-cursor { 68 | cursor: pointer !important; 69 | } 70 | 71 | .rl-nepali-calendar__month-select { 72 | color: black; 73 | outline: none; 74 | } 75 | .rl-nepali-datepicker-icon { 76 | width: 16px; 77 | filter: invert(59%) sepia(19%) saturate(232%) hue-rotate(179deg) 78 | brightness(93%) contrast(86%); 79 | } 80 | 81 | .rl-nepali-datepicker-wrapper { 82 | position: relative; 83 | /* width: fit-content; */ 84 | } 85 | 86 | .rl-nepali-datepicker-input { 87 | width: 100%; 88 | } 89 | 90 | .popovercalendar .ant-popover-inner-content { 91 | padding: 0px; 92 | } 93 | 94 | .today-btn { 95 | color: var(--cl-primary); 96 | font-weight: 600; 97 | } 98 | 99 | .rl-nepali-datepicker-content { 100 | position: absolute; 101 | z-index: 4; 102 | box-shadow: 0px 4px 10px rgba(19, 31, 54, 0.25); 103 | background-color: white; 104 | top: 24px; 105 | right: 0px; 106 | } 107 | 108 | .rl-nepali-datepicker-wrapper ul { 109 | list-style-type: none; 110 | } 111 | 112 | .rl-nepali-date-panel-wrapper { 113 | display: flex; 114 | flex-direction: row; 115 | flex-wrap: nowrap; 116 | } 117 | 118 | .rl-nepali-date-referenc-list { 119 | background-color: white; 120 | width: 120px; 121 | padding-top: 20px; 122 | font-weight: normal; 123 | font-size: 12.8px; 124 | line-height: 15px; 125 | /* identical to box height */ 126 | /* Theme_Blue */ 127 | color: var(--cl-primary); 128 | overflow-y: scroll; 129 | padding-left: 10px; 130 | } 131 | .reference-item { 132 | margin-bottom: 20px; 133 | cursor: pointer; 134 | } 135 | 136 | .rl-nepali-date-panel { 137 | width: 320px; 138 | z-index: 4; 139 | background-color: white; 140 | } 141 | 142 | .rl-nepali-date-body { 143 | padding: 8px 12px; 144 | } 145 | 146 | .rl-nepali-date-panel .left-actions div, 147 | .rl-nepali-date-panel .right-actions div { 148 | color: #ddd; 149 | } 150 | 151 | .rl-nepali-date-panel .left-actions div:hover, 152 | .rl-nepali-date-panel .right-actions div:hover { 153 | color: white; 154 | } 155 | 156 | .rl-nepali-date-panel .rl-nepalo-date-body { 157 | padding: 8px 12px; 158 | } 159 | 160 | /* Month header */ 161 | 162 | .month-header { 163 | /* width: 100%; */ 164 | background: var(--cl-primary); 165 | text-align: center; 166 | padding: 12px 8px; 167 | color: white; 168 | display: flex; 169 | flex-direction: row; 170 | align-items: center; 171 | justify-content: space-between; 172 | } 173 | 174 | .month-header .left-actions, 175 | .month-header .right-actions { 176 | display: flex; 177 | flex-direction: row; 178 | align-items: center; 179 | } 180 | 181 | .month-header .left-actions :first-child, 182 | .month-header .right-actions :first-child { 183 | margin-right: 12px; 184 | } 185 | 186 | .rl-nepali-date-content { 187 | width: 100%; 188 | } 189 | 190 | .rl-nepali-date-content thead th { 191 | font-size: 12px; 192 | } 193 | 194 | .rl-nepali-date-content th, 195 | .rl-nepali-date-content td { 196 | color: black; 197 | font-weight: 400; 198 | } 199 | 200 | .rl-picker-cell { 201 | /* border-radius: 2px; */ 202 | padding: 2px; 203 | cursor: pointer; 204 | } 205 | 206 | .rl-picker-cell .rl-picker-cell-inner { 207 | position: relative; 208 | /* z-index: 2; */ 209 | margin: auto; 210 | min-width: 30px; 211 | width: inherit; 212 | text-align: center; 213 | height: 30px; 214 | line-height: 100%; 215 | border-radius: 2px; 216 | -webkit-transition: background 0.3s, border 0.3s; 217 | transition: background 0.3s, border 0.3s; 218 | font-size: 14; 219 | display: flex; 220 | align-items: center; 221 | justify-content: center; 222 | } 223 | 224 | .rl-picker-cell .rl-picker-cell-inner .BS { 225 | font-size: 14px; 226 | } 227 | 228 | .rl-picker-cell .rl-picker-cell-inner .AD { 229 | position: absolute; 230 | right: 2px; 231 | bottom: 0px; 232 | font-size: 7px; 233 | } 234 | 235 | .rl-picker-cell:not(.disabled).in-range { 236 | background-color: var(--cl-primary2); 237 | } 238 | 239 | .rl-picker-cell:not(.disabled):hover .rl-picker-cell-inner { 240 | background-color: var(--cl-primary2); 241 | } 242 | 243 | .rl-picker-cell:not(.disabled).active .rl-picker-cell-inner { 244 | background-color: var(--cl-primary); 245 | color: white; 246 | } 247 | 248 | .rl-picker-cell:not(.disabled).today .rl-picker-cell-inner { 249 | border-color: var(--cl-primary); 250 | border: 1px solid; 251 | } 252 | 253 | .rl-picker-cell:not(.disabled).other-month .rl-picker-cell-inner { 254 | color: rgba(0, 0, 0, 0.4); 255 | /* background-color:var(--cl-secondary) ; */ 256 | } 257 | 258 | .rl-picker-cell.disabled { 259 | color: var(--cl-secondary); 260 | cursor: not-allowed; 261 | background-color: #ddd; 262 | } 263 | 264 | /* INLINE DROPDOWN CSS START*/ 265 | 266 | .inline-dropdown { 267 | position: relative; 268 | display: inline-block; 269 | z-index: 2; 270 | padding: 0; 271 | margin: 0; 272 | outline: 0; 273 | text-align: left; 274 | } 275 | 276 | .inline-dropdown:focus { 277 | pointer-events: none; 278 | } 279 | 280 | .inline-dropdown:focus .inline-dropdown-container { 281 | opacity: 1; 282 | visibility: visible; 283 | } 284 | 285 | .inline-dropdown-container { 286 | width: auto; 287 | margin: 3px 0 0 0; 288 | padding: 10px; 289 | border-radius: 3px; 290 | border-radius: 3px; 291 | pointer-events: auto; 292 | position: absolute; 293 | z-index: 5; 294 | opacity: 0; 295 | visibility: hidden; 296 | transition: visibility 1s; 297 | height: 200px; 298 | overflow-y: scroll; 299 | background-color: white; 300 | box-shadow: 0px 4px 10px rgba(19, 31, 54, 0.25); 301 | border-radius: 4px; 302 | color: black; 303 | } 304 | 305 | .inline-dropdown-container .inline-dropdown-item { 306 | cursor: pointer; 307 | padding-left: 8px; 308 | padding-right: 8px; 309 | } 310 | 311 | .inline-dropdown-container .inline-dropdown-item:hover { 312 | background-color: var(--cl-primary2); 313 | } 314 | 315 | .inline-dropdown-container .inline-dropdown-item.selected { 316 | background-color: var(--cl-primary); 317 | color: white; 318 | } 319 | 320 | /* INLINE DROPDOWN CSS END */ 321 | 322 | .rl-range-calendar { 323 | display: flex; 324 | flex-wrap: nowrap; 325 | width: fit-content; 326 | } 327 | 328 | .rl-nepali-rangepicker-wrapper .input-split { 329 | background-color: #fff; 330 | } 331 | 332 | .rl-nepali-rangepicker-wrapper .input-right { 333 | border-left-width: 0; 334 | } 335 | 336 | .rl-nepali-rangepicker-wrapper .input-right:hover, 337 | .rl-nepali-rangepicker-wrapper .input-right:focus { 338 | border-left-width: 1px; 339 | } 340 | 341 | .rl-daterange-toggler { 342 | display: flex; 343 | flex-wrap: nowrap; 344 | width: fit-content; 345 | /* background-color: var(--cl-success); */ 346 | } 347 | 348 | .rl-daterange-toggler .rl-daterange-toggler-content { 349 | display: flex; 350 | flex-wrap: nowrap; 351 | width: fit-content; 352 | margin-left: 8px; 353 | } 354 | 355 | .rl-daterange-toggler .rl-daterange-toggler-content .label-body { 356 | margin-left: 12px; 357 | margin-right: 12px; 358 | } 359 | 360 | .rl-daterange-toggler .rl-daterange-toggler-content .switch { 361 | width: 20px; 362 | height: 20px; 363 | border-radius: 50%; 364 | background-color: var(--cl-primary2); 365 | display: flex; 366 | justify-content: center; 367 | align-items: center; 368 | } 369 | 370 | .rl-daterange-toggler .selector-main { 371 | display: flex; 372 | } 373 | 374 | .rl-daterange-toggler .down-arrow { 375 | font-size: 110%; 376 | line-height: 0.9rem; 377 | margin-left: 8px; 378 | } 379 | 380 | .selector-content-wrapper .selector-content { 381 | display: flex; 382 | flex-direction: column; 383 | } 384 | 385 | .selector-content-wrapper { 386 | display: flex; 387 | } 388 | 389 | .cross-icon { 390 | position: absolute; 391 | z-index: 1; 392 | background: var(--cl-secondary); 393 | font-size: 10px; 394 | line-height: 100%; 395 | display: flex; 396 | justify-content: center; 397 | align-items: center; 398 | border-radius: 50%; 399 | color: white; 400 | opacity: 0; 401 | width: 10px; 402 | height: 10px; 403 | top: 50%; 404 | transform: translateY(-50%); 405 | right: 5px; 406 | padding: 0px; 407 | margin: 0px; 408 | font-size: 12px; 409 | padding: 2px; 410 | opacity: 1; 411 | } 412 | 413 | .rl-defined-range-selector { 414 | z-index: 4; 415 | background-color: white; 416 | padding: 4px 6px; 417 | font-weight: 400; 418 | width: fit-content; 419 | display: flex; 420 | } 421 | 422 | .rl-defined-range-selector .rl-defined-range-label-wrapper { 423 | display: flex; 424 | flex-direction: column; 425 | border-right: 1px solid var(--gray20); 426 | margin-right: 10px; 427 | width: 300px; 428 | } 429 | .rl-defined-range-selector 430 | .rl-defined-range-label-wrapper 431 | .rl-defined-range-label { 432 | padding: 12px; 433 | } 434 | .rl-defined-range-selector 435 | .rl-defined-range-label-wrapper 436 | .rl-defined-range-label:hover { 437 | background: var(--cl-primary2); 438 | cursor: pointer; 439 | } 440 | 441 | .rl-defined-range-label-selected { 442 | background: var(--cl-primary2) !important; 443 | } 444 | -------------------------------------------------------------------------------- /src/types/main.d.ts: -------------------------------------------------------------------------------- 1 | export interface DateType { 2 | day?: number | null; 3 | month?: number | null; 4 | year?: number | null; 5 | } 6 | export interface ADBSDateType { 7 | ad: DateType; 8 | bs: DateType; 9 | } 10 | 11 | export type CalendarType = "AD" | "BS"; 12 | 13 | export type ShowDropdownType = boolean | "full" | "short" | "min"; 14 | 15 | export type CalendarDataType = { 16 | month: number; 17 | year: number; 18 | dayValue?: number; 19 | }; 20 | 21 | export type ShowYearDropdownType = boolean | number[]; // TODO allow for only two values in number array; 22 | 23 | export type DateRange = { 24 | from: string | null; 25 | to: string | null; 26 | format?: string; 27 | }; 28 | 29 | export type onSelectProps = ( 30 | formattedDate: string, 31 | adDate: IDateObject, 32 | bsDate: IDateObject, 33 | dateString: Date 34 | ) => void; 35 | 36 | export type onChangeProps = ( 37 | formattedDate: string, 38 | adDate?: IDateObject, 39 | bsDate?: IDateObject, 40 | dateString?: Date 41 | ) => void; 42 | 43 | export interface IDateObject { 44 | year: number; 45 | month: number; 46 | date: number; 47 | } 48 | export interface IDateOffset { 49 | year?: number; 50 | month?: number; 51 | date?: number; 52 | } 53 | 54 | export type DisableProps = { 55 | disableDate?: ( 56 | formattedDate: string, 57 | adDate: DateType, 58 | bsDate: DateType, 59 | dateString: Date 60 | ) => boolean; 61 | disableFuture?: boolean; 62 | disablePast?: boolean; 63 | maxDate?: string; 64 | minDate?: string; 65 | }; 66 | 67 | export type RangeProps = { 68 | range?: DateRange; 69 | }; 70 | 71 | interface IDatePicker extends INepaliCalendar { 72 | size: "small" | "large"; // TODO, 73 | onChange: onChangeProps; 74 | isClearable: boolean; 75 | value: string; 76 | placehoder?: string; 77 | dateFormat: string; 78 | hideOnSelect: boolean; 79 | children?: React.ReactNode; 80 | showDelimiter?: boolean; 81 | showCalenderOnlyWhenIconIsClicked?: boolean; 82 | } 83 | 84 | export interface INepaliCalendar extends DisableProps, RangeProps { 85 | children?: React.ReactNode; 86 | zeroDayName?: string; 87 | defaultValue?: string | null; 88 | dateFormat: string; 89 | value?: string | null; 90 | showMonthDropdown?: ShowDropdownType; 91 | showYearDropdown?: ShowYearDropdownType; 92 | onSelect: onSelectProps; 93 | shouldPressOK?: boolean; 94 | showExtra?: boolean; 95 | withReference?: boolean; 96 | reference_date?: string; 97 | rangeReference?: number[]; 98 | calendarType: CalendarType; 99 | } 100 | 101 | export interface ICalendarRange { 102 | from: string; 103 | to: string; 104 | onChange: (dateFrom: string | null, dateTo: string | null) => void; 105 | dateFormat: string; 106 | calendarType: CalendarType; 107 | } 108 | 109 | export interface IDateFormat { 110 | yyyy?: number; 111 | mm?: number; 112 | m?: number; 113 | dd?: number; 114 | d?: number; 115 | } 116 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | // inclusive of first and last date; 2 | export function isInBetween(data: any, first: any, last: any): boolean { 3 | if (data >= first && data <= last && first < last) { 4 | return true; 5 | } else return false; 6 | } 7 | 8 | export const padDateMonth = (val: string | number) => { 9 | return `${val}`.padStart(2, "0"); 10 | }; 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "module": "esnext", 5 | "lib": ["dom", "esnext"], 6 | "moduleResolution": "node", 7 | "jsx": "react", 8 | "sourceMap": true, 9 | "declaration": true, 10 | "declarationDir": "./dist", 11 | "esModuleInterop": true, 12 | "noImplicitReturns": true, 13 | "noImplicitThis": true, 14 | "noImplicitAny": true, 15 | "importHelpers": true, 16 | "strictNullChecks": true, 17 | "strict": true, 18 | "suppressImplicitAnyIndexErrors": true, 19 | "noUnusedLocals": false, 20 | "forceConsistentCasingInFileNames": true, 21 | "noUnusedParameters": false, 22 | "allowSyntheticDefaultImports": true, 23 | "target": "es5", 24 | "allowJs": true, 25 | "skipLibCheck": true, 26 | "resolveJsonModule": true, 27 | "isolatedModules": true, 28 | "noEmit": true, 29 | "baseUrl": "src" 30 | }, 31 | "include": ["src"], 32 | "exclude": ["node_modules", "dist", "example", "doc"] 33 | } 34 | -------------------------------------------------------------------------------- /why-package.md: -------------------------------------------------------------------------------- 1 | - rollup => For bundling 2 | - @rollup/plugin-node-resolve => resolves any third party modules/dependencies (for our case date-fns) and add to the source code 3 | - rollup-plugin-terser => ugliying and minimizing generated bundl 4 | - tslib => To remove duplication of typescript's helper functions. imported by setting importHelpers flag to true in tsconfig.json 5 | 6 | //TODO babel , husky, lint, prettier, editorconfig 7 | 8 | prettier => for code formatting 9 | 10 | // Babel setup 11 | 12 | 1. @babel/preset-react => handles jsx and other react "things" 13 | 2. @babel/preset-typescript => for typescript of course 14 | 3. 15 | --------------------------------------------------------------------------------