├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .lintstagedrc ├── .npmignore ├── .prettierrc ├── .stylelintrc ├── .travis.yml ├── LICENSE ├── README.md ├── example.png ├── example ├── .gitignore ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── App.test.tsx │ ├── App.tsx │ ├── app.scss │ ├── components │ │ ├── Footer.tsx │ │ └── Header.tsx │ ├── index.tsx │ ├── logo.svg │ ├── react-app-env.d.ts │ └── setupTests.ts ├── tsconfig.json └── yarn.lock ├── package.json ├── src ├── .eslintrc ├── NepaliDatePicker.scss ├── NepaliDatePicker │ ├── Calender │ │ ├── Calender.tsx │ │ ├── components │ │ │ ├── CalenderController.tsx │ │ │ ├── DayPicker │ │ │ │ ├── DayPicker.tsx │ │ │ │ ├── DayPickerBody.tsx │ │ │ │ ├── DayPickerHeader.tsx │ │ │ │ └── index.ts │ │ │ ├── MonthPicker.tsx │ │ │ └── YearPicker.tsx │ │ └── index.ts │ ├── Config │ │ ├── CalenderConfig.ts │ │ ├── CalenderData.ts │ │ ├── ConfigContext.ts │ │ ├── ConfigProvider.tsx │ │ ├── ConfigStoreReducer.ts │ │ ├── ConfigTypes.ts │ │ ├── index.ts │ │ └── useConfig.ts │ ├── DropDown │ │ ├── DropDown.tsx │ │ ├── Types.ts │ │ └── index.ts │ ├── Icons │ │ ├── IconBase.tsx │ │ ├── Next.tsx │ │ ├── Previous.tsx │ │ ├── Today.tsx │ │ └── index.ts │ ├── Locale │ │ ├── index.ts │ │ ├── translations.ts │ │ └── useTrans.ts │ ├── NepaliDatePicker.tsx │ ├── Types.ts │ ├── Utils │ │ ├── DateValidations.ts │ │ └── common.ts │ └── index.tsx ├── index.test.tsx ├── index.tsx └── typings.d.ts ├── tsconfig.json ├── tsconfig.test.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | node_modules/ 4 | .snapshots/ 5 | *.min.js -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": [ 4 | "standard", 5 | "standard-react", 6 | "plugin:react/recommended", 7 | "plugin:prettier/recommended", 8 | "prettier/standard", 9 | "prettier/react", 10 | "plugin:@typescript-eslint/eslint-recommended" 11 | ], 12 | "env": { 13 | "node": true 14 | }, 15 | "parserOptions": { 16 | "ecmaVersion": 2020, 17 | "ecmaFeatures": { 18 | "legacyDecorators": true, 19 | "jsx": true 20 | } 21 | }, 22 | "settings": { 23 | "react": { 24 | "version": "16" 25 | } 26 | }, 27 | "rules": { 28 | "space-before-function-paren": 0, 29 | "react/prop-types": 0, 30 | "react/jsx-handler-names": 0, 31 | "react/jsx-fragments": 0, 32 | "react/no-unused-prop-types": 0, 33 | "import/export": 0, 34 | "react/jsx-uses-react": "error", 35 | "react/jsx-uses-vars": "error" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.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 | /dist 13 | 14 | # misc 15 | .DS_Store 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | 21 | .idea 22 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "src/**/*.{ts,tsx}": [ 3 | "git add" 4 | ], 5 | "src/**/*.scss": [ 6 | "yarn lint:scss", 7 | "git add" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !dist 3 | !package.json 4 | !.npmignore 5 | !README.md 6 | !LICENSE 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": false, 4 | "jsxSingleQuote": true, 5 | "semi": false, 6 | "tabWidth": 4, 7 | "bracketSpacing": true, 8 | "jsxBracketSameLine": false, 9 | "arrowParens": "always", 10 | "trailingComma": "all" 11 | } 12 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "stylelint-order", 4 | "stylelint-scss", 5 | "stylelint-no-unsupported-browser-features", 6 | "stylelint-declaration-block-no-ignored-properties" 7 | ], 8 | "extends": [ 9 | "stylelint-config-standard", 10 | "./node_modules/prettier-stylelint/config.js" 11 | ], 12 | "ignoreFiles": [ 13 | "**/node_modules/**" 14 | ], 15 | "rules": { 16 | "indentation": 4, 17 | "color-no-invalid-hex": true, 18 | "font-family-no-duplicate-names": true, 19 | "font-family-no-missing-generic-family-keyword": true, 20 | "declaration-block-trailing-semicolon": "always", 21 | "string-quotes": "double", 22 | "no-descending-specificity": null, 23 | "plugin/declaration-block-no-ignored-properties": true, 24 | "plugin/no-unsupported-browser-features": [ 25 | true, 26 | { 27 | "browsers": [ 28 | "> 5% in JP", 29 | "Last 2 Safari versions", 30 | "Last 2 iOS versions", 31 | "Last 2 Chrome versions", 32 | "Last 2 ChromeAndroid versions", 33 | "Last 2 Edge versions", 34 | "Last 2 Firefox versions", 35 | "Last 2 FirefoxAndroid versions" 36 | ], 37 | "severity": [ 38 | "warning" 39 | ] 40 | } 41 | ], 42 | "at-rule-no-unknown": [ 43 | true, 44 | { 45 | "ignoreAtRules": [ 46 | "function", 47 | "if", 48 | "for", 49 | "each", 50 | "include", 51 | "mixin", 52 | "content", 53 | "else", 54 | "error" 55 | ] 56 | } 57 | ], 58 | "order/properties-alphabetical-order": null 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 12 4 | - 10 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Puncoz Nepal . 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software, its source code and associated documentation files (the "Software"), 7 | to deal 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 as it is without any modification. 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 | # Nepali Datepicker (Bikram Sambat) - React.js Component 2 | 3 | > Nepali Datepicker (Bikram Sambat) as a ReactJS component 4 | 5 | [![NPM](https://img.shields.io/npm/v/nepali-datepicker-reactjs.svg)](https://www.npmjs.com/package/nepali-datepicker-reactjs) 6 | [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) 7 | [![Build](https://img.shields.io/travis/puncoz-official/nepali-datepicker-reactjs?logo=travis)](https://travis-ci.org/puncoz-official/nepali-datepicker-reactjs) 8 | [![Twitter Follow](https://img.shields.io/twitter/follow/PuncozNepal?label=Follow&style=social)](https://twitter.com/PuncozNepal) 9 | 10 | ## [Demo](https://puncoz-official.github.io/nepali-datepicker-reactjs/) 11 | 12 | ![NepaliDatePicker Demo](example.png) 13 | 14 | ## Install 15 | 16 | ```bash 17 | npm install --save nepali-datepicker-reactjs 18 | 19 | or, 20 | 21 | yarn add nepali-datepicker-reactjs 22 | ``` 23 | 24 | ## Usage 25 | 26 | ```tsx 27 | import React, { useState } from "react" 28 | import { NepaliDatePicker } from "nepali-datepicker-reactjs" 29 | import "nepali-datepicker-reactjs/dist/index.css" 30 | 31 | const App = () => { 32 | const [date, setDate] = useState("") 33 | 34 | return ( 35 |
36 | 37 | setDate(value)} 41 | options={{ calenderLocale: "ne", valueLocale: "en" }} /> 42 | 43 | ) 44 | } 45 | 46 | export default App 47 | ``` 48 | 49 | ## License 50 | 51 | MIT © [https://github.com/puncoz-official](https://github.com/puncoz-official) 52 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puncoz/nepali-datepicker-reactjs/c2f79bfa70c754fd18b5d5e97c623a7ddd333652/example.png -------------------------------------------------------------------------------- /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 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | This example is linked to the nepali-datepicker-reactjs package in the parent directory for development purposes. 2 | 3 | You can run `yarn install` and then `yarn start` to test your package. 4 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nepali-datepicker-reactjs-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": "link:../node_modules/@testing-library/jest-dom", 14 | "@testing-library/react": "link:../node_modules/@testing-library/react", 15 | "@testing-library/user-event": "link:../node_modules/@testing-library/user-event", 16 | "@types/jest": "link:../node_modules/@types/jest", 17 | "@types/node": "link:../node_modules/@types/node", 18 | "@types/react": "link:../node_modules/@types/react", 19 | "@types/react-dom": "link:../node_modules/@types/react-dom", 20 | "bootstrap": "^4.5.0", 21 | "nepali-datepicker-reactjs": "link:..", 22 | "react": "link:../node_modules/react", 23 | "react-dom": "^16.13.1", 24 | "react-scripts": "link:../node_modules/react-scripts", 25 | "typescript": "link:../node_modules/typescript" 26 | }, 27 | "devDependencies": { 28 | "@babel/plugin-syntax-object-rest-spread": "^7.8.3" 29 | }, 30 | "eslintConfig": { 31 | "extends": "react-app" 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puncoz/nepali-datepicker-reactjs/c2f79bfa70c754fd18b5d5e97c623a7ddd333652/example/public/favicon.ico -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 16 | 17 | 18 | 27 | nepali-datepicker-reactjs 28 | 29 | 30 | 31 | 34 | 35 |
36 | 37 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "nepali-datepicker-reactjs", 3 | "name": "nepali-datepicker-reactjs", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /example/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div') 7 | ReactDOM.render(, div) 8 | ReactDOM.unmountComponentAtNode(div) 9 | }) 10 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { NepaliDatePicker } from "nepali-datepicker-reactjs" 2 | import React, { FunctionComponent, useState } from "react" 3 | import "./app.scss" 4 | import Footer from "./components/Footer" 5 | import Header from "./components/Header" 6 | 7 | const App: FunctionComponent = () => { 8 | const [dateEnglish, setDateEnglish] = useState("2077-03-15") 9 | const [dateNepali, setDateNepali] = useState("") 10 | 11 | return ( 12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 |
20 | 21 | setDateEnglish(date)} 24 | options={{ calenderLocale: "en" }} /> 25 |
26 |
27 | 28 | setDateNepali(date)} 31 | options={{ valueLocale: "en" }} /> 32 |
33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 | ) 41 | } 42 | 43 | export default App 44 | -------------------------------------------------------------------------------- /example/src/app.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/scss/bootstrap"; 2 | @import "~nepali-datepicker-reactjs/dist/index.css"; 3 | -------------------------------------------------------------------------------- /example/src/components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | const Footer = () => ( 4 |
5 |

6 | © 2019-{(new Date()).getFullYear()}{" "} 7 | Puncoz Nepal 8 |

9 | 14 |
15 | ) 16 | 17 | export default Footer 18 | -------------------------------------------------------------------------------- /example/src/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from "react" 2 | import logo from "./../logo.svg" 3 | 4 | const Header: FunctionComponent = () => ( 5 |
6 | nepali-date-picker 7 |

Nepali DatePicker

8 |

Nepali Datepicker (Bikram Sambat) as a ReactJS component

9 |
10 | ) 11 | 12 | export default Header 13 | -------------------------------------------------------------------------------- /example/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom" 3 | import App from "./App" 4 | 5 | ReactDOM.render(, document.getElementById("root")) 6 | -------------------------------------------------------------------------------- /example/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 42 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /example/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /example/src/setupTests.ts: -------------------------------------------------------------------------------- 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": [ 6 | "dom", 7 | "esnext" 8 | ], 9 | "moduleResolution": "node", 10 | "jsx": "react", 11 | "sourceMap": true, 12 | "declaration": true, 13 | "esModuleInterop": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noImplicitAny": true, 17 | "strictNullChecks": true, 18 | "suppressImplicitAnyIndexErrors": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "allowSyntheticDefaultImports": true, 22 | "target": "es5", 23 | "allowJs": true, 24 | "skipLibCheck": true, 25 | "strict": true, 26 | "forceConsistentCasingInFileNames": true, 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": true 30 | }, 31 | "include": [ 32 | "src" 33 | ], 34 | "exclude": [ 35 | "node_modules", 36 | "build" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nepali-datepicker-reactjs", 3 | "version": "1.1.4", 4 | "description": "Nepali Datepicker (Bikram Sambat) as a ReactJS component", 5 | "keywords": [ 6 | "nepali date picker", 7 | "nepali", 8 | "datepicker", 9 | "bikram", 10 | "sambat", 11 | "bikramsambat", 12 | "calender" 13 | ], 14 | "author": { 15 | "name": "Puncoz Nepal", 16 | "url": "https://github.com/puncoz" 17 | }, 18 | "license": "MIT", 19 | "repository": "https://github.com/puncoz/nepali-datepicker-reactjs", 20 | "homepage": "https://puncoz-official.github.io/nepali-datepicker-reactjs", 21 | "main": "dist/index.js", 22 | "module": "dist/index.modern.js", 23 | "style": "src/NepaliDatePicker.scss", 24 | "source": "src/index.tsx", 25 | "engines": { 26 | "node": ">=10" 27 | }, 28 | "scripts": { 29 | "clean-modules": "rimraf node_modules", 30 | "clean": "rimraf dist npm-debug.log* yarn-error.log*", 31 | "format": "prettier --write \"src/**/*.ts\" \"src/**/*.tsx\"", 32 | "lint:ts": "eslint -c .eslintrc ./src/**/*.{ts,tsx}", 33 | "lint:scss": "stylelint 'src/**/*.scss' --syntax scss", 34 | "lint": "yarn lint:ts && yarn lint:scss", 35 | "lint:fix": "yarn lint:ts --fix && yarn lint:scss --fix", 36 | "test": "run-s test:unit test:lint test:build", 37 | "test:build": "run-s build", 38 | "test:lint": "eslint .", 39 | "test:unit": "cross-env CI=1 react-scripts test --env=jsdom", 40 | "test:watch": "react-scripts test --env=jsdom", 41 | "start": "microbundle-crl watch --no-compress --format modern,cjs --css-modules false", 42 | "prebuild": "yarn clean && yarn format", 43 | "build": "microbundle-crl --no-compress --format modern,cjs --css-modules false", 44 | "predeploy": "cd example && yarn install && yarn run build", 45 | "deploy": "gh-pages -d example/build" 46 | }, 47 | "husky": { 48 | "hooks": { 49 | "pre-commit": "lint-staged" 50 | } 51 | }, 52 | "peerDependencies": { 53 | "react": "^18.2.0" 54 | }, 55 | "devDependencies": { 56 | "@testing-library/jest-dom": "^4.2.4", 57 | "@testing-library/react": "^9.5.0", 58 | "@testing-library/user-event": "^7.2.1", 59 | "@types/jest": "^25.1.4", 60 | "@types/node": "^12.12.38", 61 | "@types/react": "^16.9.49", 62 | "@types/react-dom": "^16.9.8", 63 | "@typescript-eslint/eslint-plugin": "^2.26.0", 64 | "@typescript-eslint/parser": "^2.26.0", 65 | "babel-eslint": "^10.0.3", 66 | "cross-env": "^7.0.2", 67 | "eslint": "^6.8.0", 68 | "eslint-config-prettier": "^6.7.0", 69 | "eslint-config-standard": "^14.1.0", 70 | "eslint-config-standard-react": "^9.2.0", 71 | "eslint-plugin-import": "^2.18.2", 72 | "eslint-plugin-node": "^11.0.0", 73 | "eslint-plugin-prettier": "^3.1.1", 74 | "eslint-plugin-promise": "^4.2.1", 75 | "eslint-plugin-react": "^7.20.3", 76 | "eslint-plugin-standard": "^4.0.1", 77 | "gh-pages": "^2.2.0", 78 | "husky": "^4.2.5", 79 | "lint-staged": "^10.2.11", 80 | "microbundle-crl": "^0.13.10", 81 | "node-sass": "^6.0.1", 82 | "npm-run-all": "^4.1.5", 83 | "prettier": "^2.0.4", 84 | "prettier-stylelint": "^0.4.2", 85 | "react": "^18.2.0", 86 | "react-scripts": "^5.0.1", 87 | "rimraf": "^3.0.2", 88 | "stylelint": "^13.6.1", 89 | "stylelint-config-standard": "^20.0.0", 90 | "stylelint-declaration-block-no-ignored-properties": "^2.3.0", 91 | "stylelint-no-unsupported-browser-features": "^4.0.0", 92 | "stylelint-order": "^4.1.0", 93 | "stylelint-scss": "^3.18.0", 94 | "typescript": "^3.7.5" 95 | }, 96 | "files": [ 97 | "dist", 98 | "src/NepaliDatePicker.scss" 99 | ], 100 | "dependencies": { 101 | "bikram-sambat-js": "^1.0.1", 102 | "nepali-number": "^1.0.3" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/NepaliDatePicker.scss: -------------------------------------------------------------------------------- 1 | $primary: #2096f5; 2 | $secondary: #fff; 3 | $textColor: #8c8c8c; 4 | $text-disabled: #d8d8d8; 5 | 6 | .nepali-date-picker { 7 | position: relative; 8 | 9 | .calender { 10 | background: $secondary none repeat scroll 0 0; 11 | // border: 1px solid darken($secondary, 20%); 12 | border-radius: 6px; 13 | box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1); 14 | box-sizing: border-box; 15 | color: $textColor; 16 | display: block; 17 | font-family: NotoSans, sans-serif; 18 | font-size: 14px; 19 | font-style: normal; 20 | font-weight: normal; 21 | height: auto; 22 | letter-spacing: 0.2px; 23 | line-height: 1.25em; 24 | padding: 15px; 25 | position: absolute; 26 | text-align: right; 27 | user-select: none; 28 | left: 0; 29 | z-index: 9999; 30 | 31 | .calendar-controller { 32 | align-items: center; 33 | display: flex; 34 | justify-content: space-between; 35 | position: relative; 36 | 37 | .date-indicator { 38 | display: flex; 39 | } 40 | 41 | .control { 42 | cursor: pointer; 43 | position: relative; 44 | text-align: center; 45 | 46 | &.icon-today { 47 | position: absolute; 48 | right: 20px; 49 | } 50 | 51 | &.month, 52 | &.year { 53 | border: 1px solid #eee; 54 | line-height: 24px; 55 | width: 70px; 56 | 57 | .current-month, .current-year { 58 | display: block; 59 | padding: 4px; 60 | } 61 | } 62 | 63 | &.year { 64 | border-left: none; 65 | } 66 | 67 | .drop-down { 68 | background-color: #fff; 69 | box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1); 70 | left: 0; 71 | max-height: 200px; 72 | overflow-y: scroll; 73 | position: absolute; 74 | top: 0; 75 | width: 100%; 76 | z-index: 100; 77 | 78 | ul { 79 | list-style: none; 80 | margin: 0; 81 | padding: 0; 82 | } 83 | 84 | li { 85 | padding: 4px 10px; 86 | 87 | &.active { 88 | background: $primary; 89 | color: #fff; 90 | } 91 | } 92 | } 93 | } 94 | } 95 | 96 | td.month-day { 97 | &.current { 98 | opacity: 1; 99 | } 100 | 101 | &.disabled { 102 | color: $text-disabled; 103 | } 104 | 105 | &.today { 106 | color: #4a4a4a; 107 | position: relative; 108 | 109 | &::before { 110 | background-color: $primary; 111 | border-radius: 50%; 112 | bottom: 6px; 113 | content: ""; 114 | height: 4px; 115 | left: 50%; 116 | margin: auto; 117 | position: absolute; 118 | transform: translateX(-50%); 119 | width: 4px; 120 | } 121 | } 122 | 123 | &.selected { 124 | color: #fff; 125 | position: relative; 126 | 127 | &::after { 128 | background: $primary; 129 | border-radius: 50%; 130 | content: ""; 131 | height: 35px; 132 | left: 50%; 133 | position: absolute; 134 | top: 50%; 135 | transform: translate(-50%, -50%); 136 | width: 35px; 137 | z-index: -1; 138 | } 139 | } 140 | } 141 | 142 | table { 143 | text-align: center; 144 | width: 100%; 145 | border: none; 146 | 147 | td { 148 | height: 40px; 149 | width: 40px; 150 | border: none; 151 | } 152 | 153 | tr { 154 | border: none; 155 | height: 36px; 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Calender/Calender.tsx: -------------------------------------------------------------------------------- 1 | import { ADToBS } from "bikram-sambat-js" 2 | import React, { Fragment, FunctionComponent, useCallback, useEffect, useState } from "react" 3 | import { NepaliDatepickerEvents, ParsedDate, parsedDateInitialValue, SplittedDate } from "../Types" 4 | import { executionDelegation, parseBSDate, stitchDate } from "../Utils/common" 5 | import CalenderController from "./components/CalenderController" 6 | import { DayPicker } from "./components/DayPicker" 7 | import { useConfig } from "../Config" 8 | 9 | interface CalenderProps { 10 | value: string 11 | events: NepaliDatepickerEvents 12 | } 13 | 14 | const Calender: FunctionComponent = ({ value, events }) => { 15 | const [isInitialized, setIsInitialized] = useState(false) 16 | const [selectedDate, setSelectedDate] = useState(parsedDateInitialValue) 17 | const [calenderDate, setCalenderDate] = useState(parsedDateInitialValue) 18 | const { getConfig } = useConfig() 19 | useEffect(() => { 20 | const parsedDateValue = parseBSDate(value) 21 | 22 | setSelectedDate(parsedDateValue) 23 | setCalenderDate(parsedDateValue) 24 | setIsInitialized(true) 25 | }, [value]) 26 | 27 | useEffect(() => { 28 | if (isInitialized) { 29 | events.change( 30 | stitchDate({ 31 | year: selectedDate.bsYear, 32 | month: selectedDate.bsMonth, 33 | day: selectedDate.bsDay, 34 | }), 35 | ) 36 | } 37 | }, [selectedDate, isInitialized]) 38 | 39 | const onPreviousMonthHandler = useCallback(() => { 40 | executionDelegation( 41 | () => { 42 | setCalenderDate((date) => { 43 | let year = date.bsYear 44 | let month = date.bsMonth - 1 45 | 46 | if (month < 1) { 47 | month = 12 48 | year-- 49 | } 50 | if (year < getConfig("minYear") || year > getConfig("maxYear")) { 51 | return date 52 | } 53 | 54 | return parseBSDate( 55 | stitchDate( 56 | { 57 | day: date.bsDay, 58 | month, 59 | year, 60 | }, 61 | "-", 62 | ), 63 | ) 64 | }) 65 | }, 66 | () => { 67 | if (events.previousMonthSelect) { 68 | events.previousMonthSelect({ month: calenderDate.bsMonth, year: calenderDate.bsYear }) 69 | } 70 | }, 71 | ) 72 | }, []) 73 | 74 | const onNextMonthClickHandler = useCallback(() => { 75 | executionDelegation( 76 | () => { 77 | setCalenderDate((date) => { 78 | let year = date.bsYear 79 | let month = date.bsMonth + 1 80 | 81 | if (month > 12) { 82 | month = 1 83 | year++ 84 | } 85 | 86 | return parseBSDate( 87 | stitchDate( 88 | { 89 | day: date.bsDay, 90 | month, 91 | year, 92 | }, 93 | "-", 94 | ), 95 | ) 96 | }) 97 | }, 98 | () => { 99 | if (events.nextMonthSelect) { 100 | events.nextMonthSelect({ year: calenderDate.bsYear, month: calenderDate.bsMonth }) 101 | } 102 | }, 103 | ) 104 | }, []) 105 | 106 | const onTodayClickHandler = useCallback(() => { 107 | const today = parseBSDate(ADToBS(new Date())) 108 | 109 | executionDelegation( 110 | () => { 111 | setCalenderDate(today) 112 | setSelectedDate(today) 113 | }, 114 | () => { 115 | if (events.todaySelect) { 116 | events.todaySelect({ year: today.bsYear, month: today.bsMonth, day: today.bsDay }) 117 | } 118 | }, 119 | ) 120 | }, []) 121 | 122 | const onYearSelectHandler = useCallback( 123 | (year) => { 124 | executionDelegation( 125 | () => { 126 | setCalenderDate( 127 | parseBSDate( 128 | stitchDate({ 129 | year, 130 | month: calenderDate.bsMonth, 131 | day: calenderDate.bsDay, 132 | }), 133 | ), 134 | ) 135 | }, 136 | () => { 137 | if (events.yearSelect) { 138 | events.yearSelect(year) 139 | } 140 | }, 141 | ) 142 | }, 143 | [calenderDate], 144 | ) 145 | 146 | const onMonthSelectHandler = useCallback( 147 | (month) => { 148 | executionDelegation( 149 | () => { 150 | setCalenderDate( 151 | parseBSDate( 152 | stitchDate({ 153 | year: calenderDate.bsYear, 154 | month, 155 | day: calenderDate.bsDay, 156 | }), 157 | ), 158 | ) 159 | }, 160 | () => { 161 | if (events.monthSelect) { 162 | events.monthSelect(month) 163 | } 164 | }, 165 | ) 166 | }, 167 | [calenderDate], 168 | ) 169 | 170 | const onDaySelectHandler = useCallback((date: SplittedDate) => { 171 | executionDelegation( 172 | () => { 173 | const newDate = parseBSDate(stitchDate(date)) 174 | 175 | setCalenderDate(newDate) 176 | setSelectedDate(newDate) 177 | }, 178 | () => { 179 | if (events.daySelect) { 180 | events.daySelect(date) 181 | } 182 | }, 183 | ) 184 | }, []) 185 | 186 | return ( 187 |
188 |
189 | {isInitialized && ( 190 | 191 | 199 | 200 | 205 | 206 | )} 207 |
208 |
209 | ) 210 | } 211 | 212 | export default Calender 213 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Calender/components/CalenderController.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from "react" 2 | import { useConfig } from "../../Config" 3 | import { NextIcon, PreviousIcon, TodayIcon } from "../../Icons" 4 | import { useTrans } from "../../Locale" 5 | import { localeType, ParsedDate } from "../../Types" 6 | import MonthPicker from "./MonthPicker" 7 | import YearPicker from "./YearPicker" 8 | 9 | interface CalenderControllerProps { 10 | onNextMonth: () => void 11 | onPreviousMonth: () => void 12 | onToday: () => void 13 | onYearSelect: (year: number) => void 14 | onMonthSelect: (year: number) => void 15 | calenderDate: ParsedDate 16 | } 17 | 18 | const CalenderController: FunctionComponent = (props) => { 19 | const { onNextMonth, onPreviousMonth, calenderDate, onToday, onYearSelect, onMonthSelect } = props 20 | const { getConfig } = useConfig() 21 | const { trans } = useTrans(getConfig("currentLocale")) 22 | 23 | return ( 24 |
25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | ) 43 | } 44 | 45 | export default CalenderController 46 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Calender/components/DayPicker/DayPicker.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from "react" 2 | import { ParsedDate, SplittedDate } from "../../../Types" 3 | import DayPickerBody from "./DayPickerBody" 4 | import DayPickerHeader from "./DayPickerHeader" 5 | 6 | interface DayPickerProps { 7 | selectedDate: ParsedDate 8 | calenderDate: ParsedDate 9 | onDaySelect: (date: SplittedDate) => void 10 | } 11 | 12 | const DayPicker: FunctionComponent = ({ selectedDate, calenderDate, onDaySelect }) => { 13 | return ( 14 | 15 | 16 | 17 | 18 |
19 | ) 20 | } 21 | 22 | export default DayPicker 23 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Calender/components/DayPicker/DayPickerBody.tsx: -------------------------------------------------------------------------------- 1 | import { ADToBS } from "bikram-sambat-js" 2 | import React, { FunctionComponent, useCallback, useMemo } from "react" 3 | import { CalenderData, useConfig } from "../../../Config" 4 | import { useTrans } from "../../../Locale" 5 | import { localeType, ParsedDate, SplittedDate } from "../../../Types" 6 | import { getNumberOfDaysInBSMonth, range, splitDate } from "../../../Utils/common" 7 | 8 | interface DayPickerBodyProps { 9 | selectedDate: ParsedDate 10 | calenderDate: ParsedDate 11 | onSelect: (date: SplittedDate) => void 12 | } 13 | 14 | interface DayInfo { 15 | day: number 16 | month: number 17 | year: number 18 | isCurrentMonth: boolean 19 | isToday: boolean 20 | isSelected: boolean 21 | } 22 | 23 | const DayPickerBody: FunctionComponent = ({ selectedDate, calenderDate: date, onSelect }) => { 24 | const weeksInMonth = useMemo( 25 | () => Math.ceil((date.firstAdDayInBSMonth.getDay() + date.numberOfDaysInBSMonth) / 7) - 1, 26 | [date], 27 | ) 28 | const previousMonth = useMemo(() => (date.bsMonth - 1 !== 0 ? date.bsMonth - 1 : 12), [date]) 29 | const previousYear = useMemo(() => (previousMonth === 12 ? date.bsYear - 1 : date.bsYear), [previousMonth, date]) 30 | const previousMonthDays = useMemo( 31 | () => 32 | previousYear >= CalenderData.minBSYear 33 | ? getNumberOfDaysInBSMonth({ 34 | month: previousMonth, 35 | year: previousYear, 36 | }) 37 | : 30, 38 | [previousYear], 39 | ) 40 | 41 | const { getConfig } = useConfig() 42 | const { numberTrans } = useTrans(getConfig("currentLocale")) 43 | 44 | const getDayInfo = useCallback( 45 | (weekNum, weekDayNum): DayInfo => { 46 | let day = weekNum * 7 + weekDayNum - date.firstAdDayInBSMonth.getDay() 47 | const month = date.bsMonth 48 | const year = date.bsYear 49 | 50 | let isCurrentMonth = true 51 | 52 | if (day <= 0) { 53 | day = previousMonthDays + day 54 | isCurrentMonth = false 55 | } else if (day > date.numberOfDaysInBSMonth) { 56 | day = day - date.numberOfDaysInBSMonth 57 | isCurrentMonth = false 58 | } 59 | 60 | const today = splitDate(ADToBS(new Date())) 61 | 62 | const isToday = isCurrentMonth 63 | ? today.day === day && today.month === date.bsMonth && today.year === date.bsYear 64 | : false 65 | const isSelected = isCurrentMonth 66 | ? selectedDate.bsDay === day && 67 | selectedDate.bsMonth === date.bsMonth && 68 | selectedDate.bsYear === date.bsYear 69 | : false 70 | 71 | return { day, month, year, isCurrentMonth, isToday, isSelected } 72 | }, 73 | [date, selectedDate, previousMonthDays], 74 | ) 75 | 76 | const onDateSelectHandler = useCallback( 77 | (dayInfo: DayInfo) => { 78 | if (dayInfo.isCurrentMonth) { 79 | onSelect({ year: dayInfo.year, month: dayInfo.month, day: dayInfo.day }) 80 | } 81 | }, 82 | [onSelect], 83 | ) 84 | 85 | return ( 86 | 87 | {range(0, weeksInMonth).map((weekNum) => ( 88 | 89 | {range(1, 7).map((weekDayNum) => { 90 | const dayInfo = getDayInfo(weekNum, weekDayNum) 91 | 92 | return ( 93 | onDateSelectHandler(dayInfo)} 99 | > 100 | {numberTrans(dayInfo.day)} 101 | 102 | ) 103 | })} 104 | 105 | ))} 106 | 107 | ) 108 | } 109 | 110 | export default DayPickerBody 111 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Calender/components/DayPicker/DayPickerHeader.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent, useMemo } from "react" 2 | import { CalenderData, useConfig } from "../../../Config" 3 | 4 | const DayPickerHeader: FunctionComponent = () => { 5 | const { getConfig } = useConfig() 6 | const currentLocale: string = useMemo(() => getConfig("currentLocale"), [getConfig]) 7 | 8 | return ( 9 | 10 | 11 | {CalenderData.weeks[currentLocale].map((weekDay: string, index: number) => ( 12 | {weekDay} 13 | ))} 14 | 15 | 16 | ) 17 | } 18 | 19 | export default DayPickerHeader 20 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Calender/components/DayPicker/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DayPicker } from "./DayPicker" 2 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Calender/components/MonthPicker.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent, useMemo, useState } from "react" 2 | import { CalenderData, useConfig } from "../../Config" 3 | import { DropDown, OptionType } from "../../DropDown" 4 | import { localeType, ParsedDate } from "../../Types" 5 | 6 | interface MonthPickerProps { 7 | date: ParsedDate 8 | onSelect: (year: number) => void 9 | } 10 | 11 | const MonthPicker: FunctionComponent = ({ date, onSelect }) => { 12 | const [showDropdown, setShowDropdown] = useState(false) 13 | 14 | const { getConfig } = useConfig() 15 | const currentLocale: localeType = useMemo(() => getConfig("currentLocale"), [getConfig]) 16 | 17 | const currentMonth: OptionType = useMemo((): OptionType => { 18 | const month = date.bsMonth 19 | 20 | return { 21 | label: CalenderData.months[currentLocale][month - 1], 22 | value: month, 23 | } 24 | }, [date, currentLocale]) 25 | 26 | const monthList: OptionType[] = useMemo(() => { 27 | return CalenderData.months[currentLocale].map((month, index) => ({ 28 | label: month, 29 | value: index + 1, 30 | })) 31 | }, [currentLocale]) 32 | 33 | const handleDropdownView = (selected: OptionType) => { 34 | setShowDropdown(!showDropdown) 35 | onSelect(selected.value) 36 | } 37 | 38 | return ( 39 |
40 | setShowDropdown(!showDropdown)}> 41 | {currentMonth.label} 42 | 43 | {showDropdown && } 44 |
45 | ) 46 | } 47 | 48 | export default MonthPicker 49 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Calender/components/YearPicker.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent, useMemo, useState } from "react" 2 | import { useConfig } from "../../Config" 3 | import { DropDown, OptionType } from "../../DropDown" 4 | import { useTrans } from "../../Locale" 5 | import { localeType, ParsedDate } from "../../Types" 6 | import { range } from "../../Utils/common" 7 | 8 | interface YearPickerProps { 9 | date: ParsedDate 10 | onSelect: (year: number) => void 11 | } 12 | 13 | const YearPicker: FunctionComponent = ({ date, onSelect }) => { 14 | const [showDropdown, setShowDropdown] = useState(false) 15 | const { getConfig } = useConfig() 16 | const { numberTrans } = useTrans(getConfig("currentLocale")) 17 | 18 | const currentYear: OptionType = useMemo((): OptionType => { 19 | const year = date.bsYear 20 | 21 | return { 22 | label: numberTrans(year), 23 | value: year, 24 | } 25 | }, [date]) 26 | 27 | const years: OptionType[] = useMemo( 28 | (): OptionType[] => 29 | range(getConfig("minYear"), getConfig("maxYear")) 30 | .reverse() 31 | .map( 32 | (year: number): OptionType => ({ 33 | label: numberTrans(year), 34 | value: year, 35 | }), 36 | ), 37 | [], 38 | ) 39 | 40 | const handleDropdownView = (selected: OptionType) => { 41 | setShowDropdown(!showDropdown) 42 | onSelect(selected.value) 43 | } 44 | 45 | return ( 46 |
47 | setShowDropdown(!showDropdown)}> 48 | {currentYear.label} 49 | 50 | {showDropdown && } 51 |
52 | ) 53 | } 54 | 55 | export default YearPicker 56 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Calender/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Calender } from "./Calender" 2 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Config/CalenderConfig.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | maxBSYear: 2100, 3 | minBSYear: 1970, 4 | 5 | outputSeparator: "-", 6 | } 7 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Config/CalenderData.ts: -------------------------------------------------------------------------------- 1 | export const months = { 2 | en: [ 3 | "Baisakh", 4 | "Jestha", 5 | "Asar", 6 | "Shrawan", 7 | "Bhadra", 8 | "Asoj", 9 | "Kartik", 10 | "Mangsir", 11 | "Pouse", 12 | "Magh", 13 | "Falgun", 14 | "Chaitra", 15 | ], 16 | ne: ["बैशाख", "जेठ", "असार", "सावन", "भदौ", "असोज", "कार्तिक", "मंसिर", "पौष", "माघ", "फागुन", "चैत"], 17 | } 18 | 19 | export const weeks = { 20 | en: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], 21 | ne: ["आईत", "सोम", "मंगल", "बुध", "बिही", "शुक्र", "शनि"], 22 | } 23 | 24 | export const maxBSYear = 2100 25 | export const minBSYear = 1970 26 | 27 | export const bsMonthMaxDays = [ 28 | [30, 31], 29 | [31, 32], 30 | [31, 32], 31 | [31, 32], 32 | [31, 32], 33 | [30, 31], 34 | [29, 30], 35 | [29, 30], 36 | [29, 30], 37 | [29, 30], 38 | [29, 30], 39 | [30, 31], 40 | ] 41 | 42 | export const bsMonthCalculatedData = [ 43 | [0, 1, 1, 22, 1, 3, 1, 1, 1, 3, 1, 22, 1, 3, 1, 3, 1, 22, 1, 3, 1, 19, 1, 3, 1, 1, 3, 1, 2, 2, 1, 3, 1], 44 | [ 45 | 1, 46 | 2, 47 | 2, 48 | 2, 49 | 2, 50 | 2, 51 | 2, 52 | 1, 53 | 3, 54 | 1, 55 | 3, 56 | 1, 57 | 2, 58 | 2, 59 | 2, 60 | 3, 61 | 2, 62 | 2, 63 | 2, 64 | 1, 65 | 3, 66 | 1, 67 | 3, 68 | 1, 69 | 2, 70 | 2, 71 | 2, 72 | 2, 73 | 2, 74 | 2, 75 | 2, 76 | 2, 77 | 2, 78 | 2, 79 | 2, 80 | 1, 81 | 3, 82 | 1, 83 | 2, 84 | 2, 85 | 2, 86 | 2, 87 | 2, 88 | 2, 89 | 2, 90 | 2, 91 | 2, 92 | 2, 93 | 2, 94 | 1, 95 | 3, 96 | 1, 97 | 2, 98 | 2, 99 | 2, 100 | 2, 101 | 2, 102 | 1, 103 | 1, 104 | 1, 105 | 2, 106 | 2, 107 | 2, 108 | 2, 109 | 2, 110 | 1, 111 | 3, 112 | 1, 113 | 1, 114 | 2, 115 | ], 116 | [ 117 | 0, 118 | 1, 119 | 2, 120 | 1, 121 | 3, 122 | 1, 123 | 3, 124 | 1, 125 | 2, 126 | 2, 127 | 2, 128 | 2, 129 | 2, 130 | 2, 131 | 2, 132 | 2, 133 | 3, 134 | 2, 135 | 2, 136 | 2, 137 | 2, 138 | 2, 139 | 2, 140 | 2, 141 | 2, 142 | 1, 143 | 3, 144 | 1, 145 | 3, 146 | 1, 147 | 2, 148 | 2, 149 | 2, 150 | 2, 151 | 2, 152 | 2, 153 | 2, 154 | 2, 155 | 2, 156 | 1, 157 | 3, 158 | 1, 159 | 3, 160 | 1, 161 | 2, 162 | 2, 163 | 2, 164 | 2, 165 | 2, 166 | 2, 167 | 2, 168 | 2, 169 | 2, 170 | 1, 171 | 3, 172 | 1, 173 | 3, 174 | 1, 175 | 1, 176 | 1, 177 | 1, 178 | 2, 179 | 2, 180 | 2, 181 | 2, 182 | 2, 183 | 1, 184 | 3, 185 | 1, 186 | 1, 187 | 2, 188 | ], 189 | [ 190 | 1, 191 | 2, 192 | 1, 193 | 3, 194 | 1, 195 | 3, 196 | 1, 197 | 3, 198 | 1, 199 | 3, 200 | 1, 201 | 3, 202 | 1, 203 | 3, 204 | 1, 205 | 3, 206 | 1, 207 | 3, 208 | 1, 209 | 3, 210 | 1, 211 | 3, 212 | 1, 213 | 3, 214 | 1, 215 | 3, 216 | 1, 217 | 3, 218 | 1, 219 | 2, 220 | 2, 221 | 2, 222 | 1, 223 | 3, 224 | 1, 225 | 3, 226 | 1, 227 | 3, 228 | 1, 229 | 3, 230 | 1, 231 | 3, 232 | 1, 233 | 2, 234 | 2, 235 | 2, 236 | 1, 237 | 3, 238 | 1, 239 | 3, 240 | 1, 241 | 3, 242 | 1, 243 | 3, 244 | 1, 245 | 3, 246 | 1, 247 | 3, 248 | 2, 249 | 2, 250 | 1, 251 | 3, 252 | 1, 253 | 2, 254 | 2, 255 | 2, 256 | 1, 257 | 2, 258 | ], 259 | [59, 1, 26, 1, 28, 1, 2, 1, 12], 260 | [ 261 | 0, 262 | 1, 263 | 2, 264 | 2, 265 | 2, 266 | 2, 267 | 2, 268 | 2, 269 | 2, 270 | 2, 271 | 2, 272 | 2, 273 | 2, 274 | 1, 275 | 3, 276 | 1, 277 | 3, 278 | 1, 279 | 3, 280 | 1, 281 | 2, 282 | 2, 283 | 2, 284 | 2, 285 | 2, 286 | 2, 287 | 2, 288 | 2, 289 | 2, 290 | 2, 291 | 2, 292 | 1, 293 | 3, 294 | 1, 295 | 2, 296 | 2, 297 | 2, 298 | 2, 299 | 2, 300 | 2, 301 | 2, 302 | 2, 303 | 2, 304 | 2, 305 | 2, 306 | 1, 307 | 3, 308 | 1, 309 | 2, 310 | 2, 311 | 2, 312 | 2, 313 | 2, 314 | 2, 315 | 2, 316 | 2, 317 | 2, 318 | 2, 319 | 5, 320 | 1, 321 | 1, 322 | 2, 323 | 2, 324 | 1, 325 | 3, 326 | 1, 327 | 2, 328 | 1, 329 | 2, 330 | ], 331 | [0, 12, 1, 3, 1, 3, 1, 5, 1, 11, 1, 3, 1, 3, 1, 18, 1, 3, 1, 3, 1, 18, 1, 3, 1, 3, 1, 27, 1, 2], 332 | [ 333 | 1, 334 | 2, 335 | 2, 336 | 2, 337 | 2, 338 | 1, 339 | 2, 340 | 2, 341 | 2, 342 | 2, 343 | 2, 344 | 2, 345 | 2, 346 | 3, 347 | 1, 348 | 3, 349 | 2, 350 | 2, 351 | 2, 352 | 2, 353 | 2, 354 | 2, 355 | 2, 356 | 2, 357 | 2, 358 | 1, 359 | 2, 360 | 2, 361 | 2, 362 | 2, 363 | 2, 364 | 2, 365 | 2, 366 | 2, 367 | 2, 368 | 2, 369 | 2, 370 | 2, 371 | 2, 372 | 1, 373 | 2, 374 | 2, 375 | 2, 376 | 2, 377 | 2, 378 | 2, 379 | 2, 380 | 2, 381 | 2, 382 | 2, 383 | 2, 384 | 2, 385 | 2, 386 | 1, 387 | 2, 388 | 2, 389 | 2, 390 | 15, 391 | 2, 392 | 4, 393 | ], 394 | [ 395 | 0, 396 | 1, 397 | 2, 398 | 2, 399 | 2, 400 | 2, 401 | 1, 402 | 3, 403 | 1, 404 | 3, 405 | 1, 406 | 3, 407 | 1, 408 | 2, 409 | 2, 410 | 2, 411 | 3, 412 | 2, 413 | 2, 414 | 2, 415 | 1, 416 | 3, 417 | 1, 418 | 3, 419 | 1, 420 | 3, 421 | 1, 422 | 2, 423 | 2, 424 | 2, 425 | 2, 426 | 2, 427 | 2, 428 | 2, 429 | 1, 430 | 3, 431 | 1, 432 | 3, 433 | 1, 434 | 3, 435 | 1, 436 | 2, 437 | 2, 438 | 2, 439 | 2, 440 | 2, 441 | 2, 442 | 2, 443 | 2, 444 | 2, 445 | 1, 446 | 3, 447 | 1, 448 | 3, 449 | 1, 450 | 2, 451 | 2, 452 | 2, 453 | 15, 454 | 2, 455 | 4, 456 | ], 457 | [ 458 | 1, 459 | 1, 460 | 3, 461 | 1, 462 | 3, 463 | 1, 464 | 14, 465 | 1, 466 | 3, 467 | 1, 468 | 1, 469 | 1, 470 | 3, 471 | 1, 472 | 14, 473 | 1, 474 | 3, 475 | 1, 476 | 3, 477 | 1, 478 | 3, 479 | 1, 480 | 18, 481 | 1, 482 | 3, 483 | 1, 484 | 3, 485 | 1, 486 | 3, 487 | 1, 488 | 14, 489 | 1, 490 | 3, 491 | 15, 492 | 1, 493 | 2, 494 | 1, 495 | 1, 496 | ], 497 | [ 498 | 0, 499 | 1, 500 | 1, 501 | 3, 502 | 1, 503 | 3, 504 | 1, 505 | 10, 506 | 1, 507 | 3, 508 | 1, 509 | 3, 510 | 1, 511 | 1, 512 | 1, 513 | 3, 514 | 1, 515 | 3, 516 | 1, 517 | 10, 518 | 1, 519 | 3, 520 | 1, 521 | 3, 522 | 1, 523 | 3, 524 | 1, 525 | 3, 526 | 1, 527 | 14, 528 | 1, 529 | 3, 530 | 1, 531 | 3, 532 | 1, 533 | 3, 534 | 1, 535 | 3, 536 | 1, 537 | 10, 538 | 1, 539 | 20, 540 | 1, 541 | 1, 542 | 1, 543 | ], 544 | [ 545 | 1, 546 | 2, 547 | 2, 548 | 1, 549 | 3, 550 | 1, 551 | 3, 552 | 1, 553 | 3, 554 | 1, 555 | 2, 556 | 2, 557 | 2, 558 | 2, 559 | 2, 560 | 3, 561 | 2, 562 | 2, 563 | 2, 564 | 2, 565 | 2, 566 | 1, 567 | 3, 568 | 1, 569 | 3, 570 | 1, 571 | 3, 572 | 1, 573 | 2, 574 | 2, 575 | 2, 576 | 2, 577 | 2, 578 | 2, 579 | 2, 580 | 1, 581 | 3, 582 | 1, 583 | 3, 584 | 1, 585 | 3, 586 | 1, 587 | 3, 588 | 1, 589 | 2, 590 | 2, 591 | 2, 592 | 2, 593 | 2, 594 | 2, 595 | 2, 596 | 1, 597 | 3, 598 | 1, 599 | 3, 600 | 1, 601 | 20, 602 | 3, 603 | ], 604 | ] 605 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Config/ConfigContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from "react" 2 | import { initialState } from "./ConfigStoreReducer" 3 | import { ConfigAction, ConfigState } from "./ConfigTypes" 4 | 5 | const ConfigContext = createContext<{ 6 | dispatch: (action: ConfigAction) => void 7 | state: ConfigState 8 | }>({ 9 | dispatch: () => null, 10 | state: initialState(), 11 | }) 12 | 13 | export default ConfigContext 14 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Config/ConfigProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent, useReducer } from "react" 2 | import ConfigContext from "./ConfigContext" 3 | import ConfigStoreReducer, { initialState } from "./ConfigStoreReducer" 4 | import { IContextProviderProps } from "../Types" 5 | 6 | const ConfigProvider: FunctionComponent = ({ children, minYear, maxYear }) => { 7 | const [state, dispatch] = useReducer(ConfigStoreReducer, initialState(minYear, maxYear)) 8 | const contextValue = { state, dispatch } 9 | 10 | return {children} 11 | } 12 | 13 | export default ConfigProvider 14 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Config/ConfigStoreReducer.ts: -------------------------------------------------------------------------------- 1 | import { NEPALI } from "../Types" 2 | import { ConfigAction, ConfigState, SET_CONFIG } from "./ConfigTypes" 3 | 4 | export const initialState = (minYear?: number, maxYear?: number): ConfigState => { 5 | return { 6 | currentLocale: NEPALI, 7 | minYear: minYear ?? 2000, 8 | maxYear: maxYear ?? 2100, 9 | } 10 | } 11 | 12 | const ConfigReducer = (state: ConfigState = initialState(), action: ConfigAction): ConfigState => { 13 | if (action.type === SET_CONFIG) { 14 | return { ...state, [action.key]: action.value } 15 | } 16 | 17 | return state 18 | } 19 | 20 | export default ConfigReducer 21 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Config/ConfigTypes.ts: -------------------------------------------------------------------------------- 1 | import { localeType } from "../Types" 2 | 3 | export const SET_CONFIG: string = "set_config" 4 | 5 | export interface ConfigState { 6 | currentLocale: localeType 7 | minYear: number 8 | maxYear: number 9 | } 10 | 11 | export type ConfigValue = localeType 12 | 13 | export interface ConfigAction { 14 | type: typeof SET_CONFIG 15 | key: string 16 | value: ConfigValue 17 | } 18 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Config/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConfigProvider } from "./ConfigProvider" 2 | export { default as useConfig } from "./useConfig" 3 | export { default as CalenderConfig } from "./CalenderConfig" 4 | export * as CalenderData from "./CalenderData" 5 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Config/useConfig.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react" 2 | import ConfigContext from "./ConfigContext" 3 | import { ConfigState, ConfigValue, SET_CONFIG } from "./ConfigTypes" 4 | 5 | const useConfig = () => { 6 | const { state, dispatch } = useContext(ConfigContext) 7 | 8 | function setConfig(key: keyof ConfigState, value: ConfigValue) { 9 | dispatch({ 10 | key, 11 | type: SET_CONFIG, 12 | value, 13 | }) 14 | } 15 | 16 | function getConfig(key: keyof ConfigState): T { 17 | return state[key] as any 18 | } 19 | return { setConfig, getConfig } 20 | } 21 | 22 | export default useConfig 23 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/DropDown/DropDown.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent, useLayoutEffect } from "react" 2 | import { OptionType } from "./Types" 3 | 4 | interface DropDownProps { 5 | options: OptionType[] 6 | value: number 7 | onSelect: (selected: OptionType) => void 8 | } 9 | 10 | const DropDown: FunctionComponent = ({ options, value, onSelect }) => { 11 | useLayoutEffect(() => { 12 | // const elem = document.querySelector(".active") 13 | // if (elem) { 14 | // elem.scrollIntoView() 15 | // } 16 | }) 17 | return ( 18 |
19 |
20 |
    21 | {options.map((option, index) => ( 22 |
  • { 26 | onSelect(option) 27 | }} 28 | > 29 | {option.label} 30 |
  • 31 | ))} 32 |
33 |
34 |
35 | ) 36 | } 37 | 38 | export default DropDown 39 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/DropDown/Types.ts: -------------------------------------------------------------------------------- 1 | export interface OptionType { 2 | label: string 3 | value: number 4 | } 5 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/DropDown/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DropDown } from "./DropDown" 2 | export * from "./Types" 3 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Icons/IconBase.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from "react" 2 | 3 | export interface IconBaseProps { 4 | size?: string 5 | viewBoxSize?: string 6 | className?: string 7 | color?: string 8 | } 9 | 10 | const IconBase: FunctionComponent = (props) => { 11 | const { size, viewBoxSize, ...options } = props 12 | 13 | return ( 14 | 22 | {props.children} 23 | 24 | ) 25 | } 26 | 27 | IconBase.defaultProps = { 28 | color: "#6b6b6b", 29 | size: "16", 30 | viewBoxSize: "24", 31 | } 32 | 33 | export default IconBase 34 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Icons/Next.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from "react" 2 | import IconBase, { IconBaseProps } from "./IconBase" 3 | 4 | const Next: FunctionComponent = (props) => { 5 | const rotationOrigin: number = parseInt(props.size || "24", 10) / 2 6 | 7 | return ( 8 | 9 | 19 | 20 | ) 21 | } 22 | 23 | export default Next 24 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Icons/Previous.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from "react" 2 | import IconBase, { IconBaseProps } from "./IconBase" 3 | 4 | const Previous: FunctionComponent = (props) => ( 5 | 6 | 15 | 16 | ) 17 | 18 | export default Previous 19 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Icons/Today.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from "react" 2 | import IconBase, { IconBaseProps } from "./IconBase" 3 | 4 | const Today: FunctionComponent = (props) => { 5 | props = { ...props, viewBoxSize: "512", size: "15" } 6 | 7 | return ( 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ) 27 | } 28 | 29 | export default Today 30 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Icons/index.ts: -------------------------------------------------------------------------------- 1 | export { default as NextIcon } from "./Next" 2 | export { default as PreviousIcon } from "./Previous" 3 | export { default as TodayIcon } from "./Today" 4 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Locale/index.ts: -------------------------------------------------------------------------------- 1 | export { default as useTrans } from "./useTrans" 2 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Locale/translations.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | previous: { en: "Previous", ne: "अघिल्लो" }, 3 | next: { en: "Next", ne: "अर्को" }, 4 | today: { en: "Today", ne: "आज" }, 5 | } 6 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Locale/useTrans.ts: -------------------------------------------------------------------------------- 1 | import { englishToNepaliNumber, nepaliToEnglishNumber } from "nepali-number" 2 | import { ENGLISH, localeType } from "../Types" 3 | import translations from "./translations" 4 | 5 | const useTrans = (currentLocale: localeType) => { 6 | return { 7 | trans: (key: string, locale?: localeType) => { 8 | if (!translations.hasOwnProperty(key)) { 9 | return key 10 | } 11 | 12 | return translations[key][locale || currentLocale] 13 | }, 14 | 15 | numberTrans: (num: number | string, locale?: localeType) => { 16 | return `${locale || currentLocale}` === ENGLISH 17 | ? nepaliToEnglishNumber(num as string) 18 | : englishToNepaliNumber(num) 19 | }, 20 | } 21 | } 22 | 23 | export default useTrans 24 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/NepaliDatePicker.tsx: -------------------------------------------------------------------------------- 1 | import { ADToBS } from "bikram-sambat-js" 2 | import React, { FunctionComponent, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react" 3 | import { Calender } from "./Calender" 4 | import { useConfig } from "./Config" 5 | import { useTrans } from "./Locale" 6 | import { ENGLISH, INepaliDatePicker, localeType, NepaliDatepickerEvents } from "./Types" 7 | import { childOf, executionDelegation, stitchDate } from "./Utils/common" 8 | 9 | const NepaliDatePicker: FunctionComponent = (props) => { 10 | const { className, inputClassName, value, onChange, onSelect, options } = props 11 | 12 | const nepaliDatePickerWrapper = useRef(null) 13 | const nepaliDatePickerInput = useRef(null) 14 | 15 | const [date, setDate] = useState("") 16 | const [showCalendar, setShowCalendar] = useState(false) 17 | 18 | const { setConfig, getConfig } = useConfig() 19 | const { numberTrans } = useTrans(getConfig("currentLocale")) 20 | 21 | const toEnglish = useCallback((val: string): string => numberTrans(val, ENGLISH), []) 22 | const returnDateValue = useCallback((val: string): string => numberTrans(val, options.valueLocale), [ 23 | options.valueLocale, 24 | ]) 25 | 26 | useEffect(() => { 27 | setConfig("currentLocale", options.calenderLocale) 28 | }, [options.calenderLocale]) 29 | 30 | useEffect(() => { 31 | setDate(toEnglish(value || ADToBS(new Date()))) 32 | }, [value]) 33 | 34 | const handleClickOutside = useCallback((event: any) => { 35 | if (nepaliDatePickerWrapper.current && childOf(event.target, nepaliDatePickerWrapper.current)) { 36 | return 37 | } 38 | 39 | setShowCalendar(false) 40 | }, []) 41 | 42 | useLayoutEffect(() => { 43 | if (showCalendar) { 44 | document.addEventListener("mousedown", handleClickOutside) 45 | } 46 | 47 | return () => { 48 | document.removeEventListener("mousedown", handleClickOutside) 49 | } 50 | }, [showCalendar]) 51 | 52 | useLayoutEffect(() => { 53 | if (showCalendar && nepaliDatePickerWrapper.current) { 54 | const nepaliDatePicker = nepaliDatePickerWrapper.current.getBoundingClientRect() 55 | const screenHeight = window.innerHeight 56 | 57 | const calender: HTMLDivElement | null = nepaliDatePickerWrapper.current.querySelector(".calender") 58 | if (calender) { 59 | setTimeout(() => { 60 | const calenderHeight = calender.clientHeight 61 | 62 | if (calenderHeight + nepaliDatePicker.bottom > screenHeight) { 63 | if (calenderHeight < nepaliDatePicker.top) { 64 | calender.style.bottom = `${nepaliDatePicker.height}px` 65 | } 66 | } 67 | }, 0) 68 | } 69 | } 70 | }, [showCalendar]) 71 | 72 | const handleOnChange = useCallback( 73 | (changedDate: string) => { 74 | executionDelegation( 75 | () => { 76 | setDate(changedDate) 77 | }, 78 | () => { 79 | if (onChange) { 80 | onChange(returnDateValue(changedDate)) 81 | } 82 | }, 83 | ) 84 | }, 85 | [onChange], 86 | ) 87 | 88 | const handleOnDaySelect = useCallback( 89 | (selectedDate) => { 90 | executionDelegation( 91 | () => { 92 | if (options.closeOnSelect) { 93 | setShowCalendar(false) 94 | } 95 | }, 96 | () => { 97 | if (onSelect) { 98 | onSelect(returnDateValue(stitchDate(selectedDate))) 99 | } 100 | }, 101 | ) 102 | }, 103 | [onSelect], 104 | ) 105 | 106 | const datepickerEvents: NepaliDatepickerEvents = { 107 | change: handleOnChange, 108 | daySelect: handleOnDaySelect, 109 | todaySelect: handleOnDaySelect, 110 | } 111 | 112 | return ( 113 |
114 | setShowCalendar((visible) => !visible)} 121 | /> 122 | {showCalendar && date && } 123 |
124 | ) 125 | } 126 | 127 | export default NepaliDatePicker 128 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Types.ts: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes } from "react" 2 | 3 | export type voidFunction = () => void 4 | 5 | export const ENGLISH = "en" 6 | export const NEPALI = "ne" 7 | export const BS = "BS" 8 | export const AD = "AD" 9 | 10 | export type localeType = "en" | "ne" 11 | 12 | export interface NepaliDatePickerOptions { 13 | closeOnSelect: boolean 14 | calenderLocale: localeType 15 | valueLocale: localeType 16 | } 17 | 18 | export interface IContextProviderProps { 19 | minYear?: number 20 | children: React.ReactNode 21 | maxYear?: number 22 | } 23 | export interface INepaliDatePicker { 24 | value: string 25 | className: HTMLAttributes["className"] 26 | inputClassName: HTMLAttributes["className"] 27 | onChange: (date: string) => void 28 | onSelect?: (value: string) => void 29 | options: NepaliDatePickerOptions 30 | minYear?: number 31 | maxYear?: number 32 | } 33 | 34 | export interface NepaliDatePickerProps { 35 | value?: INepaliDatePicker["value"] 36 | className?: INepaliDatePicker["className"] 37 | inputClassName?: INepaliDatePicker["inputClassName"] 38 | onChange?: INepaliDatePicker["onChange"] 39 | onSelect?: INepaliDatePicker["onSelect"] 40 | options?: { 41 | closeOnSelect?: NepaliDatePickerOptions["closeOnSelect"] 42 | calenderLocale?: NepaliDatePickerOptions["calenderLocale"] 43 | valueLocale?: NepaliDatePickerOptions["valueLocale"] 44 | } 45 | minYear?: INepaliDatePicker["minYear"] 46 | maxYear?: INepaliDatePicker["maxYear"] 47 | } 48 | 49 | export interface NepaliDatepickerEvents { 50 | change: (value: string) => void 51 | yearSelect?: (year: number) => void 52 | monthSelect?: ({ year, month }: YearMonth) => void 53 | daySelect?: ({ year, month, day }: YearMonthDate) => void 54 | previousMonthSelect?: ({ month, year }: YearMonth) => void 55 | nextMonthSelect?: ({ year, month }: YearMonth) => void 56 | todaySelect?: ({ year, month, day }: YearMonthDate) => void 57 | } 58 | 59 | export interface ParsedDate { 60 | bsYear: number 61 | bsMonth: number 62 | bsDay: number 63 | weekDay: number 64 | adDate: Date 65 | numberOfDaysInBSMonth: number 66 | firstAdDayInBSMonth: Date 67 | } 68 | 69 | export const parsedDateInitialValue: ParsedDate = { 70 | adDate: new Date(), 71 | bsDay: 0, 72 | bsMonth: 0, 73 | bsYear: 0, 74 | firstAdDayInBSMonth: new Date(), 75 | numberOfDaysInBSMonth: 0, 76 | weekDay: 0, 77 | } 78 | 79 | export interface SplittedDate { 80 | year: number 81 | month: number 82 | day: number 83 | } 84 | 85 | export type YearMonthDate = SplittedDate 86 | 87 | export interface YearMonth { 88 | year: number 89 | month: number 90 | } 91 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Utils/DateValidations.ts: -------------------------------------------------------------------------------- 1 | import { CalenderConfig } from "../Config" 2 | 3 | export const validateAdYear = (year: number) => { 4 | const minAdYear = CalenderConfig.minBSYear - 57 5 | const maxAdYear = CalenderConfig.maxBSYear - 57 6 | 7 | if (year < minAdYear || year > maxAdYear) { 8 | throw new RangeError(`AD year should be in range of ${minAdYear} to ${maxAdYear}`) 9 | } 10 | } 11 | 12 | export const validateAdMonth = (month: number) => { 13 | if (month < 1 || month > 12) { 14 | throw new RangeError("AD month should be in range of 1 to 12") 15 | } 16 | } 17 | 18 | export const validateAdDay = (day: number) => { 19 | if (day < 1 || day > 31) { 20 | throw new RangeError("AD day should be in range of 1 to 31") 21 | } 22 | } 23 | 24 | export const validateBsYear = (year: number) => { 25 | const midBsYear = CalenderConfig.minBSYear 26 | const maxBsYear = CalenderConfig.maxBSYear 27 | 28 | if (year < midBsYear || year > maxBsYear) { 29 | throw new RangeError(`BS year should be in range of ${midBsYear} to ${maxBsYear}`) 30 | } 31 | } 32 | 33 | export const validateBsMonth = (month: number) => { 34 | if (month < 1 || month > 12) { 35 | throw new RangeError("BS month should be in range of 1 to 12") 36 | } 37 | } 38 | 39 | export const validateBsDay = (day: number) => { 40 | if (day < 1 || day > 32) { 41 | throw new RangeError("BS day should be in range of 1 to 32") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/Utils/common.ts: -------------------------------------------------------------------------------- 1 | import { BSToAD } from "bikram-sambat-js" 2 | import { CalenderData } from "../Config" 3 | import { BS, ParsedDate, SplittedDate, voidFunction } from "../Types" 4 | import { 5 | validateAdDay, 6 | validateAdMonth, 7 | validateAdYear, 8 | validateBsDay, 9 | validateBsMonth, 10 | validateBsYear, 11 | } from "./DateValidations" 12 | 13 | export const range = (start: number, end: number, step: number = 1): number[] => { 14 | const list = [] 15 | 16 | for (let i = start; i <= end; i = i + step) { 17 | list.push(i) 18 | } 19 | 20 | return list 21 | } 22 | 23 | export const zeroPad = (num: number): string => `${num > 9 ? num : "0" + num}` 24 | 25 | export const executionDelegation = (execution: voidFunction, delegatedExecution: voidFunction) => { 26 | new Promise((resolve) => { 27 | execution() 28 | resolve() 29 | }).then(() => { 30 | delegatedExecution() 31 | }) 32 | } 33 | 34 | export const splitDate = (date: string, separator: string = "-"): SplittedDate => { 35 | const [year, month, day] = date.split(separator) 36 | 37 | return { 38 | day: parseInt(day, 10), 39 | month: parseInt(month, 10), 40 | year: parseInt(year, 10), 41 | } 42 | } 43 | 44 | export const stitchDate = (date: SplittedDate, separator: string = "-"): string => { 45 | return `${date.year}${separator}${zeroPad(date.month)}${separator}${zeroPad(date.day)}` 46 | } 47 | 48 | export const validateDateObject = (date: SplittedDate, type: string = BS) => { 49 | const { year, month, day } = date 50 | 51 | if (type === BS) { 52 | validateBsYear(year) 53 | validateBsMonth(month) 54 | validateBsDay(day) 55 | 56 | return 57 | } 58 | 59 | validateAdYear(year) 60 | validateAdMonth(month) 61 | validateAdDay(day) 62 | } 63 | 64 | export const getNumberOfDaysInBSMonth = (yearMonth: { year: number; month: number }): number => { 65 | const { year, month } = yearMonth 66 | validateBsYear(year) 67 | validateBsMonth(month) 68 | 69 | let yearCount = 0 70 | const totalYears = year + 1 - CalenderData.minBSYear 71 | const bsMonthData: number[] = CalenderData.bsMonthCalculatedData[month - 1] 72 | 73 | return bsMonthData.reduce((numberOfDays: number, monthData: number, index: number) => { 74 | if (monthData === 0 || numberOfDays !== 0) { 75 | return numberOfDays 76 | } 77 | 78 | const bsMonthUpperDaysIndex = index % 2 79 | yearCount += monthData 80 | if (totalYears > yearCount) { 81 | return numberOfDays 82 | } 83 | 84 | if ((year === 2085 && month === 5) || (year === 2088 && month === 5)) { 85 | return CalenderData.bsMonthMaxDays[month - 1][bsMonthUpperDaysIndex] - 2 86 | } 87 | 88 | return CalenderData.bsMonthMaxDays[month - 1][bsMonthUpperDaysIndex] 89 | }, 0) 90 | } 91 | 92 | export const parseBSDate = (date: string, separator: string = "-"): ParsedDate => { 93 | const { year, month, day }: SplittedDate = splitDate(date, separator) 94 | 95 | validateDateObject({ year, month, day }) 96 | 97 | const adDate = new Date(BSToAD(date)) 98 | const firstAdDateInBSMonth = new Date(BSToAD(stitchDate({ year, month, day: 1 }, separator))) 99 | const numberOfDaysInMonth = getNumberOfDaysInBSMonth({ year, month }) 100 | 101 | return { 102 | adDate, 103 | bsDay: day, 104 | bsMonth: month, 105 | bsYear: year, 106 | firstAdDayInBSMonth: firstAdDateInBSMonth, 107 | numberOfDaysInBSMonth: numberOfDaysInMonth, 108 | weekDay: adDate.getDay(), 109 | } 110 | } 111 | 112 | export const childOf = (childNode: any, parentNode: any): boolean => parentNode.contains(childNode) 113 | -------------------------------------------------------------------------------- /src/NepaliDatePicker/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent, useMemo } from "react" 2 | import "../NepaliDatePicker.scss" 3 | import { ConfigProvider } from "./Config" 4 | import NepaliDatePicker from "./NepaliDatePicker" 5 | import { ENGLISH, INepaliDatePicker, NEPALI, NepaliDatePickerProps } from "./Types" 6 | 7 | const NepaliDatePickerWrapper: FunctionComponent = (props) => { 8 | const calenderOptions = useMemo( 9 | () => ({ 10 | closeOnSelect: true, 11 | calenderLocale: NEPALI, 12 | valueLocale: ENGLISH, 13 | ...props.options, 14 | }), 15 | [props.options], 16 | ) 17 | 18 | return ( 19 | 20 | 21 | 22 | ) 23 | } 24 | 25 | NepaliDatePickerWrapper.defaultProps = { 26 | className: "", 27 | inputClassName: "", 28 | value: "", 29 | onChange: () => null, 30 | onSelect: () => null, 31 | options: {}, 32 | } 33 | 34 | export default NepaliDatePickerWrapper 35 | -------------------------------------------------------------------------------- /src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import { NepaliDatePicker } from "." 2 | 3 | describe("NepaliDatePicker", () => { 4 | it("is truthy", () => { 5 | expect(NepaliDatePicker).toBeTruthy() 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as NepaliDatePicker } from "./NepaliDatePicker" 2 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Default CSS definition for typescript, 3 | * will be overridden with file-specific definitions by rollup 4 | */ 5 | declare module "*.css" { 6 | const content: { [className: string]: string } 7 | export default content 8 | } 9 | 10 | interface SvgrComponent extends React.StatelessComponent> {} 11 | 12 | declare module "*.svg" { 13 | const svgUrl: string 14 | const svgComponent: SvgrComponent 15 | export default svgUrl 16 | export { svgComponent as ReactComponent } 17 | } 18 | -------------------------------------------------------------------------------- /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 | }, 20 | "include": ["src"], 21 | "exclude": ["node_modules", "dist", "example"] 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } --------------------------------------------------------------------------------