├── .all-contributorsrc ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── stale.yml └── workflows │ ├── deploy_surge.yml │ ├── publish_npm.yml │ └── release.yml ├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── angular.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── projects └── angular-datetimerangepicker │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── calendar │ │ ├── calendar-component.html │ │ └── calendar-component.ts │ ├── daterangepicker │ │ ├── daterangepicker.component.html │ │ └── daterangepicker.component.ts │ ├── datetimerangepicker.module.ts │ ├── defaults.ts │ ├── format-date-pipe.ts │ ├── helper.ts │ ├── img │ │ ├── chevron-left.ts │ │ └── double-chevron-left.ts │ ├── public_api.ts │ ├── styles │ │ ├── styles.css │ │ └── touch.css │ ├── time │ │ ├── time-component.html │ │ └── time-component.ts │ └── types.ts │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ └── tslint.json ├── src ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ └── app.module.ts ├── assets │ ├── .gitkeep │ ├── 180x180.png │ ├── 192x192.png │ └── brand.png ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.css ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.json /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "contributorsPerLine": 7, 7 | "contributorsSortAlphabetically": false, 8 | "badgeTemplate": "[![All Contributors](https://img.shields.io/badge/all_contributors-<%= contributors.length %>-orange.svg?style=flat-square)](#contributors)", 9 | "contributorTemplate": "\">\" width=\"<%= options.imageSize %>px;\" alt=\"\"/>
<%= contributor.name %>
", 10 | "types": { 11 | "custom": { 12 | "symbol": "🔭", 13 | "description": "A custom contribution type.", 14 | "link": "[<%= symbol %>](<%= url %> \"<%= description %>\")," 15 | } 16 | }, 17 | "skipCi": true, 18 | "contributors": [ 19 | { 20 | "login": "technikhil314", 21 | "name": "nikhil.001mehta", 22 | "avatar_url": "https://avatars1.githubusercontent.com/u/6815560?v=4", 23 | "profile": "https://technikhil314.github.io", 24 | "contributions": [ 25 | "projectManagement" 26 | ] 27 | } 28 | ], 29 | "projectName": "auto-badger", 30 | "projectOwner": "technikhil314", 31 | "repoType": "github", 32 | "repoHost": "https://github.com" 33 | } 34 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 15 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 7 9 | 10 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - pinned 16 | - security 17 | - "[Status] Maybe Later" 18 | 19 | # Set to true to ignore issues in a project (defaults to false) 20 | exemptProjects: false 21 | 22 | # Set to true to ignore issues in a milestone (defaults to false) 23 | exemptMilestones: false 24 | 25 | # Set to true to ignore issues with an assignee (defaults to false) 26 | exemptAssignees: false 27 | 28 | # Label to use when marking as stale 29 | staleLabel: wontfix 30 | 31 | # Comment to post when marking as stale. Set to `false` to disable 32 | markComment: > 33 | This issue has been automatically marked as stale because it has not had 34 | recent activity. It will be closed if no further activity occurs. Thank you 35 | for your contributions. 36 | 37 | # Comment to post when removing the stale label. 38 | # unmarkComment: > 39 | # Your comment here. 40 | 41 | # Comment to post when closing a stale Issue or Pull Request. 42 | # closeComment: > 43 | # Your comment here. 44 | 45 | # Limit the number of actions per hour, from 1-30. Default is 30 46 | limitPerRun: 30 47 | # Limit to only `issues` or `pulls` 48 | # only: issues 49 | 50 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 51 | # pulls: 52 | # daysUntilStale: 30 53 | # markComment: > 54 | # This pull request has been automatically marked as stale because it has not had 55 | # recent activity. It will be closed if no further activity occurs. Thank you 56 | # for your contributions. 57 | 58 | # issues: 59 | # exemptLabels: 60 | # - confirmed 61 | -------------------------------------------------------------------------------- /.github/workflows/deploy_surge.yml: -------------------------------------------------------------------------------- 1 | name: deploy to surge 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | name: Deploy to angular-datetimerangepicker.surge.sh 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Setup Node 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: "12.x" 20 | 21 | - name: Build 22 | run: | 23 | npm install -g @angular/cli@6 24 | npm install 25 | ng build angular-datetimerangepicker 26 | ng build --prod 27 | 28 | - name: Install Surge 29 | run: npm install -g surge 30 | 31 | - name: Deploy to Surge 32 | run: | 33 | cd dist/angular-components 34 | surge . https://angular-datetimerangepicker.surge.sh/ --token ${{secrets.SURGE_TOKEN}} 35 | -------------------------------------------------------------------------------- /.github/workflows/publish_npm.yml: -------------------------------------------------------------------------------- 1 | name: publish to npm 2 | on: 3 | push: 4 | tags: 5 | - "*" 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: checkout 12 | uses: actions/checkout@v1 13 | - name: setup node 14 | uses: actions/setup-node@v1 15 | with: 16 | node-version: "12.x" 17 | registry-url: "https://registry.npmjs.org" 18 | env: 19 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 20 | - name: install dependencies 21 | run: | 22 | npm ci 23 | npm install -g @angular/cli@6 24 | - name: build using angular cli 25 | run: ng build angular-datetimerangepicker 26 | - run: cp -r projects/angular-datetimerangepicker/src/styles dist/angular-datetimerangepicker 27 | - name: publish 28 | run: | 29 | cd dist/angular-datetimerangepicker 30 | npm publish 31 | env: 32 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 33 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - "*" 6 | 7 | jobs: 8 | changelog: 9 | runs-on: ubuntu-latest 10 | name: create release on tag 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | # This action generates change long with then the release action consumes 16 | - name: Conventional Changelog Action 17 | id: changelog 18 | uses: TriPSs/conventional-changelog-action@v3 19 | with: 20 | github-token: ${{ secrets.github_token }} 21 | skip-commit: "true" 22 | 23 | - name: Create Release 24 | uses: actions/create-release@v1 25 | if: ${{ steps.changelog.outputs.skipped == 'false' }} 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.github_token }} 28 | with: 29 | release_name: ${{ steps.changelog.outputs.tag }} 30 | body: ${{ steps.changelog.outputs.clean_changelog }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:4200", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 nikhil.001mehta 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 | # AngularComponents 2 | 3 | ## This is collection of my open source projects created using angular 4 | 5 | Components are as follows 6 | 7 | - [angular-daterangepicker](https://github.com/technikhil314/angular-components/tree/master/projects/angular-datetimerangepicker) 8 | 9 | ## If you liked my work, show some :heart: 10 | 11 | ### :star: the repo. 12 | 13 | Also you can appreciate by 14 | 15 |

16 | 17 | 18 | 21 | 24 | 25 | 26 |
19 | Buy Me A Coffee 20 | 22 | PayPal Logo 23 |
27 |

28 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-components": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/angular-components", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "projects/angular-datetimerangepicker/src/styles/styles.css", 27 | "projects/angular-datetimerangepicker/src/styles/touch.css", 28 | "src/styles.css" 29 | ], 30 | "scripts": [] 31 | }, 32 | "configurations": { 33 | "production": { 34 | "fileReplacements": [ 35 | { 36 | "replace": "src/environments/environment.ts", 37 | "with": "src/environments/environment.prod.ts" 38 | } 39 | ], 40 | "optimization": true, 41 | "outputHashing": "all", 42 | "sourceMap": false, 43 | "extractCss": true, 44 | "namedChunks": false, 45 | "aot": true, 46 | "extractLicenses": true, 47 | "vendorChunk": false, 48 | "buildOptimizer": true 49 | } 50 | } 51 | }, 52 | "serve": { 53 | "builder": "@angular-devkit/build-angular:dev-server", 54 | "options": { 55 | "browserTarget": "angular-components:build" 56 | }, 57 | "configurations": { 58 | "production": { 59 | "browserTarget": "angular-components:build:production" 60 | } 61 | } 62 | }, 63 | "extract-i18n": { 64 | "builder": "@angular-devkit/build-angular:extract-i18n", 65 | "options": { 66 | "browserTarget": "angular-components:build" 67 | } 68 | }, 69 | "test": { 70 | "builder": "@angular-devkit/build-angular:karma", 71 | "options": { 72 | "main": "src/test.ts", 73 | "polyfills": "src/polyfills.ts", 74 | "tsConfig": "src/tsconfig.spec.json", 75 | "karmaConfig": "src/karma.conf.js", 76 | "styles": [ 77 | "src/styles.css" 78 | ], 79 | "scripts": [], 80 | "assets": [ 81 | "src/favicon.ico", 82 | "src/assets" 83 | ] 84 | } 85 | }, 86 | "lint": { 87 | "builder": "@angular-devkit/build-angular:tslint", 88 | "options": { 89 | "tsConfig": [ 90 | "src/tsconfig.app.json", 91 | "src/tsconfig.spec.json" 92 | ], 93 | "exclude": [ 94 | "**/node_modules/**" 95 | ] 96 | } 97 | } 98 | } 99 | }, 100 | "angular-components-e2e": { 101 | "root": "e2e/", 102 | "projectType": "application", 103 | "architect": { 104 | "e2e": { 105 | "builder": "@angular-devkit/build-angular:protractor", 106 | "options": { 107 | "protractorConfig": "e2e/protractor.conf.js", 108 | "devServerTarget": "angular-components:serve" 109 | }, 110 | "configurations": { 111 | "production": { 112 | "devServerTarget": "angular-components:serve:production" 113 | } 114 | } 115 | }, 116 | "lint": { 117 | "builder": "@angular-devkit/build-angular:tslint", 118 | "options": { 119 | "tsConfig": "e2e/tsconfig.e2e.json", 120 | "exclude": [ 121 | "**/node_modules/**" 122 | ] 123 | } 124 | } 125 | } 126 | }, 127 | "angular-datetimerangepicker": { 128 | "root": "projects/angular-datetimerangepicker", 129 | "sourceRoot": "projects/angular-datetimerangepicker/src", 130 | "projectType": "library", 131 | "prefix": "lib", 132 | "architect": { 133 | "build": { 134 | "builder": "@angular-devkit/build-ng-packagr:build", 135 | "options": { 136 | "tsConfig": "projects/angular-datetimerangepicker/tsconfig.lib.json", 137 | "project": "projects/angular-datetimerangepicker/ng-package.json" 138 | } 139 | }, 140 | "test": { 141 | "builder": "@angular-devkit/build-angular:karma", 142 | "options": { 143 | "main": "projects/angular-datetimerangepicker/src/test.ts", 144 | "tsConfig": "projects/angular-datetimerangepicker/tsconfig.spec.json", 145 | "karmaConfig": "projects/angular-datetimerangepicker/karma.conf.js" 146 | } 147 | }, 148 | "lint": { 149 | "builder": "@angular-devkit/build-angular:tslint", 150 | "options": { 151 | "tsConfig": [ 152 | "projects/angular-datetimerangepicker/tsconfig.lib.json", 153 | "projects/angular-datetimerangepicker/tsconfig.spec.json" 154 | ], 155 | "exclude": [ 156 | "**/node_modules/**" 157 | ] 158 | } 159 | } 160 | } 161 | } 162 | }, 163 | "defaultProject": "angular-components" 164 | } 165 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to angular-components!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-components", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^6.1.0", 15 | "@angular/common": "^6.1.0", 16 | "@angular/compiler": "^6.1.0", 17 | "@angular/core": "^6.1.0", 18 | "@angular/forms": "^6.1.0", 19 | "@angular/http": "^6.1.0", 20 | "@angular/platform-browser": "^6.1.0", 21 | "@angular/platform-browser-dynamic": "^6.1.0", 22 | "@angular/router": "^6.1.0", 23 | "calendarize": "^1.1.1", 24 | "core-js": "^2.5.4", 25 | "dayjs": "^1.9.6", 26 | "husky": "^4.3.0", 27 | "lint-staged": "^10.5.2", 28 | "rxjs": "~6.2.0", 29 | "zone.js": "~0.8.26" 30 | }, 31 | "devDependencies": { 32 | "@angular-devkit/build-angular": "~0.8.0", 33 | "@angular-devkit/build-ng-packagr": "~0.8.0", 34 | "@angular/cli": "~6.2.9", 35 | "@angular/compiler-cli": "^6.1.0", 36 | "@angular/language-service": "^6.1.0", 37 | "@types/jasmine": "~2.8.8", 38 | "@types/jasminewd2": "~2.0.3", 39 | "@types/node": "~8.9.4", 40 | "codelyzer": "~4.3.0", 41 | "jasmine-core": "~2.99.1", 42 | "jasmine-spec-reporter": "~4.2.1", 43 | "karma": "~3.0.0", 44 | "karma-chrome-launcher": "~2.2.0", 45 | "karma-coverage-istanbul-reporter": "~2.0.1", 46 | "karma-jasmine": "~1.1.2", 47 | "karma-jasmine-html-reporter": "^0.2.2", 48 | "ng-packagr": "^4.1.0", 49 | "protractor": "~5.4.0", 50 | "ts-node": "~7.0.0", 51 | "tsickle": "^0.39.1", 52 | "tslib": "^1.9.0", 53 | "tslint": "~5.11.0", 54 | "typescript": "~2.9.2" 55 | }, 56 | "husky": { 57 | "hooks": { 58 | "pre-commit": "npx ng lint --fix" 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/README.md: -------------------------------------------------------------------------------- 1 | ### Highly configurable, opinionated, themable, light weight (12kb) date and time range picker for angular 2 | 3 | [//]: <> (start placeholder for auto-badger) 4 | 5 | [![Build Status](https://img.shields.io/github/workflow/status/technikhil314/angular-components/deploy%20to%20surge?style=flat-square&color=%23007a1f)](https://github.com/technikhil314/angular-components/actions) 6 | [![version](https://img.shields.io/npm/v/angular-datetimerangepicker.svg?style=flat-square)](https://npmjs.org/angular-datetimerangepicker) 7 | [![min size](https://img.shields.io/bundlephobia/min/angular-datetimerangepicker)](https://bundlephobia.com/result?p=angular-datetimerangepicker) 8 | [![mingzip size](https://img.shields.io/bundlephobia/minzip/angular-datetimerangepicker)](https://bundlephobia.com/result?p=angular-datetimerangepicker) 9 | [![license](https://img.shields.io/npm/l/angular-datetimerangepicker?color=%23007a1f)](https://github.com/technikhil314/angular-components/blob/master/LICENSE) 10 | 11 | [![dependancies](https://img.shields.io/librariesio/release/npm/angular-datetimerangepicker?color=%23007a1f)](https://libraries.io/npm/angular-datetimerangepicker) 12 | [![downloads](https://img.shields.io/npm/dm/angular-datetimerangepicker)](https://npmcharts.com/compare/angular-datetimerangepicker) 13 | [![all contributors](https://img.shields.io/github/all-contributors/technikhil314/angular-components)](https://github.com/technikhil314/angular-components/graphs/contributors) 14 | [![code of conduct](https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square)](https://github.com/technikhil314/angular-components/blob/master/CODE_OF_CONDUCT.md) 15 | 16 | [![stargazers](https://img.shields.io/github/stars/technikhil314/angular-components?style=social)](https://github.com/technikhil314/angular-components/stargazers) 17 | [![number of forks](https://img.shields.io/github/forks/technikhil314/angular-components?style=social)](https://github.com/technikhil314/angular-components/fork) 18 | [![Follow technikhil314 on twitter](https://img.shields.io/twitter/follow/technikhil314?label=Follow)](https://www.twitter.com/technikhil314) 19 | 20 | ###### :clap: & :heart: to [auto badger](https://github.com/technikhil314/auto-badger) for making badging simple 21 | 22 | [//]: <> (end placeholder for auto-badger) 23 | 24 | ## [For Documentation, configuration click here](https://technikhil314.netlify.app/docs/daterangepicker/introduction) 25 | 26 | ## [Playground / configuration generator](https://angular-datetimerangepicker.surge.sh) 27 | 28 | ## Coding examples 29 | 30 | 1. [With predefined custom ranges](https://stackblitz.com/edit/angular-datetimerangepicker-demo) 31 | - This requires dayjs installed 32 | 1. [Without predefined custom ranges](https://stackblitz.com/edit/angular-datetimerangepicker-demo-1) 33 | - This does not require any dependency 34 | 1. [Without bootstrap](https://stackblitz.com/edit/angular-datetimerangepicker-demo-without-bootstrap) 35 | - Plain css no customizations on styling 36 | 37 | ## About this package 38 | 39 | Date and time range picker for Angular v6 and above. 40 |
41 | This is a successor of this package located here [angular-2-daterangepicker](https://www.npmjs.com/package/angular-2-daterangepicker) 42 |
43 | It is a fully responsive date and time range picker with or without bootstrap.css. 44 |
45 | The purpose of this project is to remove dependencies on bootstrap, jquery etc. 46 |
47 | No offence here. These are good libraries but with modern frameworks they add more footprint hampering the performance 48 | 49 |
50 | 51 | ## Announcements 52 | 53 | - Date: 17th Mar 2021 v2.4.1 54 | 1. Allowing use of escape key to close the calendars 55 | - Date: 10th Mar 2021 v2.4.1 56 | 1. Making dates on calendar more screen reader friendly 57 | - Date: 31st Dec 2020 v2.4.0 58 | 1. Added 12 hours time format support 59 | - Date: 01 Dec 2020 v2.3.2 60 | 1. Added disableWeekEnds, disabledDays, disabledDates 61 | - Date: 01 Dec 2020 v2.3.1 62 | 1. Handeling onChanges component lifecycle hook 63 | - Date: 30 Nov 2020 v2.3.0 64 | 1. Added options hideControls, readOnly, placeholder 65 | 1. Added touch support too (Behind toggle and WIP) (Open issue on [github](https://github.com/technikhil314/angular-components/issues) if you want to try out touch support) 66 | - Date: 26 Nov 2020 v2.2.10 67 | 1. Added option to set week start day [issue #47](https://github.com/technikhil314/angular-components/issues/47) 68 | - Date: 19 Nov 2020 v2.1.9 69 | 1. Added intelligent position detector to avoid horizontal scroll 70 | 1. Using css variables to color the previous next chevron arrows 71 | - Date: 13th Nov 2020 v2.1.8 72 | 1. Using modern css (flexbox) 73 | 1. Removing dependency on bootstrap (now works without bootstrap too) 74 | 1. Adding option for theme 75 | 1. Added screen reader and keyboard accessibility 76 | - Date: 25 Oct 2020 77 | 1. Removed momentjs now using [dayjs](https://day.js.org/) 78 | - Date: 17 Oct 2020 79 | 1. This is a successor of this package located here [angular-2-daterangepicker](https://www.npmjs.com/package/angular-2-daterangepicker) 80 | 1. Published next major version. v1.0.0 81 | 1. Fixed issue [#45](https://github.com/technikhil314/angular-components/issues/45) 82 | 1. If you want to use this module with angular < v6. Then install v1.1.52 of [this package]([https://www.npmjs.com/package/angular-2-daterangepicker]) 83 | 1. This package uses angular version 6 or above 84 | 85 |
86 | 87 | ## Todos 88 | 89 | 1. Get rid of moment to minimise the package [:heavy_check_mark:] 90 | 1. Make style more robust. Use latest CSS features. [:heavy_check_mark:] 91 | 1. Add theme support [:heavy_check_mark:] 92 | 1. Make touch friendly UI for touch devices 93 | 94 | # Facing Problems 95 | 96 | Please let me know if you are facing any issues [here](https://github.com/technikhil314/angular-components/issues) 97 | 98 |
99 | 100 | # Contributions 101 | 102 | Would :heart: to see any contributions. 103 | 104 | ### How to contribute 105 | 106 | P.S. The code for demo which lies in `src` folder 107 | 108 | P.S. Actual code for npm package lies in `projects/angular-datetimerangepicker` directory 109 | 110 | 1. Fork this repo 111 | 1. `npm install @angular/cli@6` 112 | 1. `npm install` 113 | 1. `ng build angular-datetimerangepicker --watch` 114 | 1. run `ng serve` in another terminal window/shell 115 | 1. Make changes 116 | 1. Raise PR 117 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/angular-datetimerangepicker", 4 | "lib": { 5 | "entryFile": "src/public_api.ts", 6 | "umdModuleIds": { 7 | "calendarize": "calendarize" 8 | } 9 | }, 10 | "whitelistedNonPeerDependencies": [ 11 | "calendarize", 12 | "dayjs" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-datetimerangepicker", 3 | "version": "2.4.6", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/technikhil314/angular-components" 7 | }, 8 | "description": "Highly configurable, opinionated, themable, light weight (12kb) date and time range picker for angular", 9 | "keywords": [ 10 | "angular 6", 11 | "angular 7", 12 | "angular 8", 13 | "angular 9", 14 | "angular X", 15 | "angular component", 16 | "date range picker", 17 | "date picker", 18 | "time picker", 19 | "time range picker", 20 | "range picker", 21 | "bootstrap css" 22 | ], 23 | "sideEffects": true, 24 | "author": "Nikhil Mehta", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/technikhil314/angular-components/issues" 28 | }, 29 | "homepage": "https://github.com/technikhil314/angular-components/tree/master/projects/angular-datetimerangepicker", 30 | "dependencies": { 31 | "calendarize": "^1.1.1", 32 | "dayjs": "^1.9.3" 33 | }, 34 | "peerDependencies": { 35 | "@angular/common": "^6.0.0-rc.0 || ^6.0.0", 36 | "@angular/core": "^6.0.0-rc.0 || ^6.0.0" 37 | }, 38 | "funding": [ 39 | { 40 | "type": "Buy me a coffee", 41 | "url": "https://www.buymeacoffee.com/technikhil314" 42 | }, 43 | { 44 | "type": "paypal", 45 | "url": "https://www.paypal.com/paypalme/technikhil314" 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/calendar/calendar-component.html: -------------------------------------------------------------------------------- 1 |
2 | 6 | 10 | 11 | 15 | 19 |
20 |
23 |
24 | {{ day }} 25 |
26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 |
34 |
35 | 36 | 37 | 38 |
39 |
40 |
41 |
42 | 44 | 46 | 47 |
48 |
49 |
50 | 53 | 54 | 58 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/calendar/calendar-component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | EventEmitter, 4 | Input, 5 | OnChanges, 6 | Output, 7 | } from '@angular/core'; 8 | import calendarize from 'calendarize'; 9 | import dayjs, { Dayjs } from 'dayjs'; 10 | import customParser from 'dayjs/plugin/customParseFormat'; 11 | import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; 12 | import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; 13 | import localeData from 'dayjs/plugin/localeData'; 14 | import { 15 | DateChanged, 16 | MonthNameValue, 17 | Timepicker, 18 | YearMonthChanged, 19 | } from '../types'; 20 | 21 | dayjs.extend(isSameOrAfter); 22 | dayjs.extend(isSameOrBefore); 23 | dayjs.extend(customParser); 24 | dayjs.extend(localeData); 25 | @Component({ 26 | selector: 'calendar', 27 | templateUrl: './calendar-component.html', 28 | }) 29 | export class Calendar implements OnChanges { 30 | // #region Components inputs 31 | @Input() month: number; 32 | @Input() year: number; 33 | @Input() selectedFromDate: Dayjs; 34 | @Input() selectedToDate: Dayjs; 35 | @Input() isLeft: boolean; 36 | @Input() format: string; 37 | @Input() minDate: Dayjs; 38 | @Input() maxDate: Dayjs; 39 | @Input() inactiveBeforeStart: boolean; 40 | @Input() disableBeforeStart: boolean; 41 | @Input() timePicker: Timepicker; 42 | @Input() singleCalendar: boolean; 43 | @Input() weekStartsOn: number; 44 | @Input() addTouchSupport: boolean; 45 | @Input() disabledDates: Dayjs[]; 46 | @Input() disabledDays: number[]; 47 | @Input() disableWeekEnds: boolean; 48 | // #endregion 49 | 50 | maxMonth = 0; 51 | maxYear = 0; 52 | minYear = 0; 53 | minMonth = 0; 54 | srFormat = 'DD MMMM YYYY hh:mm a'; 55 | // #region component outputs 56 | @Output() dateChanged: EventEmitter = new EventEmitter(); 57 | @Output() monthChanged: EventEmitter = new EventEmitter(); 58 | @Output() yearChanged: EventEmitter = new EventEmitter(); 59 | @Output() scrollTop: EventEmitter = new EventEmitter(); 60 | // #endregion 61 | 62 | // #region all component variables 63 | isTouch = false; 64 | weekList: Dayjs[][]; 65 | weekDays: string[]; 66 | monthsList: MonthNameValue[] = []; 67 | yearsList: number[] = []; 68 | weekEndOn: number[]; 69 | // #endregion 70 | 71 | // #region setters and getters 72 | get monthText() { 73 | return dayjs() 74 | .set('year', +this.year) 75 | .set('month', +this.month) 76 | .format('MMM'); 77 | } 78 | // #endregion 79 | 80 | // #region Component Life cycle handlers 81 | ngOnChanges(): void { 82 | this.isTouch = 83 | this.addTouchSupport && !window.matchMedia('(hover: hover)').matches; 84 | this.maxYear = this.maxDate ? this.maxDate.get('year') : 100000; 85 | this.maxMonth = this.maxDate ? this.maxDate.get('month') : 12; 86 | this.minYear = this.minDate ? this.minDate.get('year') : 0; 87 | this.minMonth = this.minDate ? this.minDate.get('month') : -1; 88 | this.createCalendarGridData(); 89 | } 90 | // #endregion 91 | 92 | // #region view manipulations and condition providers 93 | getNextMonthFirstWeek(): Dayjs[] { 94 | const thisMonthStartDate = dayjs() 95 | .set('year', +this.year) 96 | .set('month', +this.month) 97 | .startOf('month'); 98 | const nextMonthStartDate = thisMonthStartDate 99 | .add(1, 'month') 100 | .startOf('month'); 101 | const year = nextMonthStartDate.get('year'); 102 | const month = nextMonthStartDate.get('month'); 103 | return calendarize(nextMonthStartDate.toDate(), this.weekStartsOn) 104 | .shift() 105 | .filter(Boolean) 106 | .map((day) => { 107 | return dayjs() 108 | .set('year', year) 109 | .set('month', month) 110 | .set('date', day) 111 | .set('hour', 0) 112 | .set('minute', 0) 113 | .set('second', 0) 114 | .set('millisecond', 0); 115 | }); 116 | } 117 | getPreviousMonthNthLastWeek(nthLastCount): Dayjs[] { 118 | const thisMonthStartDate = dayjs() 119 | .set('year', +this.year) 120 | .set('month', +this.month) 121 | .startOf('month'); 122 | const previousMonthStartDate = thisMonthStartDate 123 | .subtract(1, 'month') 124 | .startOf('month'); 125 | const year = previousMonthStartDate.get('year'); 126 | const month = previousMonthStartDate.get('month'); 127 | return calendarize(previousMonthStartDate.toDate(), this.weekStartsOn) 128 | .slice(-nthLastCount)[0] 129 | .filter(Boolean) 130 | .map((day) => { 131 | return dayjs() 132 | .set('year', year) 133 | .set('month', month) 134 | .set('date', day) 135 | .set('hour', 0) 136 | .set('minute', 0) 137 | .set('second', 0) 138 | .set('millisecond', 0); 139 | }); 140 | } 141 | createTouchCalendarGridData(): void { 142 | const monthsList = dayjs.months(); 143 | this.yearsList = []; 144 | this.monthsList = []; 145 | for (let i = 1900; i <= +dayjs().add(100, 'year').get('year'); i++) { 146 | if (i < this.minYear || i > this.maxYear) { 147 | continue; 148 | } 149 | this.yearsList.push(i); 150 | } 151 | for (let i = 0; i < monthsList.length; i++) { 152 | if (this.year === this.minYear && i < this.minMonth) { 153 | continue; 154 | } 155 | if (this.year === this.maxYear && i > this.maxMonth) { 156 | continue; 157 | } 158 | this.monthsList.push({ 159 | name: monthsList[i], 160 | value: i, 161 | }); 162 | } 163 | } 164 | createCalendarGridData(): void { 165 | if (this.year <= this.minYear && this.month < this.minMonth) { 166 | this.year = this.minYear; 167 | this.month = this.minMonth; 168 | } 169 | if (this.year >= this.maxYear && this.month > this.maxMonth) { 170 | this.year = this.maxYear; 171 | this.month = this.maxMonth; 172 | } 173 | let year = null; 174 | let month = null; 175 | this.setWeekDays(); 176 | this.setWeekEnd(); 177 | const thisMonthStartDate = dayjs() 178 | .set('year', +this.year) 179 | .set('month', +this.month) 180 | .startOf('month'); 181 | year = thisMonthStartDate.get('year'); 182 | month = thisMonthStartDate.get('month'); 183 | const thisMonthWeekList = calendarize( 184 | thisMonthStartDate.toDate(), 185 | this.weekStartsOn 186 | ).map((week) => { 187 | return week.filter(Boolean).map((day) => { 188 | if (day === 0) { 189 | return null; 190 | } 191 | return dayjs() 192 | .set('year', year) 193 | .set('month', month) 194 | .set('date', day) 195 | .set('hour', 0) 196 | .set('minute', 0) 197 | .set('second', 0) 198 | .set('millisecond', 0); 199 | }); 200 | }); 201 | // if this months first week has less than 7 days then take previous month's last week and merge them 202 | // This should be done only for grid view which is shown only on non touch devices 203 | if (!this.isTouch) { 204 | if (thisMonthWeekList[0].length < 7) { 205 | thisMonthWeekList[0] = this.getPreviousMonthNthLastWeek(1).concat( 206 | thisMonthWeekList[0] 207 | ); 208 | } 209 | // if this months last week has less than 7 days then take next month's first week and merge them 210 | if (thisMonthWeekList.slice(-1)[0].length < 7) { 211 | thisMonthWeekList[thisMonthWeekList.length - 1] = thisMonthWeekList 212 | .slice(-1)[0] 213 | .concat(this.getNextMonthFirstWeek()); 214 | } 215 | // if total number of weeks is less than 6 then we need to add one more week 216 | // Here we add previous months second last week 217 | if (thisMonthWeekList.length < 6) { 218 | thisMonthWeekList.unshift(this.getPreviousMonthNthLastWeek(2)); 219 | } 220 | } else { 221 | this.createTouchCalendarGridData(); 222 | } 223 | this.weekList = thisMonthWeekList; 224 | } 225 | setWeekEnd() { 226 | this.weekEndOn = [this.weekStartsOn, this.weekStartsOn + 6]; 227 | } 228 | setWeekDays() { 229 | let weekDays: string[] = dayjs 230 | .weekdaysShort() 231 | .map((x) => x.replace(/[a-zA-Z]{1}$/, '')); 232 | weekDays = [ 233 | ...weekDays.slice(this.weekStartsOn, 7), 234 | ...weekDays.slice(0, this.weekStartsOn), 235 | ]; 236 | this.weekDays = weekDays; 237 | } 238 | isDisabled(day: Dayjs) { 239 | if (this.disableWeekEnds && this.weekEndOn.includes(day.get('day'))) { 240 | return true; 241 | } 242 | if (this.disabledDays && this.disabledDays.includes(day.get('day'))) { 243 | return true; 244 | } 245 | if ( 246 | this.disabledDates && 247 | this.disabledDates.find((x) => x.isSame(day, 'date')) 248 | ) { 249 | return true; 250 | } 251 | if (this.disableBeforeStart && !this.isLeft) { 252 | return day.isBefore(this.selectedFromDate); 253 | } 254 | return day.isBefore(this.minDate) || day.isAfter(this.maxDate); 255 | } 256 | isDateAvailable(day: Dayjs) { 257 | if (day.get('month') !== this.month) { 258 | return false; 259 | } 260 | if ( 261 | !this.singleCalendar && 262 | this.inactiveBeforeStart && 263 | day.isBefore(this.selectedFromDate, 'date') 264 | ) { 265 | return false; 266 | } 267 | return true; 268 | } 269 | isSelectedDate(day) { 270 | if ( 271 | day.get('month') === this.month && 272 | day.isSame(this.selectedFromDate, 'date') 273 | ) { 274 | return true; 275 | } 276 | if ( 277 | !this.singleCalendar && 278 | day.get('month') === this.month && 279 | day.isSameOrAfter(this.selectedFromDate, 'date') && 280 | day.isSameOrBefore(this.selectedToDate, 'date') 281 | ) { 282 | return true; 283 | } 284 | } 285 | getFormattedDate(day) { 286 | return day.format(this.format); 287 | } 288 | // #endregion 289 | 290 | // #region self event handlers 291 | scrollMeOutTop() { 292 | this.scrollTop.emit(); 293 | } 294 | dateSelected(day) { 295 | this.dateChanged.emit({ 296 | day: day, 297 | isLeft: this.isLeft, 298 | }); 299 | } 300 | monthSelected(value) { 301 | this.monthChanged.emit({ 302 | value: value, 303 | isLeft: this.isLeft, 304 | }); 305 | } 306 | yearSelected(value) { 307 | if (value) { 308 | this.yearChanged.emit({ 309 | value: value, 310 | isLeft: this.isLeft, 311 | }); 312 | } 313 | } 314 | // #endregion 315 | } 316 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/daterangepicker/daterangepicker.component.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |
13 |
14 |
15 | {{fromDate | formatDate: derivedOptions.displayFormat}} 16 | 24 |
25 |
26 | {{toDate | formatDate: derivedOptions.displayFormat}} 27 | 35 |
36 |
37 |
38 |
39 | 43 | 47 | 50 |
51 |
52 | 56 |
57 |
58 |
59 |
60 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/daterangepicker/daterangepicker.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | DoCheck, 4 | ElementRef, 5 | EventEmitter, 6 | HostListener, 7 | Input, 8 | OnChanges, 9 | OnInit, 10 | Output, 11 | } from '@angular/core'; 12 | import dayjs, { Dayjs } from 'dayjs'; 13 | import customParser from 'dayjs/plugin/customParseFormat'; 14 | import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; 15 | import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; 16 | import defaults from '../defaults'; 17 | import { isLargeDesktop, isTouch } from '../helper'; 18 | import { DefinedDateRange, Options } from '../types'; 19 | 20 | dayjs.extend(isSameOrAfter); 21 | dayjs.extend(isSameOrBefore); 22 | dayjs.extend(customParser); 23 | 24 | declare var window: any; 25 | 26 | @Component({ 27 | selector: 'daterangepicker', 28 | templateUrl: './daterangepicker.component.html', 29 | }) 30 | export class Daterangepicker implements OnInit, DoCheck, OnChanges { 31 | // #region Inputs to component 32 | @Input() options: Options; 33 | @Input() class: string; 34 | // #endregion 35 | 36 | // #region output events from component 37 | @Output() rangeSelected = new EventEmitter(); 38 | // #endregion 39 | 40 | // #region all component variables 41 | showCalendars: boolean; 42 | range = ''; 43 | enableApplyButton = false; 44 | areOldDatesStored = false; 45 | fromDate: Dayjs; 46 | toDate: Dayjs; 47 | tempFromDate: Dayjs; 48 | tempToDate: Dayjs; 49 | oldFromDate: Dayjs; 50 | oldToDate: Dayjs; 51 | fromMonth: number; 52 | toMonth: number; 53 | fromYear: number; 54 | toYear: number; 55 | format: string; 56 | derivedOptions: Options; 57 | isLargeDesktop = false; 58 | // #endregion 59 | 60 | // #region inside out side click handler 61 | @HostListener('document:mousedown', ['$event']) 62 | @HostListener('document:mouseup', ['$event']) 63 | @HostListener('document:keyup', ['$event']) 64 | handleOutsideClick(event) { 65 | if (!this.derivedOptions.disabled) { 66 | const current: any = event.target; 67 | const host: any = this.elem.nativeElement; 68 | if ( 69 | host.compareDocumentPosition(current) & 70 | window.Node.DOCUMENT_POSITION_CONTAINED_BY 71 | ) { 72 | if (event.key === 'Escape') { 73 | return this.toggleCalendars(false); 74 | } 75 | this.storeOldDates(); 76 | return this.toggleCalendars(true); 77 | } 78 | if (this.showCalendars) { 79 | if (!this.derivedOptions.autoApply) { 80 | this.restoreOldDates(); 81 | } 82 | return this.toggleCalendars(false); 83 | } 84 | } 85 | } 86 | // #endregion 87 | 88 | // #region constructor 89 | constructor(private elem: ElementRef) {} 90 | // #endregion 91 | 92 | // #region Component Life cycle handlers 93 | ngDoCheck() { 94 | this.deriveOptions(true); 95 | } 96 | ngOnChanges() { 97 | this.initialize(); 98 | } 99 | ngOnInit(): void { 100 | this.initialize(); 101 | window.onresize = () => { 102 | this.isLargeDesktop = isLargeDesktop(); 103 | }; 104 | this.elem.nativeElement.setAttribute('class', ''); 105 | } 106 | // #endregion 107 | 108 | // #region Initilizers / configuration handlers 109 | initialize() { 110 | // get default options provided by user 111 | this.deriveOptions(); 112 | this.validateMinMaxDates(); 113 | this.setFromDate(this.derivedOptions.startDate); 114 | this.setToDate(this.derivedOptions.endDate); 115 | // update calendar grid 116 | this.updateCalendar(); 117 | } 118 | deriveOptions(isUpdate: boolean = false) { 119 | if (isUpdate) { 120 | const { 121 | startDate, 122 | endDate, 123 | minDate, 124 | maxDate, 125 | ...restOptions 126 | } = this.options; 127 | this.derivedOptions = { 128 | ...new Options(), 129 | ...this.derivedOptions, 130 | ...restOptions, 131 | }; 132 | } else { 133 | this.derivedOptions = { 134 | ...new Options(), 135 | ...this.options, 136 | }; 137 | } 138 | this.isLargeDesktop = isLargeDesktop(); 139 | if (isNaN(this.fromMonth)) { 140 | this.fromMonth = dayjs().get('month'); 141 | } 142 | if (isNaN(this.fromYear)) { 143 | this.fromYear = dayjs().get('year'); 144 | } 145 | if (isNaN(this.toMonth)) { 146 | this.toMonth = dayjs().get('month'); 147 | } 148 | if (isNaN(this.toYear)) { 149 | this.toYear = dayjs().get('year'); 150 | } 151 | this.derivedOptions.weekStartsOn = 152 | Math.abs(this.derivedOptions.weekStartsOn) % 7; 153 | if (this.derivedOptions.noDefaultRangeSelected) { 154 | this.derivedOptions.startDate = null; 155 | this.derivedOptions.endDate = null; 156 | } 157 | if (this.derivedOptions.singleCalendar) { 158 | this.derivedOptions.autoApply = true; 159 | this.derivedOptions.endDate = null; 160 | } 161 | if (this.derivedOptions.timePicker) { 162 | this.derivedOptions.autoApply = false; 163 | } 164 | if (this.derivedOptions.alwaysOpen) { 165 | this.derivedOptions.position = null; 166 | } 167 | if (!this.derivedOptions.displayFormat) { 168 | this.derivedOptions.displayFormat = this.derivedOptions.format; 169 | } 170 | if (this.derivedOptions.addTouchSupport && isTouch) { 171 | this.derivedOptions.alwaysOpen = false; 172 | } 173 | if ( 174 | this.derivedOptions.showRanges && 175 | !this.derivedOptions.preDefinedRanges 176 | ) { 177 | this.derivedOptions.preDefinedRanges = defaults.ranges; 178 | } 179 | if (window.innerWidth > 600) { 180 | if (this.derivedOptions.position === 'left') { 181 | const spaceToRight = 182 | window.innerWidth - 183 | this.elem.nativeElement.getBoundingClientRect().left; 184 | if (spaceToRight < 500) { 185 | this.derivedOptions.position = 'right'; 186 | } 187 | } else if (this.derivedOptions.position === 'right') { 188 | if (this.elem.nativeElement.getBoundingClientRect().right < 500) { 189 | this.derivedOptions.position = 'left'; 190 | } 191 | } 192 | } 193 | } 194 | // #endregion 195 | 196 | // #region date setters and getters 197 | setFromDate(value: Dayjs) { 198 | if (this.derivedOptions.noDefaultRangeSelected && !value) { 199 | this.fromDate = null; 200 | this.tempFromDate = this.getActualFromDate(value); 201 | } else { 202 | this.fromDate = this.getActualFromDate(value); 203 | } 204 | } 205 | getActualFromDate(value: Dayjs) { 206 | let temp; 207 | if ((temp = this.getValidateDayjs(value))) { 208 | return this.getValidateFromDate(temp); 209 | } 210 | return this.getValidateFromDate(dayjs()); 211 | } 212 | setToDate(value: Dayjs) { 213 | if (this.derivedOptions.noDefaultRangeSelected && !value) { 214 | this.toDate = null; 215 | this.tempToDate = this.getActualToDate(value); 216 | } else { 217 | this.toDate = this.getActualToDate(value); 218 | } 219 | } 220 | getActualToDate(value: Dayjs) { 221 | let temp; 222 | if ((temp = this.getValidateDayjs(value))) { 223 | return this.getValidateToDate(temp); 224 | } 225 | return this.getValidateToDate(dayjs()); 226 | } 227 | // #endregion 228 | 229 | // #region Validators 230 | validateMinMaxDates() { 231 | if (this.derivedOptions) { 232 | // only mindate is suppplied 233 | if (this.derivedOptions.minDate && !this.derivedOptions.maxDate) { 234 | this.derivedOptions.minDate = this.getDayjs( 235 | this.derivedOptions.minDate 236 | ); 237 | } 238 | // only maxdate is supplied 239 | if (!this.derivedOptions.minDate && this.derivedOptions.maxDate) { 240 | this.derivedOptions.maxDate = this.getDayjs( 241 | this.derivedOptions.maxDate 242 | ); 243 | } 244 | // both min and max dates are supplied 245 | if (this.derivedOptions.minDate && this.derivedOptions.maxDate) { 246 | this.derivedOptions.minDate = this.getDayjs( 247 | this.derivedOptions.minDate 248 | ); 249 | this.derivedOptions.maxDate = this.getDayjs( 250 | this.derivedOptions.maxDate 251 | ); 252 | if ( 253 | this.derivedOptions.maxDate.isBefore( 254 | this.derivedOptions.minDate, 255 | 'date' 256 | ) 257 | ) { 258 | this.derivedOptions.minDate = null; 259 | this.derivedOptions.maxDate = null; 260 | console.warn( 261 | 'supplied minDate is after maxDate. Discarding options for minDate and maxDate' 262 | ); 263 | } 264 | } 265 | if ( 266 | this.derivedOptions.minDate && 267 | this.derivedOptions.minDate.format('HH:mm') === '00:00' 268 | ) { 269 | this.derivedOptions.minDate.set('hour', 0); 270 | this.derivedOptions.minDate.set('minute', 0); 271 | this.derivedOptions.minDate.set('second', 0); 272 | } 273 | if ( 274 | this.derivedOptions.maxDate && 275 | this.derivedOptions.maxDate.format('HH:mm') === '00:00' 276 | ) { 277 | this.derivedOptions.minDate.set('hour', 23); 278 | this.derivedOptions.minDate.set('minute', 59); 279 | this.derivedOptions.minDate.set('second', 59); 280 | } 281 | } 282 | } 283 | getValidateFromDate(value: Dayjs) { 284 | if (!this.derivedOptions.timePicker) { 285 | if ( 286 | this.derivedOptions.minDate && 287 | this.derivedOptions.maxDate && 288 | value.isSameOrAfter(this.derivedOptions.minDate, 'date') && 289 | value.isSameOrBefore(this.derivedOptions.maxDate, 'date') 290 | ) { 291 | return value; 292 | } 293 | if ( 294 | this.derivedOptions.minDate && 295 | !this.derivedOptions.maxDate && 296 | value.isAfter(this.derivedOptions.minDate, 'date') 297 | ) { 298 | return value; 299 | } 300 | if (this.derivedOptions.minDate) { 301 | return this.derivedOptions.minDate.clone(); 302 | } 303 | return dayjs(); 304 | } else { 305 | if ( 306 | this.derivedOptions.minDate && 307 | this.derivedOptions.maxDate && 308 | value.isSameOrAfter(this.derivedOptions.minDate, 'date') && 309 | value.isSameOrBefore(this.derivedOptions.maxDate, 'date') 310 | ) { 311 | return value; 312 | } 313 | if ( 314 | this.derivedOptions.minDate && 315 | !this.derivedOptions.maxDate && 316 | value.isAfter(this.derivedOptions.minDate, 'date') 317 | ) { 318 | return value; 319 | } 320 | if (this.derivedOptions.minDate) { 321 | return this.derivedOptions.minDate.clone(); 322 | } 323 | return dayjs(); 324 | } 325 | } 326 | getValidateToDate(value: Dayjs) { 327 | if (!this.derivedOptions.timePicker) { 328 | if ( 329 | (this.derivedOptions.maxDate && 330 | value.isSameOrAfter(this.fromDate, 'date'), 331 | value.isSameOrBefore(this.derivedOptions.maxDate, 'date')) 332 | ) { 333 | return value; 334 | } 335 | if (this.derivedOptions.maxDate) { 336 | return this.derivedOptions.maxDate.clone(); 337 | } 338 | return dayjs(); 339 | } else { 340 | if ( 341 | (this.derivedOptions.maxDate && 342 | value.isSameOrAfter(this.fromDate, 'date'), 343 | value.isSameOrBefore(this.derivedOptions.maxDate, 'date')) 344 | ) { 345 | return value; 346 | } 347 | if (this.derivedOptions.maxDate) { 348 | return this.derivedOptions.maxDate.clone(); 349 | } 350 | return dayjs(); 351 | } 352 | } 353 | // #endregion 354 | 355 | // #region util functions 356 | getDayjs(value: string | Dayjs) { 357 | return dayjs(value, this.derivedOptions.format); 358 | } 359 | getValidateDayjs(value: string | Dayjs) { 360 | let dayjsValue = null; 361 | if (dayjs(value, this.derivedOptions.format, true).isValid()) { 362 | dayjsValue = dayjs(value, this.derivedOptions.format, true); 363 | } 364 | return dayjsValue; 365 | } 366 | // #endregion 367 | 368 | // #region date formatters 369 | formatFromDate(event) { 370 | if ( 371 | event.target.value !== this.fromDate.format(this.derivedOptions.format) 372 | ) { 373 | this.dateChanged({ 374 | day: event.target.value ? this.getDayjs(event.target.value) : dayjs(), 375 | isLeft: true, 376 | }); 377 | } 378 | } 379 | formatToDate(event) { 380 | if (event.target.value !== this.toDate.format(this.derivedOptions.format)) { 381 | this.dateChanged({ 382 | day: event.target.value ? this.getDayjs(event.target.value) : dayjs(), 383 | isLeft: false, 384 | }); 385 | } 386 | } 387 | // #endregion 388 | 389 | // #region Child component event handlers 390 | scrollTop() { 391 | const flyout = this.elem.nativeElement.querySelector('.drp-flyout'); 392 | flyout.scrollBy(0, 300); 393 | } 394 | dateChanged(data) { 395 | let value = data.day; 396 | const isLeft = data.isLeft; 397 | if (isLeft) { 398 | if (!this.derivedOptions.timePicker) { 399 | value = value.hour(0); 400 | value = value.minute(0); 401 | value = value.second(0); 402 | } 403 | this.fromDate = value; 404 | if (!this.derivedOptions.timePicker) { 405 | if (value.isAfter(this.toDate, 'date')) { 406 | this.toDate = this.fromDate.clone(); 407 | } 408 | } else { 409 | if (value.isAfter(this.toDate, this.derivedOptions.format)) { 410 | this.toDate = this.fromDate.clone(); 411 | } 412 | } 413 | } else { 414 | if (!this.derivedOptions.timePicker) { 415 | value = value.hour(23); 416 | value = value.minute(59); 417 | value = value.second(59); 418 | } 419 | this.toDate = value; 420 | this.toYear = this.toDate.get('year'); 421 | if (!this.derivedOptions.timePicker) { 422 | if (value.isBefore(this.fromDate, 'date')) { 423 | this.fromDate = this.toDate.clone(); 424 | } 425 | } else { 426 | if (value.isBefore(this.fromDate, this.derivedOptions.format)) { 427 | this.fromDate = this.toDate.clone(); 428 | } 429 | } 430 | } 431 | if (this.derivedOptions.autoApply) { 432 | if (this.derivedOptions.singleCalendar || !isLeft) { 433 | this.toggleCalendars(false); 434 | this.setRange(); 435 | this.emitRangeSelected(); 436 | } 437 | } else if ( 438 | !this.derivedOptions.singleCalendar && 439 | this.fromDate && 440 | this.fromDate.isValid() && 441 | this.toDate && 442 | this.toDate.isValid() 443 | ) { 444 | this.enableApplyButton = true; 445 | } else if (this.derivedOptions.singleCalendar) { 446 | this.enableApplyButton = true; 447 | } 448 | this.fromMonth = this.fromDate 449 | ? this.fromDate.get('month') 450 | : this.fromMonth; 451 | this.toMonth = this.toDate ? this.toDate.get('month') : this.toMonth; 452 | this.fromYear = this.fromDate ? this.fromDate.get('year') : this.fromYear; 453 | if (!this.toDate && this.fromDate) { 454 | this.toYear = this.fromYear; 455 | this.toMonth = this.fromMonth; 456 | } 457 | if (this.toYear < this.fromYear) { 458 | this.toYear = this.fromYear; 459 | } 460 | } 461 | monthChanged(data) { 462 | let temp; 463 | if (!isTouch) { 464 | if (data.isLeft) { 465 | temp = dayjs() 466 | .set('year', this.fromYear) 467 | .set('month', this.fromMonth) 468 | .add(data.value, 'month'); 469 | this.fromMonth = temp.get('month'); 470 | this.fromYear = temp.get('year'); 471 | } else { 472 | temp = dayjs() 473 | .set('year', this.toYear) 474 | .set('month', this.toMonth) 475 | .add(data.value, 'month'); 476 | this.toMonth = temp.get('month'); 477 | this.toYear = temp.get('year'); 478 | } 479 | } else { 480 | if (data.isLeft) { 481 | this.fromMonth = data.value; 482 | } else { 483 | this.toMonth = data.value; 484 | } 485 | } 486 | } 487 | yearChanged(data) { 488 | if (data.isLeft) { 489 | this.fromYear = data.value; 490 | } else { 491 | this.toYear = data.value; 492 | } 493 | } 494 | // #endregion 495 | 496 | // #region side effect handlers of user actions 497 | emitRangeSelected() { 498 | let data = {}; 499 | if (this.derivedOptions.singleCalendar) { 500 | data = { 501 | start: this.getDayjs(this.fromDate), 502 | }; 503 | } else { 504 | data = { 505 | start: this.getDayjs(this.fromDate), 506 | end: this.getDayjs(this.toDate), 507 | }; 508 | } 509 | this.enableApplyButton = false; 510 | this.rangeSelected.emit(data); 511 | } 512 | storeOldDates() { 513 | if (!this.areOldDatesStored) { 514 | this.oldFromDate = this.fromDate; 515 | this.oldToDate = this.toDate; 516 | this.areOldDatesStored = true; 517 | } 518 | } 519 | restoreOldDates() { 520 | this.fromDate = this.oldFromDate; 521 | this.toDate = this.oldToDate; 522 | } 523 | setRange() { 524 | if (this.derivedOptions.singleCalendar && this.fromDate) { 525 | this.range = this.fromDate.format(this.derivedOptions.displayFormat); 526 | } else if (this.fromDate && this.toDate) { 527 | this.range = 528 | this.fromDate.format(this.derivedOptions.displayFormat) + 529 | ' - ' + 530 | this.toDate.format(this.derivedOptions.displayFormat); 531 | } else { 532 | this.range = ''; 533 | } 534 | } 535 | // #endregion 536 | 537 | // #region all control button handlers 538 | apply() { 539 | this.toggleCalendars(false); 540 | this.setRange(); 541 | this.emitRangeSelected(); 542 | } 543 | cancel() { 544 | this.restoreOldDates(); 545 | this.toggleCalendars(false); 546 | } 547 | clear() { 548 | this.fromDate = this.toDate = null; 549 | this.apply(); 550 | this.enableApplyButton = false; 551 | } 552 | applyPredefinedRange(data: DefinedDateRange) { 553 | this.setFromDate(data.value.start); 554 | this.setToDate(data.value.end); 555 | this.toggleCalendars(false); 556 | this.emitRangeSelected(); 557 | } 558 | // #endregion 559 | 560 | // #region view manipulations and condition providers 561 | toggleCalendars(value: boolean) { 562 | this.showCalendars = value; 563 | if (!value) { 564 | this.areOldDatesStored = false; 565 | this.updateCalendar(); 566 | } 567 | } 568 | updateCalendar() { 569 | // get month and year to show calendar 570 | const fromDate = dayjs(this.fromDate).isValid() 571 | ? this.fromDate 572 | : this.tempFromDate; 573 | const toDate = dayjs(this.toDate).isValid() ? this.toDate : this.tempToDate; 574 | let tDate = dayjs(fromDate, this.derivedOptions.format); 575 | this.fromMonth = tDate.get('month'); 576 | this.fromYear = tDate.get('year'); 577 | tDate = dayjs(toDate, this.derivedOptions.format); 578 | this.toMonth = tDate.get('month'); 579 | this.toYear = tDate.get('year'); 580 | this.setRange(); 581 | } 582 | getAriaLabel() { 583 | if (this.fromDate && this.toDate) { 584 | return ( 585 | this.fromDate.format(this.derivedOptions.displayFormat) + 586 | ' to ' + 587 | this.toDate.format(this.derivedOptions.displayFormat) 588 | ); 589 | } 590 | return 'Please select a date range'; 591 | } 592 | // #endregion 593 | } 594 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/datetimerangepicker.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { Calendar } from './calendar/calendar-component'; 5 | import { Daterangepicker } from './daterangepicker/daterangepicker.component'; 6 | import { FormatDatePipe } from './format-date-pipe'; 7 | import { ChevronLeft } from './img/chevron-left'; 8 | import { DoubleChevronLeft } from './img/double-chevron-left'; 9 | import { TimePicker } from './time/time-component'; 10 | @NgModule({ 11 | imports: [FormsModule, CommonModule], 12 | declarations: [ 13 | Daterangepicker, 14 | Calendar, 15 | TimePicker, 16 | FormatDatePipe, 17 | DoubleChevronLeft, 18 | ChevronLeft, 19 | ], 20 | exports: [Daterangepicker], 21 | }) 22 | export class DatetimerangepickerModule {} 23 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/defaults.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs'; 2 | 3 | const defaults = { 4 | ranges: [ 5 | { 6 | name: 'Today', 7 | value: { 8 | start: dayjs().startOf('day'), 9 | end: dayjs().endOf('day'), 10 | }, 11 | }, 12 | { 13 | name: 'Yesterday', 14 | value: { 15 | start: dayjs().subtract(1, 'day').startOf('day'), 16 | end: dayjs().subtract(1, 'day').endOf('day'), 17 | }, 18 | }, 19 | { 20 | name: 'This week', 21 | value: { 22 | start: dayjs().startOf('week'), 23 | end: dayjs().endOf('week'), 24 | }, 25 | }, 26 | { 27 | name: 'This month', 28 | value: { 29 | start: dayjs().startOf('month'), 30 | end: dayjs().endOf('month'), 31 | }, 32 | }, 33 | { 34 | name: 'Last Month', 35 | value: { 36 | start: dayjs().subtract(1, 'month').startOf('month'), 37 | end: dayjs().subtract(1, 'month').endOf('month'), 38 | }, 39 | }, 40 | ], 41 | }; 42 | 43 | export default defaults; 44 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/format-date-pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | @Pipe({ name: 'formatDate' }) 3 | export class FormatDatePipe implements PipeTransform { 4 | transform(value: any, format: string): string { 5 | if (value) { 6 | return value.format(format); 7 | } 8 | return ''; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/helper.ts: -------------------------------------------------------------------------------- 1 | export const isTouch: boolean = !window.matchMedia('(hover: hover)').matches; 2 | export function isLargeDesktop(): boolean { 3 | return window.matchMedia('(min-width: 1000px)').matches; 4 | } 5 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/img/chevron-left.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'chevronleft', 5 | template: ` 6 | 19 | 20 | 23 | 24 | 25 | `, 26 | }) 27 | export class ChevronLeft { 28 | @Input() class: string; 29 | } 30 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/img/double-chevron-left.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'doublechevronleft', 5 | template: ` 6 | 19 | 20 | 23 | 26 | 27 | 28 | `, 29 | }) 30 | export class DoubleChevronLeft { 31 | @Input() class: string; 32 | } 33 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/public_api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of angular-datetimerangepicker 3 | */ 4 | declare var require: any; 5 | const dayjs = require('dayjs'); 6 | const isSameOrAfter = require('dayjs/plugin/isSameOrAfter'); 7 | dayjs.extend(isSameOrAfter); 8 | const isSameOrBefore = require('dayjs/plugin/isSameOrBefore'); 9 | dayjs.extend(isSameOrBefore); 10 | const customParser = require('dayjs/plugin/customParseFormat'); 11 | dayjs.extend(customParser); 12 | const isoWeek = require('dayjs/plugin/isoWeek'); 13 | dayjs.extend(isoWeek); 14 | const arraySupport = require('dayjs/plugin/arraySupport'); 15 | dayjs.extend(arraySupport); 16 | 17 | export * from './datetimerangepicker.module'; 18 | export * from './types'; 19 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/styles/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --drp-input-height: 33px; 3 | --drp-input-border-radius: 4px; 4 | --drp-bg: hsla(0, 0%, 98%); 5 | --drp-fg: hsla(0, 0%, 20%); 6 | --drp-shadow-color: rgba(0, 0, 0, 0.2); 7 | --drp-hover-bg: hsla(0, 0%, 80%); 8 | --drp-outline-color: hsl(240deg, 50%, 30%); 9 | --drp-input-border-color: #666; 10 | --drp-input-disabled-color: #dedede; 11 | } 12 | 13 | .drp-wrapper { 14 | position: relative; 15 | border: none; 16 | font-feature-settings: "tnum"; 17 | color: var(--drp-fg); 18 | } 19 | 20 | .drp-wrapper.dark { 21 | --drp-fg: hsla(0, 0%, 99%); 22 | --drp-bg: hsla(0, 0%, 20%); 23 | --drp-shadow-color: rgba(255, 255, 255, 0.2); 24 | --drp-hover-bg: hsla(0, 0%, 40%); 25 | --drp-outline-color: hsl(240deg, 30%, 90%); 26 | --drp-input-border-color: #666; 27 | --drp-input-disabled-color: #2d2d2d; 28 | } 29 | 30 | /* all input boxes */ 31 | 32 | .drp-input { 33 | padding: 0px 10px; 34 | height: var(--drp-input-height); 35 | width: 100%; 36 | box-sizing: border-box; 37 | border-radius: var(--drp-input-border-radius); 38 | border: 1px solid var(--drp-input-border-color); 39 | cursor: text; 40 | background: var(--drp-bg); 41 | color: var(--drp-fg); 42 | } 43 | 44 | .drp-input[disabled] { 45 | background: var(--drp-input-disabled-color); 46 | } 47 | 48 | .drp-input[disabled] { 49 | cursor: not-allowed; 50 | pointer-events: none; 51 | } 52 | 53 | /* Selected date to be shown on top of each calendar */ 54 | .drp-selected-date { 55 | display: inline-block; 56 | min-height: 1rem; 57 | } 58 | 59 | /* the flyout/popup */ 60 | 61 | .drp-flyout { 62 | width: var(--drp-flyout-width, auto); 63 | position: absolute; 64 | background: var(--drp-bg); 65 | display: flex; 66 | flex-direction: column; 67 | z-index: 1000; 68 | border-radius: 10px; 69 | box-shadow: 0px 0px 5px 1px var(--drp-shadow-color); 70 | border: 1px solid #aaa; 71 | padding: 10px 10px 0; 72 | max-width: min-content; 73 | } 74 | 75 | .drp-flyout.right { 76 | width: var(--drp-flyout-single-calendar-width, auto); 77 | } 78 | 79 | .drp-flyout.right { 80 | right: 0; 81 | } 82 | 83 | .drp-flyout.left { 84 | left: 0; 85 | } 86 | 87 | .drp-flyout.center { 88 | left: -50%; 89 | } 90 | 91 | .drp-flyout.alwaysOpen { 92 | width: 100%; 93 | position: static; 94 | } 95 | 96 | .drp-flyout.tooltipChevron { 97 | top: calc(var(--drp-input-height) + 10px); 98 | } 99 | 100 | .drp-flyout.tooltipChevron::after, 101 | .drp-flyout.tooltipChevron::before { 102 | content: ""; 103 | position: absolute; 104 | height: 0; 105 | width: 0; 106 | border-style: solid; 107 | transform: rotate(-45deg); 108 | } 109 | 110 | .drp-flyout.tooltipChevron::after { 111 | border-color: var(--drp-bg) var(--drp-bg) transparent transparent; 112 | border-width: 10px; 113 | } 114 | 115 | .drp-flyout.tooltipChevron::before { 116 | border-color: var(--drp-shadow-color) var(--drp-shadow-color) transparent 117 | transparent; 118 | border-width: 12px; 119 | } 120 | 121 | .drp-flyout.left.tooltipChevron::after { 122 | top: -8px; 123 | left: 15px; 124 | } 125 | 126 | .drp-flyout.left.tooltipChevron::before { 127 | top: -10px; 128 | left: 13px; 129 | } 130 | 131 | .drp-flyout.right.tooltipChevron::after { 132 | top: -8px; 133 | right: 15px; 134 | } 135 | 136 | .drp-flyout.right.tooltipChevron::before { 137 | top: -10px; 138 | right: 13px; 139 | } 140 | 141 | .drp-flyout.center.tooltipChevron::after { 142 | top: -8px; 143 | left: calc(50% - 12px); 144 | } 145 | 146 | .drp-flyout.center.tooltipChevron::before { 147 | top: -10px; 148 | left: calc(50% - 13px); 149 | } 150 | 151 | /* chevron / arrows */ 152 | svg { 153 | fill: var(--drp-fg); 154 | } 155 | 156 | .drp-btn.next svg { 157 | transform: rotateY(180deg); 158 | } 159 | 160 | .drp-btn.up svg { 161 | transform: rotate(90deg); 162 | } 163 | 164 | .drp-btn.down svg { 165 | transform: rotate(-90deg); 166 | } 167 | 168 | /* wrapper for both calendars */ 169 | 170 | .drp-calendars-wrapper { 171 | display: flex; 172 | justify-content: space-around; 173 | } 174 | 175 | /* wrapper for each calendar and its input box */ 176 | 177 | .drp-calendar-container { 178 | margin-left: 10px; 179 | margin-right: 10px; 180 | position: relative; 181 | text-align: center; 182 | } 183 | 184 | @media only screen and (max-width: 600px) { 185 | .drp-calendars-wrapper { 186 | flex-wrap: wrap; 187 | } 188 | .drp-calendar-container + .drp-calendar-container { 189 | margin-top: 10px; 190 | } 191 | } 192 | 193 | /* Each row in calendar */ 194 | 195 | .drp-calendar-row { 196 | width: 100%; 197 | margin: 0; 198 | justify-content: space-between; 199 | display: flex; 200 | } 201 | 202 | .drp-calendar-row span { 203 | min-width: 14.29%; 204 | text-align: center; 205 | padding: 2px 0; 206 | transition: background 0.2s; 207 | border-radius: 2px; 208 | } 209 | 210 | .drp-calendar-row span.active { 211 | background: var(--drp-hover-bg); 212 | } 213 | 214 | .drp-calendar-row span.off { 215 | opacity: 0.65; 216 | filter: blur(0.05em); 217 | } 218 | 219 | .drp-calendar-row span:not(.day):hover { 220 | background: var(--drp-hover-bg); 221 | } 222 | 223 | .drp-calendar-row span:hover .drp-btn { 224 | color: var(--drp-hover-fg, var(--drp-fg)); 225 | } 226 | 227 | /* Range buttons wrapper */ 228 | .range-controls { 229 | flex-wrap: wrap; 230 | } 231 | 232 | /* all buttons */ 233 | 234 | .drp-btn { 235 | border-radius: 4px; 236 | border: 1px solid var(--drp-fg); 237 | color: var(--drp-fg); 238 | background: var(--drp-bg); 239 | white-space: nowrap; 240 | transition: transform 0.3s, background 0.3s; 241 | line-height: 1.5; 242 | vertical-align: middle; 243 | box-shadow: none; 244 | } 245 | 246 | .drp-btn:hover { 247 | cursor: pointer; 248 | } 249 | 250 | .drp-btn + .drp-btn { 251 | margin-left: 5px; 252 | } 253 | 254 | .drp-btn.flat { 255 | background: transparent; 256 | border: none; 257 | } 258 | 259 | .drp-btn.flat:hover { 260 | background: var(--drp-hover-bg); 261 | } 262 | 263 | .drp-btn.flat.link:hover { 264 | background: var(--drp-bg); 265 | text-decoration: underline; 266 | } 267 | 268 | .drp-btn.outline { 269 | border: 1px solid var(--drp-outline-color); 270 | margin-bottom: 10px; 271 | padding: 0 5px; 272 | } 273 | 274 | .drp-btn.outline:hover { 275 | filter: invert(1) hue-rotate(180deg); 276 | } 277 | 278 | .drp-btn.apply { 279 | color: white; 280 | background: #129a90; 281 | border: 1px solid #125612; 282 | } 283 | 284 | /* Utility classes */ 285 | 286 | .disabled, 287 | [disabled] { 288 | cursor: not-allowed !important; 289 | pointer-events: none !important; 290 | opacity: 0.5 !important; 291 | } 292 | 293 | .hidden { 294 | display: none !important; 295 | } 296 | 297 | .drp-flex { 298 | margin: 10px 0 5px; 299 | display: flex; 300 | justify-content: center; 301 | align-items: center; 302 | } 303 | 304 | .drp-space-around { 305 | justify-content: space-around; 306 | } 307 | 308 | .drp-column { 309 | flex-direction: column; 310 | } 311 | 312 | .my-0 { 313 | margin-top: 0; 314 | margin-bottom: 0; 315 | } 316 | 317 | .ml-2 { 318 | margin-left: 0.5rem; 319 | } 320 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/styles/touch.css: -------------------------------------------------------------------------------- 1 | @media (hover: none) { 2 | .drp-flyout.touch.tooltipChevron::after, 3 | .drp-flyout.touch.tooltipChevron::before { 4 | display: none; 5 | } 6 | .drp-flyout.touch.fixed { 7 | position: fixed; 8 | top: auto; 9 | left: 0; 10 | right: 0; 11 | bottom: 0; 12 | max-width: 100vw; 13 | flex-direction: row; 14 | justify-content: space-around; 15 | display: flex; 16 | flex-wrap: wrap; 17 | max-height: 325px; 18 | overflow: auto; 19 | scroll-behavior: smooth; 20 | overscroll-behavior: contain; 21 | overflow-x: hidden; 22 | } 23 | .drp-flyout.touch .drp-calendars-wrapper { 24 | flex-wrap: wrap; 25 | } 26 | .drp-flyout.touch .drp-calendar-row { 27 | flex-direction: column; 28 | } 29 | .drp-flyout.touch .drp-btn { 30 | padding-top: 3px; 31 | padding-bottom: 3px; 32 | } 33 | .drp-flyout.touch .drp-calendar-row span.disabled { 34 | display: none; 35 | } 36 | .drp-flyout.touch .drp-calendar-column { 37 | max-height: 150px; 38 | overscroll-behavior: contain; 39 | overflow-y: scroll; 40 | scroll-snap-type: y mandatory; 41 | justify-content: flex-start; 42 | } 43 | .drp-flyout.touch .drp-calendar-column .drp-calendar-row { 44 | scroll-snap-align: start; 45 | } 46 | .drp-flyout.touch .drp-controls, 47 | .drp-flyout.touch .drp-calendar-container { 48 | width: 100vw; 49 | align-self: center; 50 | } 51 | .drp-flyout.touch .drp-next-calendar-cta { 52 | position: absolute; 53 | bottom: 5px; 54 | right: 0; 55 | } 56 | } 57 | @media (hover: none) and (min-width: 700px) { 58 | .drp-flyout.touch .drp-calendars-wrapper, 59 | .drp-flyout.touch.fixed { 60 | flex-wrap: nowrap; 61 | } 62 | .drp-flyout.touch .drp-controls, 63 | .drp-flyout.touch .drp-calendar-container { 64 | width: auto; 65 | } 66 | .drp-flyout.touch .drp-controls { 67 | max-width: 30%; 68 | } 69 | .drp-flyout.touch .drp-next-calendar-cta { 70 | display: none; 71 | } 72 | } 73 | 74 | @media (hover: none) and (min-width: 1000px) { 75 | .drp-flyout.touch .drp-flyout.tooltipChevron::after, 76 | .drp-flyout.touch .drp-flyout.tooltipChevron::before { 77 | display: block; 78 | } 79 | .drp-flyout.touch { 80 | max-height: auto; 81 | } 82 | .drp-flyout.touch .drp-controls { 83 | max-width: 100vw; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/time/time-component.html: -------------------------------------------------------------------------------- 1 |
3 |
4 | 8 | 9 | 13 |
14 | 15 | h 16 | 17 |
18 | 22 | 23 | 28 |
29 | 30 | m 31 | 32 |
33 | 36 | {{meridiem}} 37 | 40 |
41 |
42 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/time/time-component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | EventEmitter, 4 | Input, 5 | OnChanges, 6 | OnInit, 7 | Output, 8 | SimpleChanges, 9 | } from '@angular/core'; 10 | import { Timepicker } from '../types'; 11 | import dayjs, { Dayjs } from 'dayjs'; 12 | import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; 13 | import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; 14 | import customParser from 'dayjs/plugin/customParseFormat'; 15 | 16 | dayjs.extend(isSameOrAfter); 17 | dayjs.extend(isSameOrBefore); 18 | dayjs.extend(customParser); 19 | @Component({ 20 | selector: 'timepicker', 21 | templateUrl: './time-component.html', 22 | }) 23 | export class TimePicker implements OnInit, OnChanges { 24 | // #region all component inputs 25 | @Input() 26 | options: Timepicker = new Timepicker(); 27 | @Input() 28 | selectedFromDate: Dayjs; 29 | @Input() 30 | selectedToDate: Dayjs; 31 | @Input() 32 | minDate: Dayjs; 33 | @Input() 34 | maxDate: Dayjs; 35 | @Input() 36 | format: string; 37 | @Input() 38 | isLeft: boolean; 39 | // #endregion 40 | 41 | // #region all components outputs 42 | @Output() 43 | timeChanged: EventEmitter = new EventEmitter(); 44 | // #endregion 45 | 46 | meridiem: string; 47 | 48 | // #region Component Life cycle handlers 49 | ngOnInit(): void { 50 | this.meridiem = this.isLeft 51 | ? this.selectedFromDate.format('A') 52 | : this.selectedToDate.format('A'); 53 | if ( 54 | !this.options.minuteInterval || 55 | this.options.minuteInterval % 60 === 0 56 | ) { 57 | this.options.minuteInterval = 1; 58 | } 59 | this.options.minuteInterval = this.options.minuteInterval % 60; 60 | } 61 | ngOnChanges(changes: SimpleChanges): void { 62 | this.selectedFromDate = changes['selectedFromDate'] 63 | ? dayjs(changes['selectedFromDate'].currentValue, this.format) 64 | : this['selectedFromDate']; 65 | this.selectedToDate = changes['selectedToDate'] 66 | ? dayjs(changes['selectedToDate'].currentValue, this.format) 67 | : this['selectedToDate']; 68 | this.maxDate = changes['maxDate'] 69 | ? dayjs(changes['maxDate'].currentValue, this.format) 70 | : this['maxDate']; 71 | this.minDate = changes['minDate'] 72 | ? dayjs(changes['minDate'].currentValue, this.format) 73 | : this['minDate']; 74 | this.meridiem = this.isLeft 75 | ? this.selectedFromDate.format('A') 76 | : this.selectedToDate.format('A'); 77 | } 78 | // #endregion 79 | 80 | // #region view manipulations and condition providers 81 | getCurrentHour() { 82 | const modFactor: number = this.options.twentyFourHourFormat ? 24 : 12; 83 | let currentHour = 84 | (this.isLeft 85 | ? this.selectedFromDate.get('hour') 86 | : this.selectedToDate.get('hour')) % modFactor; 87 | if (currentHour === 0 && !this.options.twentyFourHourFormat) { 88 | currentHour = 12; 89 | } 90 | return isNaN(currentHour) 91 | ? '—' 92 | : currentHour > 9 93 | ? currentHour 94 | : '0' + currentHour; 95 | } 96 | getCurrentMinute() { 97 | const currentMinute = this.isLeft 98 | ? this.selectedFromDate.get('minute') 99 | : this.selectedToDate.get('minute'); 100 | return isNaN(currentMinute) 101 | ? '—' 102 | : currentMinute > 9 103 | ? currentMinute 104 | : '0' + currentMinute; 105 | } 106 | isValidToAddMinute(value: number) { 107 | let possibleNewValue, possibleSelectedDate; 108 | if (this.isLeft) { 109 | possibleNewValue = this.selectedFromDate.get('minute') + value; 110 | possibleSelectedDate = this.selectedFromDate.clone().add(value, 'minute'); 111 | } else { 112 | possibleNewValue = this.selectedToDate.get('minute') + value; 113 | possibleSelectedDate = this.selectedToDate.clone().add(value, 'minute'); 114 | } 115 | let retValue = possibleNewValue < 60 && possibleNewValue >= 0; 116 | if (this.minDate.isValid()) { 117 | retValue = retValue && possibleSelectedDate.isSameOrAfter(this.minDate); 118 | } 119 | if (this.maxDate.isValid()) { 120 | retValue = retValue && possibleSelectedDate.isSameOrBefore(this.maxDate); 121 | } 122 | return retValue; 123 | } 124 | isValidToAddHour(value: number) { 125 | let possibleNewValue, possibleSelectedDate; 126 | if (this.isLeft) { 127 | possibleNewValue = this.selectedFromDate.get('hour') + value; 128 | possibleSelectedDate = this.selectedFromDate.clone().add(value, 'hour'); 129 | } else { 130 | possibleNewValue = this.selectedToDate.get('hour') + value; 131 | possibleSelectedDate = this.selectedToDate.clone().add(value, 'hour'); 132 | } 133 | let retValue = possibleNewValue < 24 && possibleNewValue >= 0; 134 | if (this.minDate.isValid()) { 135 | retValue = retValue && possibleSelectedDate.isSameOrAfter(this.minDate); 136 | } 137 | if (this.maxDate.isValid()) { 138 | retValue = retValue && possibleSelectedDate.isSameOrBefore(this.maxDate); 139 | } 140 | return retValue; 141 | } 142 | // #endregion 143 | 144 | // #region self event handlers 145 | addHour(value: number) { 146 | if (this.isLeft) { 147 | this.selectedFromDate = this.selectedFromDate.set( 148 | 'hour', 149 | (+this.selectedFromDate.get('hour') + value) % 24 150 | ); 151 | } else { 152 | this.selectedToDate = this.selectedToDate.set( 153 | 'hour', 154 | (+this.selectedToDate.get('hour') + value) % 24 155 | ); 156 | } 157 | this.triggerTimeChanged(); 158 | } 159 | addMinute(value: number) { 160 | if (this.isLeft) { 161 | this.selectedFromDate = this.selectedFromDate.set( 162 | 'minute', 163 | (this.selectedFromDate.get('minute') + value) % 60 164 | ); 165 | } else { 166 | this.selectedToDate = this.selectedToDate.set( 167 | 'minute', 168 | (this.selectedToDate.get('minute') + value) % 60 169 | ); 170 | } 171 | this.triggerTimeChanged(); 172 | } 173 | triggerTimeChanged() { 174 | this.isLeft 175 | ? this.timeChanged.emit(this.selectedFromDate) 176 | : this.timeChanged.emit(this.selectedToDate); 177 | } 178 | toggleAMPM() { 179 | if (this.meridiem === 'AM') { 180 | this.meridiem = 'PM'; 181 | if (this.isLeft) { 182 | this.selectedFromDate = this.selectedFromDate.set( 183 | 'hour', 184 | (this.selectedFromDate.get('hour') + 12) % 24 185 | ); 186 | } else { 187 | this.selectedToDate = this.selectedToDate.set( 188 | 'hour', 189 | (this.selectedToDate.get('hour') + 12) % 24 190 | ); 191 | } 192 | } else if (this.meridiem === 'PM') { 193 | this.meridiem = 'AM'; 194 | if (this.isLeft) { 195 | this.selectedFromDate = this.selectedFromDate.set( 196 | 'hour', 197 | (this.selectedFromDate.get('hour') - 12) % 24 198 | ); 199 | } else { 200 | this.selectedToDate = this.selectedToDate.set( 201 | 'hour', 202 | (this.selectedToDate.get('hour') - 12) % 24 203 | ); 204 | } 205 | } 206 | this.addHour(0); 207 | } 208 | // #endregion 209 | } 210 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/src/types.ts: -------------------------------------------------------------------------------- 1 | import { Dayjs } from 'dayjs'; 2 | 3 | export type Theme = 'dark' | 'light'; 4 | export type Position = 'left' | 'right' | 'center'; 5 | export class DateRange { 6 | start: Dayjs; 7 | end: Dayjs; 8 | } 9 | 10 | export class DateChanged { 11 | day: Dayjs; 12 | isLeft: boolean; 13 | } 14 | 15 | export class YearMonthChanged { 16 | value: number; 17 | isLeft: boolean; 18 | } 19 | 20 | export class MonthNameValue { 21 | name: string; 22 | value: number; 23 | } 24 | 25 | export class DefinedDateRange { 26 | name: string; 27 | value: DateRange; 28 | } 29 | 30 | export class Timepicker { 31 | minuteInterval = 1; 32 | twentyFourHourFormat = true; 33 | } 34 | 35 | export class YearsGrid { 36 | startYear: number; 37 | numberOfYearsInOneView: number; 38 | yearsPerRow: number; 39 | yearsList: number[][]; 40 | currentYear: number; 41 | } 42 | 43 | export class Options { 44 | startDate?: Dayjs = null; 45 | endDate?: Dayjs = null; 46 | minDate?: Dayjs = null; 47 | maxDate?: Dayjs = null; 48 | format?: string = 'YYYY-MM-DD'; 49 | displayFormat?: string; 50 | inactiveBeforeStart?: boolean = false; 51 | autoApply?: boolean = false; 52 | singleCalendar?: boolean = false; 53 | preDefinedRanges?: DefinedDateRange[]; 54 | noDefaultRangeSelected?: boolean = false; 55 | showRanges?: boolean = false; 56 | position?: Position = 'left'; 57 | disabled?: boolean = false; 58 | timePicker?: Timepicker = null; 59 | disableBeforeStart?: boolean = false; 60 | alwaysOpen?: boolean = false; 61 | theme?: Theme = 'light'; 62 | required?: boolean = false; 63 | DOB?: boolean = false; 64 | weekStartsOn?: number = 0; 65 | addTouchSupport?: boolean = false; 66 | placeholder?: string = ''; 67 | hideControls?: boolean = false; 68 | readOnly?: boolean = false; 69 | disableWeekEnds: boolean = false; 70 | disabledDays: number[] = null; 71 | disabledDates: Dayjs[] = null; 72 | } 73 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "skipLibCheck": true, 7 | "outDir": "../../out-tsc/lib", 8 | "target": "es2015", 9 | "module": "es2015", 10 | "moduleResolution": "node", 11 | "declaration": true, 12 | "sourceMap": true, 13 | "inlineSources": true, 14 | "emitDecoratorMetadata": true, 15 | "experimentalDecorators": true, 16 | "importHelpers": true, 17 | "removeComments": true, 18 | "types": [], 19 | "lib": [ 20 | "dom", 21 | "es2015", 22 | "es2017" 23 | ] 24 | }, 25 | "angularCompilerOptions": { 26 | "annotateForClosureCompiler": true, 27 | "skipTemplateCodegen": true, 28 | "strictMetadataEmit": true, 29 | "fullTemplateTypeCheck": true, 30 | "strictInjectionParameters": true, 31 | "enableResourceInlining": true 32 | }, 33 | "exclude": [ 34 | "src/test.ts", 35 | "**/*.spec.ts" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/angular-datetimerangepicker/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json" 3 | } 4 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 100%; 3 | margin-top: 80px; 4 | } 5 | 6 | .header { 7 | position: fixed; 8 | height: 65px; 9 | border-bottom: 1px solid black; 10 | width: 100%; 11 | z-index: 1000; 12 | background-color: white; 13 | box-shadow: 0 0 5px 5px #ddd; 14 | padding: 0 20px; 15 | } 16 | 17 | .label { 18 | color: black; 19 | margin-bottom: 0; 20 | margin-left: 4px; 21 | } 22 | 23 | @media only screen and (max-width: 767px) { 24 | .main { 25 | margin-top: 130px; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 24 |
25 |
26 |

Configuration Options Generator

27 |
28 |
29 | 30 | 32 |
33 |
34 | 35 | 37 |
38 |
39 | 40 | 42 | 43 |
44 |
45 | 48 | 50 | 51 |
52 |
53 | 54 | 56 | 57 |
58 |
59 | 60 | 62 | 63 |
64 |
65 | 67 | 69 |
70 |
71 | 73 | 75 |
76 |
77 | 78 | 80 |
81 |
82 | 84 | 85 |
86 |
87 | 89 | 90 |
91 |
92 | 93 | 94 |
95 |
96 | 97 | 98 |
99 |
100 | 102 | 103 |
104 |
105 | 107 | 108 |
109 |
110 | 111 | 112 |
113 |
114 | 115 | 116 |
117 |
118 | 119 | 120 |
121 |
122 | 124 | 126 |
127 |
128 | 130 | 131 |
132 |
133 | 134 | 135 |
136 |
137 |

Theme

138 |
139 | 140 | 141 |
142 |
143 | 144 | 145 |
146 |
147 |
148 |

Position

149 |
150 | 152 | 153 |
154 |
155 | 157 | 158 |
159 |
160 | 162 | 163 |
164 |
165 |
166 | 168 | 171 |

172 | Works only on touch devices 173 |
174 | 175 | 176 | Check Documentation for 177 | more details 178 | 179 | 180 |

181 |
182 |
183 |
184 |
185 |

Live Demo

186 |
187 |
188 | 189 | 190 |
191 |
192 |

193 | Selected Range Data 194 | Shown in UTC time 195 |

196 |
{{prettyPrintJSON(selectedRange)}}
197 |
198 |
199 |
200 |
201 |
202 |

Theming

203 | You can theme the component from here 204 |
205 | 207 | 208 |
209 |
210 | 212 | 213 |
214 |
215 | 217 | 218 |
219 |
220 | 222 | 223 |
224 |
225 | 227 | 228 |
229 |
230 | 232 | 233 |
234 |
235 | 237 | 238 |
239 |
240 | 242 | 243 |
244 |
245 |
246 |
247 |
248 | Theme Config 249 |
250 | Add these css variables to your stylesheet 251 |
{{prettyPrintJSON(themeObject)}}
252 |
253 |
254 |
255 |

Configuration

256 |
{{prettyPrintJSON(daterangepickerOptions)}}
257 |
258 |
259 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'angular-components'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app.title).toEqual('angular-components'); 23 | }); 24 | 25 | it('should render title in a h1 tag', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.debugElement.nativeElement; 29 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-components!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Options } from 'angular-datetimerangepicker/types'; 3 | declare var require: any; 4 | const dayjs = require('dayjs'); 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.css'], 10 | }) 11 | export class AppComponent implements OnInit { 12 | isTimePickerEnabled = true; 13 | format = 'DD.MM.YYYY HH:mm'; 14 | themeObject = { 15 | '--drp-bg': null, 16 | '--drp-fg': null, 17 | '--drp-hover-bg': null, 18 | '--drp-hover-fg': null, 19 | '--drp-shadow-color': null, 20 | '--drp-outline-color': null, 21 | '--drp-input-border-color': null, 22 | '--drp-input-disabled-color': null, 23 | }; 24 | initialConfigDatePickerOptions: any = { 25 | format: this.format, 26 | singleCalendar: true, 27 | displayFormat: this.format, 28 | position: 'left', 29 | noDefaultRangeSelected: true, 30 | timePicker: { 31 | minuteInterval: 1, 32 | twentyFourHourFormat: true, 33 | }, 34 | }; 35 | startDateConfigDatePickerOptions: any = { 36 | ...this.initialConfigDatePickerOptions, 37 | }; 38 | endDateConfigDatePickerOptions: any = { 39 | ...this.initialConfigDatePickerOptions, 40 | }; 41 | minDateConfigDatePickerOptions: any = { 42 | ...this.initialConfigDatePickerOptions, 43 | }; 44 | maxDateConfigDatePickerOptions: any = { 45 | ...this.initialConfigDatePickerOptions, 46 | }; 47 | daterangepickerOptions: any = { 48 | startDate: dayjs(), 49 | endDate: dayjs().add(10, 'days'), 50 | minDate: dayjs().add(-12, 'months'), 51 | maxDate: dayjs().add(12, 'months'), 52 | format: this.format, 53 | displayFormat: 'DD.MM.YYYY hh:mm a', 54 | autoApply: true, 55 | theme: 'dark', 56 | weekStartsOn: 0, 57 | placeholder: 'demo placeholder', 58 | hideControls: false, 59 | singleCalendar: false, 60 | position: 'left', 61 | required: false, 62 | readOnly: true, 63 | disabled: false, 64 | disableWeekEnds: true, 65 | disabledDays: [3], 66 | disabledDates: [ 67 | dayjs().add(10, 'day'), 68 | dayjs().add(11, 'day'), 69 | dayjs().add(12, 'day'), 70 | dayjs().add(13, 'day'), 71 | dayjs().add(14, 'day'), 72 | ], 73 | disableBeforeStart: false, 74 | inactiveBeforeStart: true, 75 | noDefaultRangeSelected: false, 76 | alwaysOpen: false, 77 | addTouchSupport: true, 78 | showRanges: true, 79 | timePicker: { 80 | minuteInterval: 5, 81 | twentyFourHourFormat: false, 82 | }, 83 | preDefinedRanges: [ 84 | { 85 | name: 'Day After tomorrow', 86 | value: { 87 | start: dayjs().add(2, 'days'), 88 | end: dayjs().add(2, 'days'), 89 | }, 90 | }, 91 | { 92 | name: 'Today', 93 | value: { 94 | start: dayjs(), 95 | end: dayjs(), 96 | }, 97 | }, 98 | { 99 | name: 'Tomorrow', 100 | value: { 101 | start: dayjs().add(1, 'days'), 102 | end: dayjs().add(1, 'days'), 103 | }, 104 | }, 105 | { 106 | name: 'This week', 107 | value: { 108 | start: dayjs(), 109 | end: dayjs().add(7, 'days'), 110 | }, 111 | }, 112 | { 113 | name: 'This month', 114 | value: { 115 | start: dayjs(), 116 | end: dayjs().add(1, 'month'), 117 | }, 118 | }, 119 | { 120 | name: 'Next 2 months', 121 | value: { 122 | start: dayjs(), 123 | end: dayjs().add(2, 'month'), 124 | }, 125 | }, 126 | ], 127 | }; 128 | selectedRange = { 129 | start: null, 130 | end: null, 131 | }; 132 | ngOnInit() { 133 | for (const prop in this.themeObject) { 134 | this.themeObject[prop] = getComputedStyle(document.documentElement) 135 | .getPropertyValue(prop) 136 | .trim(); 137 | } 138 | } 139 | rangeSelected(data) { 140 | this.selectedRange = data; 141 | } 142 | singleCalendar(event) { 143 | this.daterangepickerOptions.singleCalendar = event.target.checked; 144 | } 145 | autoApply(event) { 146 | this.daterangepickerOptions.autoApply = event.target.checked; 147 | } 148 | inactiveBeforeStart(event) { 149 | this.daterangepickerOptions.inactiveBeforeStart = event.target.checked; 150 | } 151 | showRanges(event) { 152 | this.daterangepickerOptions.showRanges = event.target.checked; 153 | } 154 | setTimePicker(event) { 155 | this.isTimePickerEnabled = event.target.checked; 156 | this.daterangepickerOptions.timePicker = event.target.checked 157 | ? { 158 | minuteInterval: 5, 159 | twentyFourHourFormat: true, 160 | } 161 | : null; 162 | } 163 | prettyPrintJSON(object) { 164 | return JSON.stringify(object, null, 2); 165 | } 166 | colorChange(e) { 167 | this.daterangepickerOptions.theme = 'light'; 168 | this.themeObject[e.target.dataset.cssPropName] = e.target.value; 169 | document.documentElement.style.setProperty( 170 | e.target.dataset.cssPropName, 171 | e.target.value 172 | ); 173 | } 174 | optionChanged(propValue, event) { 175 | this.daterangepickerOptions = { 176 | ...this.daterangepickerOptions, 177 | [propValue]: event.start 178 | ? event.start.format(this.format) 179 | : event.target.checked, 180 | }; 181 | if (['minDate', 'maxDate'].includes(propValue)) { 182 | this.endDateConfigDatePickerOptions = { 183 | ...this.endDateConfigDatePickerOptions, 184 | noDefaultRangeSelected: false, 185 | minDate: this.daterangepickerOptions.minDate, 186 | maxDate: this.daterangepickerOptions.maxDate, 187 | }; 188 | this.startDateConfigDatePickerOptions = { 189 | ...this.startDateConfigDatePickerOptions, 190 | noDefaultRangeSelected: false, 191 | minDate: this.daterangepickerOptions.minDate, 192 | maxDate: this.daterangepickerOptions.maxDate, 193 | }; 194 | } 195 | } 196 | formatChanged(propValue, event) { 197 | this.daterangepickerOptions = { 198 | ...this.daterangepickerOptions, 199 | [propValue]: event.target.value, 200 | }; 201 | } 202 | disabledDaysChanged(event) { 203 | this.daterangepickerOptions.disabledDays = event.target.value 204 | ? event.target.value.split(',').map((x) => +x) 205 | : null; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { DatetimerangepickerModule } from 'angular-datetimerangepicker'; 4 | import { AppComponent } from './app.component'; 5 | import { FormsModule } from '@angular/forms'; 6 | 7 | @NgModule({ 8 | declarations: [AppComponent], 9 | imports: [BrowserModule, DatetimerangepickerModule, FormsModule], 10 | bootstrap: [AppComponent], 11 | }) 12 | export class AppModule {} 13 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technikhil314/angular-components/6ce3bdf705bd611d965273b4c6f1f3d21b338964/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technikhil314/angular-components/6ce3bdf705bd611d965273b4c6f1f3d21b338964/src/assets/180x180.png -------------------------------------------------------------------------------- /src/assets/192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technikhil314/angular-components/6ce3bdf705bd611d965273b4c6f1f3d21b338964/src/assets/192x192.png -------------------------------------------------------------------------------- /src/assets/brand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technikhil314/angular-components/6ce3bdf705bd611d965273b4c6f1f3d21b338964/src/assets/brand.png -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Angular Date time range picker | technikhil314 8 | 9 | 10 | 11 | 12 | 13 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 37 | 38 | 39 | 40 | 41 | 44 | 47 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | 14 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --drp-input-height: calc(1.5em + 0.75rem + 2px); 3 | } 4 | 5 | .label { 6 | font-weight: 500; 7 | } 8 | 9 | .configuration { 10 | max-height: calc(100vh - 80px); 11 | overflow-y: auto; 12 | } 13 | 14 | .configDatePicker { 15 | --drp-input-height: 33px; 16 | --drp-input-border-radius: 4px; 17 | --drp-bg: hsla(0, 0%, 98%); 18 | --drp-fg: hsla(0, 0%, 20%); 19 | --drp-shadow-color: rgba(0, 0, 0, 0.2); 20 | --drp-hover-bg: hsla(0, 0%, 80%); 21 | --drp-outline-color: hsl(240deg, 50%, 30%); 22 | --drp-input-border-color: #666; 23 | --drp-input-disabled-color: #dedede; 24 | } 25 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "baseUrl": "./", 7 | "outDir": "./dist/out-tsc", 8 | "sourceMap": true, 9 | "declaration": false, 10 | "module": "es2015", 11 | "moduleResolution": "node", 12 | "emitDecoratorMetadata": true, 13 | "experimentalDecorators": true, 14 | "removeComments": true, 15 | "skipLibCheck": true, 16 | "target": "es5", 17 | "typeRoots": [ 18 | "node_modules/@types" 19 | ], 20 | "lib": [ 21 | "es2017", 22 | "dom" 23 | ], 24 | "paths": { 25 | "angular-datetimerangepicker": [ 26 | "dist/angular-datetimerangepicker" 27 | ], 28 | "angular-datetimerangepicker/*": [ 29 | "dist/angular-datetimerangepicker/*" 30 | ] 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "import-blacklist": [ 19 | true, 20 | "rxjs/Rx" 21 | ], 22 | "import-spacing": true, 23 | "indent": [ 24 | true, 25 | "spaces" 26 | ], 27 | "interface-over-type-literal": true, 28 | "label-position": true, 29 | "member-access": false, 30 | "member-ordering": [ 31 | true, 32 | { 33 | "order": [ 34 | "static-field", 35 | "instance-field", 36 | "static-method", 37 | "instance-method" 38 | ] 39 | } 40 | ], 41 | "no-arg": true, 42 | "no-console": [ 43 | true, 44 | "debug", 45 | "info", 46 | "time", 47 | "timeEnd", 48 | "trace" 49 | ], 50 | "no-construct": true, 51 | "no-debugger": true, 52 | "no-duplicate-super": true, 53 | "no-empty": false, 54 | "no-empty-interface": true, 55 | "no-eval": true, 56 | "no-misused-new": true, 57 | "no-non-null-assertion": true, 58 | "no-redundant-jsdoc": true, 59 | "no-shadowed-variable": true, 60 | "no-string-literal": false, 61 | "no-string-throw": true, 62 | "no-switch-case-fall-through": true, 63 | "no-trailing-whitespace": true, 64 | "no-unnecessary-initializer": true, 65 | "no-unused-expression": true, 66 | "no-use-before-declare": true, 67 | "no-var-keyword": true, 68 | "object-literal-sort-keys": false, 69 | "one-line": [ 70 | true, 71 | "check-open-brace", 72 | "check-catch", 73 | "check-else", 74 | "check-whitespace" 75 | ], 76 | "prefer-const": true, 77 | "quotemark": [ 78 | true, 79 | "single" 80 | ], 81 | "radix": true, 82 | "semicolon": [ 83 | true, 84 | "always" 85 | ], 86 | "triple-equals": [ 87 | true, 88 | "allow-null-check" 89 | ], 90 | "typedef-whitespace": [ 91 | true, 92 | { 93 | "call-signature": "nospace", 94 | "index-signature": "nospace", 95 | "parameter": "nospace", 96 | "property-declaration": "nospace", 97 | "variable-declaration": "nospace" 98 | } 99 | ], 100 | "unified-signatures": true, 101 | "variable-name": false, 102 | "whitespace": [ 103 | true, 104 | "check-branch", 105 | "check-decl", 106 | "check-operator", 107 | "check-separator", 108 | "check-type" 109 | ], 110 | "no-output-on-prefix": true, 111 | "use-input-property-decorator": true, 112 | "use-output-property-decorator": true, 113 | "use-host-property-decorator": true, 114 | "no-input-rename": true, 115 | "no-output-rename": true, 116 | "use-life-cycle-interface": true, 117 | "use-pipe-transform-interface": true, 118 | "directive-class-suffix": true 119 | } 120 | } 121 | --------------------------------------------------------------------------------