├── .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": "[](#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 |
19 |
20 | |
21 |
22 |
23 | |
24 |
25 |
26 |
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 | [](https://github.com/technikhil314/angular-components/actions)
6 | [](https://npmjs.org/angular-datetimerangepicker)
7 | [](https://bundlephobia.com/result?p=angular-datetimerangepicker)
8 | [](https://bundlephobia.com/result?p=angular-datetimerangepicker)
9 | [](https://github.com/technikhil314/angular-components/blob/master/LICENSE)
10 |
11 | [](https://libraries.io/npm/angular-datetimerangepicker)
12 | [](https://npmcharts.com/compare/angular-datetimerangepicker)
13 | [](https://github.com/technikhil314/angular-components/graphs/contributors)
14 | [](https://github.com/technikhil314/angular-components/blob/master/CODE_OF_CONDUCT.md)
15 |
16 | [](https://github.com/technikhil314/angular-components/stargazers)
17 | [](https://github.com/technikhil314/angular-components/fork)
18 | [](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 |
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 |
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 |
183 |
184 |
185 |
Live Demo
186 |
187 |
191 |
192 |
193 | Selected Range Data
194 | Shown in UTC time
195 |
196 |
{{prettyPrintJSON(selectedRange)}}
197 |
198 |
199 |
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 |
--------------------------------------------------------------------------------