├── .editorconfig
├── .gitignore
├── .npmignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── angular.json
├── browserslist
├── demo
├── .browserslistrc
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── e2e
│ ├── app.e2e-spec.ts
│ ├── app.po.ts
│ └── tsconfig.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
│ ├── app
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ └── app.module.ts
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.css
│ ├── test.ts
│ └── tsconfig.json
└── tslint.json
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
├── app.module.ts
├── assets
│ ├── .gitkeep
│ ├── favicon.ico
│ └── svg
│ │ ├── calendar-clock.svg
│ │ ├── calendar.svg
│ │ └── clock.svg
├── components
│ ├── date
│ │ ├── date.component.html
│ │ ├── date.component.spec.ts
│ │ └── date.component.ts
│ ├── datePicker
│ │ ├── datePicker.component.html
│ │ ├── datePicker.component.spec.ts
│ │ └── datePicker.component.ts
│ ├── dateTimePicker
│ │ ├── dateTimePicker.component.html
│ │ ├── dateTimePicker.component.spec.ts
│ │ └── dateTimePicker.component.ts
│ ├── time
│ │ ├── time.component.html
│ │ ├── time.component.spec.ts
│ │ └── time.component.ts
│ └── timePicker
│ │ ├── timePicker.component.html
│ │ ├── timePicker.component.spec.ts
│ │ └── timePicker.component.ts
├── index.ts
├── models
│ ├── dayOfTheMonth.interface.ts
│ └── styleObject.model.ts
├── polyfills.ts
├── scss
│ ├── _button.scss
│ ├── _custom.scss
│ ├── _date.scss
│ ├── _picker.scss
│ ├── _time.scss
│ ├── _variables.scss
│ └── date.component.scss
├── services
│ ├── date.service.spec.ts
│ ├── date.service.ts
│ ├── isMobile.service.ts
│ └── renderer.service.ts
├── test.ts
└── typings
│ └── isMobile.d.ts
├── tsconfig.json
└── tslint.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 4
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.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 | **/node_modules
11 |
12 | # IDEs and editors
13 | .idea
14 | .project
15 | .classpath
16 | .c9/
17 | *.launch
18 | .settings/
19 | *.sublime-workspace
20 | out/
21 | *.iws
22 |
23 |
24 | # IDE - VSCode
25 | .vscode/*
26 | !.vscode/settings.json
27 | !.vscode/tasks.json
28 | !.vscode/launch.json
29 | !.vscode/extensions.json
30 |
31 | # IDE - Visual Studio
32 | .vs/*
33 |
34 | # misc
35 | /.sass-cache
36 | /connect.lock
37 | /coverage
38 | /libpeerconnection.log
39 | npm-debug.log
40 | testem.log
41 | /typings
42 |
43 | # e2e
44 | /e2e/*.js
45 | /e2e/*.map
46 |
47 | # System Files
48 | .DS_Store
49 | Thumbs.db
50 |
51 | .env
52 |
53 | *.js
54 | *.js.map
55 | *.log
56 | demo/app/*.js
57 | demo/*.d.ts
58 | demo/node_modules
59 | !demo/*.js
60 | **/*.js
61 | **/*.d.ts
62 | !*.js
63 | bash.exe.stackdump
64 | package-lock.json
65 |
66 | !/dist/package.json
67 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | ngx-datetimepicker/node_modules/
2 | ngx-datetimepicker/src/
3 | ngx-datetimepicker/e2e/**
4 | .vscode
5 | .gitignore
6 | !ngx-datetimepicker/src/app/
7 | ngx-datetimepicker/src/app/app.component.*
8 | ngx-datetimepicker/src/app/app.module.*
9 | ngx-datetimepicker/src/app/**.spec.*
10 | ngx-datetimepicker/src/**.*
11 | ngx-datetimepicker/**.*
12 | ngx-datetimepicker/src/typings/
13 | ngx-datetimepicker/src/environments/
14 | !ngx-datetimepicker/src/assets/
15 | ngx-datetimepicker/src/**.json
16 | ngx-datetimepicker/src/**.md
17 | demo
18 | scss
19 | tsconfig.json
20 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.trimTrailingWhitespace": true,
4 | "editor.tabSize": 4,
5 | "editor.formatOnType": true,
6 | "files.exclude": {
7 | "**/.git": true,
8 | "**/.DS_Store": true,
9 | "**/tests/": true,
10 | "ngx-datetimepicker/src/**/*.js": true,
11 | "ngx-datetimepicker/src/**/*.js.map": true,
12 | "ngx-datetimepicker/src/**/*.d.ts": true
13 | }
14 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Renovo Solutions
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 | > [!NOTE]
2 | > This project is no longer actively maintained.
3 | >
4 | > We want to thank everyone for their contributions and hope we've helped you in your coding journey.
5 |
6 | # ngx-datetime-picker
7 |
8 | - No jQuery or other external dependencies
9 | - Three separate components for Date Time, Date, and Time picker
10 | - When being used on a mobile or touch devices, it falls back to HTML5 inputs of date, datetime-local, and time
11 |
12 | [](https://www.npmjs.com/package/ngx-datetime-picker)
13 | [](https://www.npmjs.com/package/ngx-datetime-picker)
14 |
15 | ## Demo
16 |
17 | Live demo can be found at .
18 |
19 |
20 |
21 |
22 |
23 |
24 | Date time picker that falls back to native HTML5 components on mobile
25 |
26 |
27 |
28 | ## DateTime formats
29 |
30 | Date formats are called at `datePicker.component.ts` - functions `formattedDate()` and `mobileFormattedDate()`.
31 |
32 | Time formats are called at `timePicker.component.ts` - functions `formattedTime()` and `mobileFormattedTime()`.
33 |
34 | DateTime formats are called at `dateTimePicker.component.ts` - functions `formattedDate()` and `mobileFormattedDate()`.
35 |
36 | Change these calls to a different format to get different results.
37 | All formats are defined in [`date.service.ts`](/src/services/date.service.ts).
38 |
39 | ## Usage
40 |
41 | ```typescript
42 | import { DateTimePickerModule} from 'ngx-datetime-picker';
43 |
44 | @NgModule({
45 | imports: [
46 | DateTimePickerModule
47 | ],
48 | ```
49 |
50 | ```html
51 |
52 |
53 |
54 |
55 |
56 | ```
57 |
58 | Additional options for each picker:
59 |
60 | - `[disableButton]="false" (default)`
61 | - `[disableInput]="false" (default)`
62 | - `[disablePicker]="false" (default)`
63 | - `[doNotCloseOnDateSet]="false" (default)`
64 |
65 | Additional options for `ngx-date-picker` and `ngx-datetime-picker`:
66 |
67 | - `[min]="null" (default)`
68 | - `[max]="null" (default)`
69 |
70 | Additional options for `ngx-time-picker` and `ngx-datetime-picker`:
71 |
72 | - `[use24HourClock]="false" (default)`
73 |
74 | ## CSS
75 |
76 | Compile `ngx-datetime-picker.css` from SCSS and copy into to your project.
77 |
78 | If you are using angular-cli, the css can be added to your `angular-cli.json`:
79 |
80 | ```typescript
81 | "styles": [
82 | "../node_modules/ngx-datetime-picker/ngx-datetime-picker.css"
83 | ]
84 | ```
85 |
86 | [SCSS files](/src/scss/) available for quick customization. Override the defaults, compile, and include them in your project.
87 |
88 | ## Development:
89 |
90 | ### Work flow
91 |
92 | - Clone repository to your machine.
93 | - Run `npm run setup` to prepare the project.
94 | - Live edit mode with `npm run serve`.
95 | - Run tests with watcher with `npm run test`.
96 | - Run tests without a watcher with `npm run test.once`.
97 | - Prepare for distribution with `npm run dist`. (Note: you may need to also run `tsc index.ts` if you have updated the exported values)
98 | - Test a demo project using the exported ngModule with `npm run demo`.
99 | - Sass files are compiled locally using [Koala](http://koala-app.com/).
100 | - Set Koala to watch `date.component.scss` and compile it to `ngx-datetimepicker > src > assets` as `ngx-datetime-picker.css`.
101 |
102 | - _Optionally_, you can use to publish the demo install to github pages. First run `npm run demo`, then switch into your demo directory with `cd demo`. From there, you can copy and paste the two commands to publish to github pages:
103 |
104 | ```shell
105 | ng build --prod --aot --base-href "https://renovosolutions.github.io/ngx-datetimepicker/"
106 |
107 | ngh --repo=https://github.com/renovosolutions/ngx-datetimepicker.git
108 | ```
109 |
110 | _Note: this will publish to the `gh-pages` branch and you wil need to authenticate again._
111 |
112 | ### Requirements
113 |
114 | - angular-cli 12.2.16 or higher, known issues with version 13
115 | - node 10 or higher
116 |
117 | ### Contributors
118 |
119 | | [
](https://github.com/JoshDSommer) | [
](https://github.com/BrentWMiller) | [
](https://github.com/benjamin-a-kuntz) | [
](https://github.com/SFarageNIS) | [
](https://github.com/TheLand) | [
](https://github.com/JojiePalahang) | [
](https://github.com/SamGraber) | [
](https://github.com/alecrem) | [
](https://github.com/jrquiick17) |
120 | | :-----------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------: |
121 | | [Josh Sommer](https://github.com/JoshDSommer) | [Brent Miller](https://github.com/BrentWMiller) | [Benjamin Kuntz](https://github.com/benjamin-a-kuntz) | [Steven Farage](https://github.com/SFarageNIS) | [Marco Mantovani](https://github.com/TheLand) | [Jojie Palahang](https://github.com/JojiePalahang) | [Sam Graber](https://github.com/SamGraber) | [Alejandro Cremades](https://github.com/alecrem) | [Jeremy Quick](https://github.com/jrquick17) |
122 | | | | | | | | | | [Personal](https://jrquick.com) |
123 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "cli": {
4 | "analytics": false
5 | },
6 | "version": 1,
7 | "newProjectRoot": "projects",
8 | "projects": {
9 | "ngx-datetimepicker": {
10 | "root": "",
11 | "sourceRoot": "src",
12 | "projectType": "application",
13 | "architect": {
14 | "build": {
15 | "builder": "@angular-devkit/build-angular:browser",
16 | "options": {
17 | "outputPath": "dist/",
18 | "index": "src/index.html",
19 | "main": "src/main.ts",
20 | "tsConfig": "src/tsconfig.json",
21 | "polyfills": "src/polyfills.ts",
22 | "assets": [
23 | "src/assets",
24 | "src/favicon.ico"
25 | ],
26 | "styles": [
27 | "scss/date.component.scss"
28 | ],
29 | "scripts": []
30 | },
31 | "configurations": {
32 | "production": {
33 | "optimization": true,
34 | "outputHashing": "all",
35 | "sourceMap": false,
36 | "extractCss": true,
37 | "namedChunks": false,
38 | "aot": true,
39 | "extractLicenses": true,
40 | "vendorChunk": false,
41 | "buildOptimizer": true,
42 | "fileReplacements": [
43 | {
44 | "replace": "src/environments/environment.ts",
45 | "with": "src/environments/environment.prod.ts"
46 | }
47 | ]
48 | }
49 | }
50 | },
51 | "serve": {
52 | "builder": "@angular-devkit/build-angular:dev-server",
53 | "options": {
54 | "browserTarget": "ngx-datetimepicker:build"
55 | },
56 | "configurations": {
57 | "production": {
58 | "browserTarget": "ngx-datetimepicker:build:production"
59 | }
60 | }
61 | },
62 | "extract-i18n": {
63 | "builder": "@angular-devkit/build-angular:extract-i18n",
64 | "options": {
65 | "browserTarget": "ngx-datetimepicker:build"
66 | }
67 | },
68 | "test": {
69 | "builder": "@angular-devkit/build-angular:karma",
70 | "options": {
71 | "codeCoverage": true,
72 | "main": "src/test.ts",
73 | "karmaConfig": "./karma.conf.js",
74 | "polyfills": "src/polyfills.ts",
75 | "tsConfig": "./tsconfig.json",
76 | "scripts": [],
77 | "styles": [
78 | "src/scss/date.component.scss"
79 | ],
80 | "assets": [
81 | "src/assets",
82 | "src/favicon.ico"
83 | ]
84 | }
85 | },
86 | "lint": {
87 | "builder": "@angular-devkit/build-angular:tslint",
88 | "options": {
89 | "tsConfig": [
90 | "src/tsconfig.json"
91 | ],
92 | "exclude": []
93 | }
94 | }
95 | }
96 | },
97 | "ngx-datetimepicker-e2e": {
98 | "root": "e2e",
99 | "sourceRoot": "e2e",
100 | "projectType": "application",
101 | "architect": {
102 | "e2e": {
103 | "builder": "@angular-devkit/build-angular:protractor",
104 | "options": {
105 | "protractorConfig": "./protractor.conf.js",
106 | "devServerTarget": "ngx-datetimepicker:serve"
107 | }
108 | },
109 | "lint": {
110 | "builder": "@angular-devkit/build-angular:tslint",
111 | "options": {
112 | "tsConfig": [
113 | "e2e/tsconfig.json"
114 | ],
115 | "exclude": []
116 | }
117 | }
118 | }
119 | }
120 | },
121 | "defaultProject": "ngx-datetimepicker",
122 | "schematics": {
123 | "@schematics/angular:component": {
124 | "prefix": "app",
125 | "styleext": "css"
126 | },
127 | "@schematics/angular:directive": {
128 | "prefix": "app"
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/demo/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/demo/.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 |
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 |
10 | # IDEs and editors
11 | /.idea
12 | .project
13 | .classpath
14 | .c9/
15 | *.launch
16 | .settings/
17 |
18 | # IDE - VSCode
19 | .vscode/*
20 | !.vscode/settings.json
21 | !.vscode/tasks.json
22 | !.vscode/launch.json
23 | !.vscode/extensions.json
24 |
25 | # misc
26 | /.sass-cache
27 | /connect.lock
28 | /coverage/*
29 | /libpeerconnection.log
30 | npm-debug.log
31 | testem.log
32 | /typings
33 |
34 | # e2e
35 | /e2e/*.js
36 | /e2e/*.map
37 |
38 | #System Files
39 | .DS_Store
40 | Thumbs.db
41 |
--------------------------------------------------------------------------------
/demo/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 | This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 12.2.16.
3 |
4 | ## Development server
5 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
6 |
7 | ## Code scaffolding
8 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
9 |
10 | ## Build
11 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
12 |
13 | ## Running unit tests
14 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
15 |
16 | ## Running end-to-end tests
17 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
18 | Before running the tests make sure you are serving the app via `ng serve`.
19 |
20 | ## Deploying to GitHub Pages
21 | Run `ng github-pages:deploy` to deploy to GitHub Pages.
22 |
23 | ## DateTime formats
24 | Date formats are called at datePicker.component.ts - functions formattedDate() and mobileFormattedDate().
25 | Time formats are called at timePicker.component.ts - functions formattedTime() and mobileFormattedTime().
26 | DateTime formats are called at dateTimePicker.component.ts - functions formattedDate() and mobileFormattedDate().
27 | Change these calls to a different format to get different results.
28 | All formats are defined in date.service.ts.
29 |
30 | ## Further help
31 | To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
32 |
--------------------------------------------------------------------------------
/demo/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "demo": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "architect": {
11 | "build": {
12 | "builder": "@angular-devkit/build-angular:browser",
13 | "options": {
14 | "outputPath": "dist",
15 | "index": "src/index.html",
16 | "main": "src/main.ts",
17 | "tsConfig": "src/tsconfig.json",
18 | "polyfills": "src/polyfills.ts",
19 | "assets": [
20 | "src/assets",
21 | "src/favicon.ico"
22 | ],
23 | "styles": [
24 | "node_modules/ngx-datetime-picker/ngx-datetime-picker.css",
25 | "src/styles.css"
26 | ],
27 | "scripts": [],
28 | "vendorChunk": true,
29 | "extractLicenses": false,
30 | "buildOptimizer": false,
31 | "sourceMap": true,
32 | "optimization": false,
33 | "namedChunks": true
34 | },
35 | "configurations": {
36 | "production": {
37 | "budgets": [
38 | {
39 | "type": "anyComponentStyle",
40 | "maximumWarning": "6kb"
41 | }
42 | ],
43 | "optimization": true,
44 | "outputHashing": "all",
45 | "sourceMap": false,
46 | "namedChunks": false,
47 | "extractLicenses": true,
48 | "vendorChunk": false,
49 | "buildOptimizer": true,
50 | "fileReplacements": [
51 | {
52 | "replace": "src/environments/environment.ts",
53 | "with": "src/environments/environment.prod.ts"
54 | }
55 | ]
56 | }
57 | },
58 | "defaultConfiguration": ""
59 | },
60 | "serve": {
61 | "builder": "@angular-devkit/build-angular:dev-server",
62 | "options": {
63 | "browserTarget": "demo:build"
64 | },
65 | "configurations": {
66 | "production": {
67 | "browserTarget": "demo:build:production"
68 | }
69 | }
70 | },
71 | "extract-i18n": {
72 | "builder": "@angular-devkit/build-angular:extract-i18n",
73 | "options": {
74 | "browserTarget": "demo:build"
75 | }
76 | },
77 | "test": {
78 | "builder": "@angular-devkit/build-angular:karma",
79 | "options": {
80 | "codeCoverage": true,
81 | "main": "src/test.ts",
82 | "karmaConfig": "./karma.conf.js",
83 | "polyfills": "src/polyfills.ts",
84 | "tsConfig": "src/tsconfig.json",
85 | "scripts": [],
86 | "styles": [
87 | "node_modules/ngx-datetime-picker/ngx-datetime-picker.css",
88 | "src/styles.css"
89 | ],
90 | "assets": [
91 | "src/assets",
92 | "src/favicon.ico"
93 | ]
94 | }
95 | },
96 | "lint": {
97 | "builder": "@angular-devkit/build-angular:tslint",
98 | "options": {
99 | "tsConfig": [
100 | "src/tsconfig.json"
101 | ],
102 | "exclude": []
103 | }
104 | }
105 | }
106 | },
107 | "demo-e2e": {
108 | "root": "e2e",
109 | "sourceRoot": "e2e",
110 | "projectType": "application",
111 | "architect": {
112 | "e2e": {
113 | "builder": "@angular-devkit/build-angular:protractor",
114 | "options": {
115 | "protractorConfig": "./protractor.conf.js",
116 | "devServerTarget": "demo:serve"
117 | }
118 | },
119 | "lint": {
120 | "builder": "@angular-devkit/build-angular:tslint",
121 | "options": {
122 | "tsConfig": [
123 | "e2e/tsconfig.json"
124 | ],
125 | "exclude": []
126 | }
127 | }
128 | }
129 | }
130 | },
131 | "defaultProject": "demo",
132 | "schematics": {
133 | "@schematics/angular:component": {
134 | "prefix": "app",
135 | "style": "css"
136 | },
137 | "@schematics/angular:directive": {
138 | "prefix": "app"
139 | }
140 | },
141 | "cli": {
142 | "analytics": false
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/demo/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { DemoPage } from './app.po';
2 |
3 | describe('demo App', function () {
4 | let page: DemoPage;
5 |
6 | beforeEach(() => {
7 | page = new DemoPage();
8 | });
9 |
10 | it('should display message saying ngx-datetime-picker', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('ngx-datetime-picker');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/demo/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, element, by } from 'protractor';
2 |
3 | export class DemoPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/demo/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "declaration": false,
5 | "experimentalDecorators": true,
6 | "module": "commonjs",
7 | "moduleResolution": "node",
8 | "outDir": "../dist/out-tsc-e2e",
9 | "sourceMap": true,
10 | "target": "es5",
11 | "typeRoots": [
12 | "../node_modules/@types"
13 | ]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/demo/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/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'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | mime: {
19 | 'text/x-typescript': ['ts', 'tsx']
20 | },
21 | coverageReporter: {
22 | reporters: [
23 | { type: 'html' },
24 | { type: 'text-summary' }
25 | ],
26 | check: {
27 | global: {
28 | statements: 50,
29 | branches: 50,
30 | functions: 50,
31 | lines: 50
32 | }
33 | }
34 | },
35 |
36 | reporters: config.angularCli && config.angularCli.codeCoverage
37 | ? ['progress', 'kjhtml', 'coverage']
38 | : ['progress', 'kjhtml'],
39 | port: 9876,
40 | colors: true,
41 | logLevel: config.LOG_INFO,
42 | autoWatch: true,
43 | browsers: ['Chrome'],
44 | singleRun: false
45 | });
46 | };
47 |
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "angular-cli": {},
6 | "scripts": {
7 | "ng": "ng",
8 | "start": "ng serve",
9 | "test": "ng test",
10 | "pree2e": "webdriver-manager update --standalone false --gecko false",
11 | "e2e": "protractor"
12 | },
13 | "private": true,
14 | "dependencies": {
15 | "@angular/common": "12.x",
16 | "@angular/compiler": "12.x",
17 | "@angular/core": "12.x",
18 | "@angular/forms": "12.x",
19 | "@angular/platform-browser": "12.x",
20 | "@angular/platform-browser-dynamic": "12.x",
21 | "@angular/router": "12.x",
22 | "core-js": "3.x",
23 | "ngx-datetime-picker": "2.x",
24 | "rxjs": "6.x",
25 | "rxjs-compat": "6.x",
26 | "tslib": "2.x",
27 | "zone.js": "0.11.x"
28 | },
29 | "devDependencies": {
30 | "@angular-devkit/build-angular": "12.x",
31 | "@angular/cli": "12.x",
32 | "@angular/compiler-cli": "12.x",
33 | "@types/jasmine": "3.x",
34 | "@types/node": "12.x",
35 | "jasmine-core": "3.x",
36 | "jasmine-spec-reporter": "5.x",
37 | "karma": "6.x",
38 | "karma-chrome-launcher": "3.x",
39 | "karma-cli": "2.x",
40 | "karma-jasmine": "4.x",
41 | "protractor": "7.x",
42 | "ts-node": "8.x",
43 | "tslint": "6.x",
44 | "typescript": "4.3.x"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/demo/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 | /*global jasmine */
5 | var SpecReporter = require('jasmine-spec-reporter');
6 |
7 | exports.config = {
8 | allScriptsTimeout: 11000,
9 | specs: [
10 | './e2e/**/*.e2e-spec.ts'
11 | ],
12 | capabilities: {
13 | 'browserName': 'chrome'
14 | },
15 | directConnect: true,
16 | baseUrl: 'http://localhost:4200/',
17 | framework: 'jasmine',
18 | jasmineNodeOpts: {
19 | showColors: true,
20 | defaultTimeoutInterval: 30000,
21 | print: function () { }
22 | },
23 | useAllAngular2AppRoots: true,
24 | beforeLaunch: function () {
25 | require('ts-node').register({
26 | project: 'e2e'
27 | });
28 | },
29 | onPrepare: function () {
30 | jasmine.getEnv().addReporter(new SpecReporter());
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/demo/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RenovoSolutions/ngx-datetimepicker/d863814e5d73781a9ec19edb2a01b6926c5c9612/demo/src/app/app.component.css
--------------------------------------------------------------------------------
/demo/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
{{ dateTimeExample }}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
{{ dateExample }}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
{{ timeExample }}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/demo/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { DateService } from 'ngx-datetime-picker';
3 | import { TestBed, async } from '@angular/core/testing';
4 | import { AppComponent } from './app.component';
5 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
6 |
7 |
8 | describe('AppComponent', () => {
9 | beforeEach(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [
12 | AppComponent
13 | ],
14 | providers: [
15 | DateService
16 | ],
17 | schemas: [
18 | CUSTOM_ELEMENTS_SCHEMA
19 | ]
20 | });
21 | TestBed.compileComponents();
22 | });
23 |
24 | it('should create the app', async(() => {
25 | const fixture = TestBed.createComponent(AppComponent);
26 | const app = fixture.debugElement.componentInstance;
27 | expect(app).toBeTruthy();
28 | }));
29 |
30 | it('should have as title ngx-datetime-picker', async(() => {
31 | const fixture = TestBed.createComponent(AppComponent);
32 | const app = fixture.debugElement.componentInstance;
33 | expect(app.title).toEqual('ngx-datetime-picker');
34 | }));
35 |
36 | it('should render the content of app.component.html', async(() => {
37 | const fixture = TestBed.createComponent(AppComponent);
38 | fixture.detectChanges();
39 | const compiled = fixture.debugElement.nativeElement;
40 | expect(compiled.querySelector('div').textContent).toContain('Date & Time Picker');
41 | }));
42 | });
43 |
--------------------------------------------------------------------------------
/demo/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { DateService } from 'ngx-datetime-picker';
3 |
4 | @Component({
5 | selector: 'app-root',
6 | templateUrl: './app.component.html',
7 | styleUrls: ['./app.component.css']
8 | })
9 |
10 | export class AppComponent {
11 | public dateTimeExample = null;
12 | public dateExample = null;
13 | public timeExample = null;
14 |
15 | title = 'ngx-datetime-picker';
16 |
17 | constructor(private dateService: DateService) { }
18 |
19 | setToNow(): void {
20 | const now = new Date();
21 |
22 | this.dateExample = now;
23 | this.timeExample = now.toLocaleTimeString([], { hour: '2-digit', minute: "2-digit", second: "2-digit", hour12: false });
24 | this.dateTimeExample = now;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/demo/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { AppComponent } from './app.component';
5 | import { DateService, DateTimePickerModule } from 'ngx-datetime-picker';
6 |
7 | @NgModule({
8 | declarations: [
9 | AppComponent
10 | ],
11 | imports: [
12 | BrowserModule,
13 | FormsModule,
14 | DateTimePickerModule
15 | ],
16 | providers: [
17 | DateService
18 | ],
19 | bootstrap: [
20 | AppComponent
21 | ]
22 | })
23 | export class AppModule { }
24 |
--------------------------------------------------------------------------------
/demo/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/demo/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/demo/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RenovoSolutions/ngx-datetimepicker/d863814e5d73781a9ec19edb2a01b6926c5c9612/demo/src/favicon.ico
--------------------------------------------------------------------------------
/demo/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
18 | Loading...
19 |
20 |
21 |
--------------------------------------------------------------------------------
/demo/src/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 | import { enableProdMode } from '@angular/core';
3 | import { environment } from './environments/environment';
4 | import { AppModule } from './app/app.module';
5 |
6 | import 'zone.js';
7 |
8 | if (environment.production) {
9 | enableProdMode();
10 | }
11 |
12 | platformBrowserDynamic().bootstrapModule(AppModule).then();
13 |
--------------------------------------------------------------------------------
/demo/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | // This file includes polyfills needed by Angular and is loaded before the app.
2 | // You can add your own extra polyfills to this file.
3 | import 'core-js/es/symbol';
4 | import 'core-js/es/object';
5 | import 'core-js/es/function';
6 | import 'core-js/es/parse-int';
7 | import 'core-js/es/parse-float';
8 | import 'core-js/es/number';
9 | import 'core-js/es/math';
10 | import 'core-js/es/string';
11 | import 'core-js/es/date';
12 | import 'core-js/es/array';
13 | import 'core-js/es/regexp';
14 | import 'core-js/es/map';
15 | import 'core-js/es/set';
16 | import 'core-js/es/reflect';
17 |
18 | // If you need to support the browsers/features below, uncomment the import
19 | // and run `npm install import-name-here';
20 | // Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
21 |
22 | // Needed for: IE9
23 | // import 'classlist.js';
24 |
25 | // Animations
26 | // Needed for: All but Chrome and Firefox, Not supported in IE9
27 | // import 'web-animations-js';
28 |
29 | // Date, currency, decimal and percent pipes
30 | // Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
31 | // import 'intl';
32 |
33 | // NgClass on SVG elements
34 | // Needed for: IE10, IE11
35 | // import 'classlist.js';
36 |
--------------------------------------------------------------------------------
/demo/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
3 | body {
4 | margin: 0;
5 | font-family: Arial, Helvetica, sans-serif;
6 | font-size: 16px;
7 | }
8 |
9 | .header {
10 | width: 100%;
11 | padding: 1em;
12 | margin-bottom: .5em;
13 | font-size: 2em;
14 | color: #fff;
15 | background-color: #222651;
16 | }
17 |
18 | .header--subtitle a {
19 | font-size: .5em;
20 | color: #f41875;
21 | text-decoration: none;
22 | }
23 |
24 | .header--subtitle a:hover {
25 | transition: .25s ease;
26 | color: #ff6fac;
27 | }
28 |
29 | .demo {
30 | padding: 1em 2em;
31 | }
32 |
33 | .demo-section {
34 | position: relative;
35 | margin-bottom: 2.5em;
36 | }
37 |
38 | .demo-section--label {
39 | display: inline-block;
40 | margin: 1.5em 0 .5em 0;
41 | font-size: 1.25em;
42 | }
43 |
44 | .demo-section--string {
45 | position: absolute;
46 | margin-top: .5em;
47 | padding: .5em;
48 | background-color: #f1f1f1;
49 | transition: .25s ease;
50 | }
51 |
52 | .demo-buttons {
53 | padding-top: 2em;
54 | }
55 |
56 | .btn-demo {
57 | padding: 1em 1.5em;
58 | cursor: pointer;
59 | background-color: #fff;
60 | border: .2em solid #222651;
61 | border-radius: .2em;
62 | color: #222651;
63 | font-weight: bold;
64 | }
65 |
66 | .btn-demo:hover {
67 | background-color: #222651;
68 | color: #fff;
69 | }
70 |
--------------------------------------------------------------------------------
/demo/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';
4 | import 'zone.js/dist/long-stack-trace-zone';
5 | import 'zone.js/dist/proxy.js';
6 | import 'zone.js/dist/sync-test';
7 | import 'zone.js/dist/jasmine-patch';
8 | import 'zone.js/dist/async-test';
9 | import 'zone.js/dist/fake-async-test';
10 | import { getTestBed } from '@angular/core/testing';
11 | import {
12 | BrowserDynamicTestingModule,
13 | platformBrowserDynamicTesting
14 | } from '@angular/platform-browser-dynamic/testing';
15 |
16 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
17 | declare var __karma__: any;
18 | declare var require: any;
19 |
20 | // Prevent Karma from running prematurely.
21 | __karma__.loaded = function () { };
22 |
23 | // First, initialize the Angular testing environment.
24 | getTestBed().initTestEnvironment(
25 | BrowserDynamicTestingModule,
26 | platformBrowserDynamicTesting()
27 | );
28 | // Then we find all the tests.
29 | const context = require.context('./', true, /\.spec\.ts$/);
30 | // And load the modules.
31 | context.keys().map(context);
32 | // Finally, start Karma to run the tests.
33 | __karma__.start();
34 |
--------------------------------------------------------------------------------
/demo/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "",
4 | "declaration": false,
5 | "experimentalDecorators": true,
6 | "lib": [
7 | "es6",
8 | "dom"
9 | ],
10 | "mapRoot": "./",
11 | "module": "es2020",
12 | "moduleResolution": "node",
13 | "outDir": "../dist/out-tsc",
14 | "sourceMap": true,
15 | "target": "es2015",
16 | "typeRoots": [
17 | "../node_modules/@types"
18 | ]
19 | },
20 | "include": [
21 | "**/*"
22 | ],
23 | "exclude": [
24 | "**/app.module.ts",
25 | "**/app.component.ts"
26 | ]
27 | }
--------------------------------------------------------------------------------
/demo/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "callable-types": true,
7 | "class-name": true,
8 | "comment-format": [
9 | true,
10 | "check-space"
11 | ],
12 | "curly": true,
13 | "eofline": true,
14 | "forin": true,
15 | "deprecation": {
16 | "severity": "warning"
17 | },
18 | "import-blacklist": [
19 | true
20 | ],
21 | "import-spacing": true,
22 | "indent": [
23 | true,
24 | "spaces"
25 | ],
26 | "interface-over-type-literal": true,
27 | "label-position": true,
28 | "max-line-length": [
29 | true,
30 | 140
31 | ],
32 | "member-access": false,
33 | "member-ordering": [
34 | true,
35 | "static-before-instance",
36 | "variables-before-functions"
37 | ],
38 | "no-arg": true,
39 | "no-bitwise": true,
40 | "no-console": [
41 | true,
42 | "debug",
43 | "info",
44 | "time",
45 | "timeEnd",
46 | "trace"
47 | ],
48 | "no-construct": true,
49 | "no-debugger": true,
50 | "no-duplicate-variable": true,
51 | "no-empty": false,
52 | "no-empty-interface": true,
53 | "no-eval": true,
54 | "no-inferrable-types": true,
55 | "no-shadowed-variable": true,
56 | "no-string-literal": false,
57 | "no-string-throw": true,
58 | "no-switch-case-fall-through": true,
59 | "no-trailing-whitespace": true,
60 | "no-unused-expression": true,
61 | "no-var-keyword": true,
62 | "object-literal-sort-keys": false,
63 | "one-line": [
64 | true,
65 | "check-open-brace",
66 | "check-catch",
67 | "check-else",
68 | "check-whitespace"
69 | ],
70 | "prefer-const": true,
71 | "quotemark": [
72 | true,
73 | "single"
74 | ],
75 | "radix": true,
76 | "semicolon": [
77 | "always"
78 | ],
79 | "triple-equals": [
80 | true,
81 | "allow-null-check"
82 | ],
83 | "typedef-whitespace": [
84 | true,
85 | {
86 | "call-signature": "nospace",
87 | "index-signature": "nospace",
88 | "parameter": "nospace",
89 | "property-declaration": "nospace",
90 | "variable-declaration": "nospace"
91 | }
92 | ],
93 | "typeof-compare": true,
94 | "unified-signatures": true,
95 | "variable-name": false,
96 | "whitespace": [
97 | true,
98 | "check-branch",
99 | "check-decl",
100 | "check-operator",
101 | "check-separator",
102 | "check-type"
103 | ],
104 | "directive-selector": [
105 | true,
106 | "attribute",
107 | "app",
108 | "camelCase"
109 | ],
110 | "component-selector": [
111 | true,
112 | "element",
113 | "app",
114 | "kebab-case"
115 | ],
116 | "no-inputs-metadata-property": true,
117 | "no-outputs-metadata-property": true,
118 | "no-host-metadata-property": true,
119 | "no-input-rename": true,
120 | "no-output-rename": true,
121 | "use-lifecycle-interface": true,
122 | "use-pipe-transform-interface": true,
123 | "component-class-suffix": true,
124 | "directive-class-suffix": true,
125 | "no-access-missing-member": true,
126 | "templates-use-public": true,
127 | "invoke-injectable": true
128 | }
129 | }
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { NgxDatetimepickerPage } from './app.po';
2 |
3 | describe('ngx-datetimepicker App', function () {
4 | let page: NgxDatetimepickerPage;
5 |
6 | beforeEach(() => {
7 | page = new NgxDatetimepickerPage();
8 | });
9 |
10 | it('should display message saying app works', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('app works!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, element, by } from 'protractor';
2 |
3 | export class NgxDatetimepickerPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "module": "commonjs",
8 | "moduleResolution": "node",
9 | "outDir": "../dist/out-tsc-e2e",
10 | "sourceMap": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "../ngx-datetimepicker/node_modules/@types"
14 | ]
15 | }
16 | }
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/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'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | mime: {
19 | 'text/x-typescript': ['ts', 'tsx']
20 | },
21 | coverageReporter: {
22 | reporters: [
23 | { type: 'html' },
24 | { type: 'text-summary' }
25 | ],
26 | check: {
27 | global: {
28 | statements: 50,
29 | branches: 50,
30 | functions: 50,
31 | lines: 50
32 | }
33 | }
34 | },
35 |
36 | reporters: config.angularCli && config.angularCli.codeCoverage
37 | ? ['progress', 'kjhtml', 'coverage']
38 | : ['progress', 'kjhtml'],
39 | port: 9876,
40 | colors: true,
41 | logLevel: config.LOG_INFO,
42 | autoWatch: true,
43 | browsers: ['Chrome'],
44 | singleRun: false
45 | });
46 | };
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-datetime-picker",
3 | "version": "3.0.1",
4 | "description": "Date, DateTime & Time Picker components that uses native HTML5 components on mobile",
5 | "repository": {
6 | "url": "https://github.com/RenovoSolutions/ngx-datetimepicker"
7 | },
8 | "scripts": {
9 | "build": "ng-packagr -p package.json && sass src/scss/date.component.scss dist/ngx-datetime-picker.css",
10 | "test": "ng test",
11 | "test.once": "ng test --watch=false",
12 | "setup": "cd ngx-datetimepicker && npm install",
13 | "demo": "npm run build && cd demo && npm install && cp -fr ../dist/* node_modules/ngx-datetime-picker && ng serve",
14 | "demo.build": "npm run build && cd demo && npm install && cp -fr ../dist/* node_modules/ngx-datetimepicker/ && ng build",
15 | "demo.run": "npm run build && cd demo && npm install && cp -r ../dist/* node_modules/ngx-datetime-picker && ng serve"
16 | },
17 | "author": "Josh Sommer (https://twitter.com/_JoshSommer)",
18 | "license": "ISC",
19 | "devDependencies": {
20 | "@angular-devkit/build-angular": "12.x",
21 | "@angular/cli": "12.x",
22 | "@angular/common": "12.x",
23 | "@angular/compiler": "12.x",
24 | "@angular/compiler-cli": "12.x",
25 | "@angular/core": "12.x",
26 | "@angular/forms": "12.x",
27 | "@angular/localize": "12.x",
28 | "@angular/platform-browser": "12.x",
29 | "@angular/platform-browser-dynamic": "12.x",
30 | "@types/jest": "27.x",
31 | "karma": "6.x",
32 | "karma-chrome-launcher": "3.x",
33 | "karma-coverage": "2.x",
34 | "karma-jasmine": "4.x",
35 | "karma-jasmine-html-reporter": "1.x",
36 | "ng-packagr": "12.x",
37 | "rxjs": "6.x",
38 | "sass": "1.x",
39 | "typescript": "4.3.x",
40 | "zone.js": "0.11.x"
41 | },
42 | "peerDependencies": {
43 | "@angular/common": "12.x",
44 | "@angular/core": "12.x",
45 | "@angular/forms": "12.x"
46 | },
47 | "ngPackage": {
48 | "lib": {
49 | "cssUrl": "none",
50 | "entryFile": "src/index.ts",
51 | "styleIncludePaths": [
52 | "./src/scss"
53 | ]
54 | }
55 | },
56 | "dependencies": {
57 | "tslib": "2.x"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/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 | /*global jasmine */
5 | var SpecReporter = require('jasmine-spec-reporter');
6 |
7 | exports.config = {
8 | allScriptsTimeout: 11000,
9 | specs: [
10 | './e2e/**/*.e2e-spec.ts'
11 | ],
12 | capabilities: {
13 | 'browserName': 'chrome'
14 | },
15 | directConnect: true,
16 | baseUrl: 'http://localhost:4200/',
17 | framework: 'jasmine',
18 | jasmineNodeOpts: {
19 | showColors: true,
20 | defaultTimeoutInterval: 30000,
21 | print: function () { }
22 | },
23 | useAllAngular2AppRoots: true,
24 | beforeLaunch: function () {
25 | require('ts-node').register({
26 | project: 'e2e'
27 | });
28 | },
29 | onPrepare: function () {
30 | jasmine.getEnv().addReporter(new SpecReporter());
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { HttpClientModule } from '@angular/common/http';
5 | import { DateService } from './services/date.service';
6 | import { Renderer } from './services/renderer.service';
7 |
8 | @NgModule({
9 | imports: [
10 | BrowserModule,
11 | FormsModule,
12 | HttpClientModule
13 | ],
14 | providers: [
15 | DateService,
16 | Renderer
17 | ]
18 | })
19 | export class DateTimePickerModule { }
20 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RenovoSolutions/ngx-datetimepicker/d863814e5d73781a9ec19edb2a01b6926c5c9612/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RenovoSolutions/ngx-datetimepicker/d863814e5d73781a9ec19edb2a01b6926c5c9612/src/assets/favicon.ico
--------------------------------------------------------------------------------
/src/assets/svg/calendar-clock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/assets/svg/calendar.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/assets/svg/clock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/components/date/date.component.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
15 |
20 |
21 |
26 |
27 |
28 |
31 |
36 | {{ month }}
37 |
38 |
39 |
40 |
43 |
48 | {{ year }}
49 |
50 |
51 |
52 |
54 |
55 | Su
56 | Mo
57 | Tu
58 | We
59 | Th
60 | Fr
61 | Sa
62 |
63 |
64 |
65 |
75 | {{ day.day}}
76 |
77 |
90 |
91 |
92 |
93 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/src/components/date/date.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { DateComponent } from './date.component';
2 |
3 | describe('a date component', () => {
4 | let dateComponent: DateComponent;
5 | let mockDateService: any;
6 |
7 |
8 | // register all needed dependencies
9 | beforeEach(() => {
10 | // @ts-ignore
11 | mockDateService = jasmine.createSpyObj('mockDateService', ['getDateList', 'getMonths', 'getAvailableYears']);
12 |
13 | mockDateService.getDateList.and.returnValue([]);
14 |
15 | dateComponent = new DateComponent(mockDateService);
16 | });
17 |
18 | it('should have an instance', () => {
19 | expect(dateComponent).toBeTruthy();
20 | });
21 |
22 | it('should set the selected date to today when no date is set', () => {
23 | dateComponent.ngOnInit();
24 |
25 | expect(dateComponent.selectedMonth).toEqual(new Date().getMonth() + 1);
26 | expect(dateComponent.selectedDay).toEqual(new Date().getDate());
27 | expect(dateComponent.selectedYear).toEqual(new Date().getFullYear());
28 | });
29 |
30 |
31 | it('should set the selected date to 2/23/2007 and get individual days/month/year. ', () => {
32 | dateComponent.selectedDate = new Date('2/23/2007');
33 |
34 | expect(dateComponent.selectedMonth).toEqual(2);
35 | expect(dateComponent.selectedDay).toEqual(23);
36 | expect(dateComponent.selectedYear).toEqual(2007);
37 | });
38 |
39 | it('should change the selected date to 3/23/2007', () => {
40 | dateComponent.selectedDate = new Date('2/23/2007');
41 | expect(dateComponent.selectedMonth).toEqual(2);
42 |
43 | dateComponent.selectedMonth = 3;
44 |
45 | expect(dateComponent.selectedDate).toEqual(new Date('3/23/2007'));
46 | expect(dateComponent.selectedMonth).toEqual(3);
47 | expect(mockDateService.getDateList).toHaveBeenCalled();
48 | });
49 |
50 | it('should change the selected date to 2/24/2007', () => {
51 | dateComponent.selectedDate = new Date('2/23/2007');
52 | expect(dateComponent.selectedMonth).toEqual(2);
53 |
54 | dateComponent.selectedDay = 24;
55 |
56 | expect(dateComponent.selectedDate).toEqual(new Date('2/24/2007'));
57 | expect(dateComponent.selectedDay).toEqual(24);
58 | expect(mockDateService.getDateList).toHaveBeenCalledTimes(0);
59 | });
60 |
61 |
62 | it('should change the selected date to 2/23/2017', () => {
63 | dateComponent.selectedDate = new Date('2/23/2007');
64 | expect(dateComponent.selectedMonth).toEqual(2);
65 |
66 | dateComponent.selectedYear = 2017;
67 |
68 | expect(dateComponent.selectedDate).toEqual(new Date('2/23/2017'));
69 | expect(dateComponent.selectedYear).toEqual(2017);
70 | expect(mockDateService.getDateList).toHaveBeenCalled();
71 | });
72 |
73 | it('should change the selected date to 3/3/2017 and reload the available days', () => {
74 | dateComponent.selectedDate = new Date('2/23/2007');
75 |
76 | expect(dateComponent.selectedMonth).toEqual(2);
77 |
78 | dateComponent.setSelectedDate(new Date('3/3/2017'));
79 |
80 | expect(dateComponent.selectedMonth).toEqual(3);
81 | expect(dateComponent.selectedDay).toEqual(3);
82 | expect(dateComponent.selectedYear).toEqual(2017);
83 | expect(mockDateService.getDateList).toHaveBeenCalled();
84 | });
85 |
86 | it('should toggle the month selection menu', () => {
87 | dateComponent.toggleMonthMenu();
88 |
89 | expect(dateComponent.showMonthSelection).toBe(true);
90 |
91 | dateComponent.toggleMonthMenu();
92 |
93 | expect(dateComponent.showMonthSelection).toBe(false);
94 | });
95 |
96 | it('should toggle the year selection menu', () => {
97 | dateComponent.toggleYearMenu();
98 |
99 | expect(dateComponent.showYearSelection).toBe(true);
100 |
101 | dateComponent.toggleYearMenu();
102 |
103 | expect(dateComponent.showYearSelection).toBe(false);
104 | });
105 |
106 | it('should tell the picker to hide when closed', () => {
107 | dateComponent.closeDatePicker.subscribe(visiblility => {
108 | expect(visiblility).toBe(false);
109 | })
110 | dateComponent.closePicker();
111 | });
112 |
113 | describe('time component', () => {
114 | it('should change the selected time to 8pm', (done) => {
115 | dateComponent.ngOnInit();
116 |
117 | dateComponent.selectedDateChange.subscribe((selectedDate: Date) => {
118 | expect(selectedDate.getHours()).toBe(8);
119 | done();
120 | });
121 |
122 | dateComponent.setSelectedDate(dateComponent.highlightedDate, 8);
123 |
124 | });
125 |
126 | it('should change the selected time to 45 mins', (done) => {
127 | dateComponent.ngOnInit();
128 |
129 | dateComponent.selectedDateChange.subscribe((selectedDate: Date) => {
130 | expect(selectedDate.getMinutes()).toBe(45);
131 | done();
132 | });
133 |
134 | dateComponent.setSelectedDate(dateComponent.highlightedDate, null, 45);
135 | });
136 |
137 | });
138 | });
139 |
--------------------------------------------------------------------------------
/src/components/date/date.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, AfterViewInit, Input, Output, EventEmitter, ElementRef, ViewChild, ViewEncapsulation } from '@angular/core';
2 | import { DateService } from '../../services/date.service';
3 | import { dayOfTheMonth } from '../../models/dayOfTheMonth.interface';
4 |
5 | @Component({
6 | selector: 'ngx-date',
7 | templateUrl: './date.component.html',
8 | encapsulation: ViewEncapsulation.None,
9 | })
10 | export class DateComponent implements OnInit, AfterViewInit {
11 | @Input() selectedDate: Date;
12 | @Input() includeTime: boolean;
13 | @Input() doNotCloseOnDateSet: boolean = false;
14 | @Input() min: string = null;
15 | @Input() max: string = null;
16 | @Input() use24HourClock: boolean = false;
17 |
18 | @Output() selectedDateChange = new EventEmitter();
19 | @Output() closeDatePicker = new EventEmitter();
20 |
21 | @ViewChild('yearSelect', { static: false }) yearSelect: ElementRef;
22 | @ViewChild('monthSelect', { static: false }) monthSelect: ElementRef;
23 |
24 | public availableDays: dayOfTheMonth[];
25 | public months: string[];
26 | public years: number[];
27 |
28 | public highlightedDate: Date;
29 |
30 | public selectedHour: number;
31 | public selectedMinute: number;
32 |
33 | public alreadySpecifiedMonth: boolean = false;
34 | public alreadySpecifiedYear: boolean = false;
35 |
36 | public showMonthSelection: boolean = false;
37 | public showYearSelection: boolean = false;
38 |
39 | get selectedMonth(): number {
40 | if (this.selectedDate == null) {
41 | return new Date().getMonth() + 1;
42 | }
43 | return this.selectedDate.getMonth() + 1;
44 | }
45 |
46 | get selectedDay(): number {
47 | if (this.selectedDate == null) {
48 | return new Date().getDate();
49 | }
50 | return this.selectedDate.getDate();
51 | }
52 |
53 | get selectedYear(): number {
54 | if (this.selectedDate == null) {
55 | return new Date().getFullYear();
56 | }
57 | return this.selectedDate.getFullYear();
58 | }
59 |
60 | set selectedMonth(month: number) {
61 | let newDate = new Date(this.selectedDate);
62 |
63 | if (month == null) {
64 | month = new Date().getMonth();
65 | }
66 |
67 | newDate.setMonth(month - 1);
68 | this.loadCalendarMonth(newDate);
69 | }
70 |
71 | set selectedDay(day: number) {
72 | let newDate = new Date(this.selectedDate);
73 |
74 | newDate.setDate(day);
75 | this.loadCalendarMonth(newDate);
76 |
77 | }
78 | set selectedYear(year: number) {
79 | let newDate = new Date(this.selectedDate);
80 |
81 | newDate.setFullYear(year);
82 | this.loadCalendarMonth(newDate);
83 | }
84 |
85 | get selectedMonthText(): string {
86 | return this.dateService.getMonthText(this.selectedDate);
87 | }
88 |
89 | constructor(
90 | private dateService: DateService
91 | ) {
92 |
93 | }
94 |
95 | setMonth(i: number): void {
96 | this.selectedMonth = i;
97 |
98 | this.showMonthSelection = false;
99 | this.alreadySpecifiedMonth = true;
100 |
101 | if (!this.alreadySpecifiedYear) {
102 | this.showYearSelection = true;
103 | }
104 | }
105 |
106 | setSelectedDate(date: Date, hour?: number, minutes?: number): void {
107 | if (this.includeTime && !!date && !!this.selectedDate) {
108 | date.setHours(this.selectedDate.getHours(), this.selectedDate.getMinutes());
109 | }
110 | if (!date) {
111 | date = new Date();
112 | }
113 |
114 | if (this.min && date < new Date(this.min)) {
115 | date = new Date(this.min);
116 | }
117 |
118 | if (this.max && date > new Date(this.max)) {
119 | date = new Date(this.max);
120 | }
121 |
122 | //load calendarMonth will set the selected date;
123 | this.loadCalendarMonth(date);
124 |
125 | if (hour != null) {
126 | this.selectedDate.setHours(hour);
127 | }
128 |
129 | if (minutes != null) {
130 | this.selectedDate.setMinutes(minutes);
131 | }
132 |
133 | this.selectedDateChange.emit(this.selectedDate);
134 | this.highlightedDate = this.selectedDate;
135 | this.selectedHour = date.getHours();
136 | this.selectedMinute = date.getMinutes();
137 |
138 | if (!this.doNotCloseOnDateSet) {
139 | this.closePicker();
140 | }
141 | }
142 |
143 | setYear(year: number): void {
144 | this.selectedYear = year;
145 |
146 | this.showYearSelection = false;
147 | this.alreadySpecifiedYear = true;
148 |
149 | if (!this.alreadySpecifiedMonth) {
150 | this.showMonthSelection = true;
151 | }
152 | }
153 |
154 | private loadCalendarMonth(date: Date) {
155 | if (date == null) {
156 | date = new Date();
157 | }
158 | const shouldReloadCalendar = (this.selectedMonth != (date.getMonth() + 1) || this.selectedYear != date.getFullYear());
159 | this.selectedDate = date;
160 |
161 | if (shouldReloadCalendar) {
162 | this.availableDays = [...this.dateService.getDateList(this.selectedMonth, this.selectedYear)];
163 | }
164 | }
165 |
166 | ngOnInit() {
167 | this.months = this.dateService.getMonths();
168 | this.years = this.dateService.getAvailableYears();
169 |
170 | //If no date is selected then default to today's date.
171 | if (!this.selectedDate) {
172 | if (this.min && new Date(this.min) > new Date()) {
173 | this.selectedDate = new Date(this.min);
174 | } else if (this.max && new Date(this.max) < new Date()) {
175 | this.selectedDate = new Date(this.max);
176 | } else {
177 | this.selectedDate = new Date();
178 | }
179 | }
180 | if (typeof this.selectedDate == 'string') {
181 | this.selectedDate = new Date(this.selectedDate);
182 | }
183 |
184 | if (this.includeTime) {
185 | this.selectedHour = this.selectedDate.getHours();
186 | }
187 |
188 | if (this.includeTime) {
189 | this.selectedMinute = this.selectedDate.getMinutes();
190 | }
191 | this.highlightedDate = this.selectedDate;
192 | this.availableDays = [...this.dateService.getDateList(this.selectedMonth, this.selectedYear)];
193 | }
194 |
195 | ngAfterViewInit() {
196 | // subscribing to it's own event emitter to set the selected year position
197 | this.selectedDateChange.subscribe(
198 | () => {
199 | this.scrollToMonth();
200 | this.scrollToYear();
201 | }
202 | );
203 | }
204 |
205 | public canSelectYear(year: number): boolean {
206 | return this.dateService.canSelectYear(year, this.min, this.max);
207 | }
208 |
209 | public canSelectMonth(month: number): boolean {
210 | return this.dateService.canSelectMonth(month, this.selectedYear, this.min, this.max);
211 | }
212 |
213 | public canSelectDay(day: number, month: number): boolean {
214 | return this.dateService.canSelectDay(day, month, this.selectedYear, this.min, this.max);
215 | }
216 |
217 | public scrollToYear(): void {
218 | // setTime out is being used since I need this code to excute next, if not the change won't be visible until the second click
219 | setTimeout(() => {
220 | if (this.yearSelect && this.yearSelect.nativeElement) {
221 | const selectContainer = this.yearSelect.nativeElement;
222 | const selectedYear = selectContainer.querySelector('.calendar--year__selected');
223 | selectContainer.scrollTop = selectedYear.offsetTop - (selectContainer.clientHeight / 2) - (selectedYear.clientHeight);
224 | }
225 | });
226 | }
227 |
228 | public scrollToMonth(): void {
229 | // setTime out is being used since I need this code to execute next, if not the change won't be visible until the second click
230 | setTimeout(() => {
231 | if (this.monthSelect && this.monthSelect.nativeElement) {
232 | const selectContainer = this.monthSelect.nativeElement;
233 | const selectedMonth = selectContainer.querySelector('.calendar--month__selected');
234 | selectContainer.scrollTop = selectedMonth.offsetTop - (selectContainer.clientHeight / 2) - (selectedMonth.clientHeight);
235 | }
236 | });
237 | }
238 |
239 | public previousMonth(): void {
240 | this.alreadySpecifiedMonth = false;
241 |
242 | let previousMonth = new Date(this.selectedDate);
243 | //because javascript sets months based on a 0 index need to jump back 2 to go to the previous month.
244 | previousMonth.setMonth(this.selectedMonth - 2);
245 | this.loadCalendarMonth(previousMonth);
246 | }
247 |
248 | public nextMonth(): void {
249 | this.alreadySpecifiedMonth = false;
250 |
251 | let nextMonth = new Date(this.selectedDate);
252 | /// same as above but since selected month is 1-12 the index is already the next month.
253 | nextMonth.setMonth(this.selectedMonth);
254 | this.loadCalendarMonth(nextMonth);
255 | }
256 |
257 | public toggleMonthMenu(): void {
258 | this.scrollToMonth();
259 |
260 | this.showMonthSelection = !this.showMonthSelection;
261 | }
262 |
263 | public toggleYearMenu(): void {
264 | this.scrollToYear();
265 | this.showYearSelection = !this.showYearSelection;
266 | }
267 |
268 | public closePicker(): void {
269 | this.alreadySpecifiedMonth = false;
270 | this.alreadySpecifiedYear = false;
271 |
272 | this.closeDatePicker.emit(false);
273 | }
274 |
275 | public setDateToNow(): void {
276 | this.setSelectedDate(new Date());
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/src/components/datePicker/datePicker.component.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
18 |
19 |
27 |
28 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/components/datePicker/datePicker.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { DatePickerComponent } from './datePicker.component';
2 |
3 | describe('an input component', () => {
4 | let datePickerComponent: DatePickerComponent;
5 | let mockDateService: any;
6 | let eRef: any;
7 | let renderer: any;
8 |
9 |
10 | // register all needed dependencies
11 | beforeEach(() => {
12 | // @ts-ignore
13 | mockDateService = jasmine.createSpyObj('mockDateService', ['getDateList', 'getMonths', 'getAvailableYears']);
14 | // @ts-ignore
15 | renderer = jasmine.createSpyObj('renderer', ['invokeElementMethod']);
16 |
17 | mockDateService.getDateList.and.returnValue([]);
18 | datePickerComponent = new DatePickerComponent({}, mockDateService, eRef, renderer);
19 | });
20 |
21 | it('should have an instance', () => {
22 | expect(datePickerComponent).toBeTruthy();
23 | });
24 | it('should hide the picker', () => {
25 | let visibility = false;
26 |
27 | datePickerComponent.setPickerVisible(visibility);
28 |
29 | expect(datePickerComponent.pickerVisible).toBe(false);
30 | });
31 |
32 | it('should focus the input element', () => {
33 | let nativeInput = {};
34 | datePickerComponent.input = { nativeElement: nativeInput } as any;
35 |
36 | datePickerComponent.focus();
37 |
38 | expect(renderer.invokeElementMethod).toHaveBeenCalledTimes(1);
39 | expect(renderer.invokeElementMethod).toHaveBeenCalledWith(nativeInput, 'focus');
40 | });
41 |
42 | });
43 |
--------------------------------------------------------------------------------
/src/components/datePicker/datePicker.component.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Component,
3 | OnInit,
4 | Input,
5 | Output,
6 | EventEmitter,
7 | ViewEncapsulation,
8 | ElementRef,
9 | forwardRef,
10 | ViewChild,
11 | HostListener
12 | } from '@angular/core';
13 |
14 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
15 | import { IsMobileService } from '../../services/isMobile.service';
16 | import { DateService } from '../../services/date.service';
17 | import { StyleObject } from '../../models/styleObject.model';
18 | import { Renderer } from '../../services/renderer.service';
19 |
20 | @Component({
21 | selector: 'ngx-date-picker',
22 | templateUrl: './datePicker.component.html',
23 | encapsulation: ViewEncapsulation.None,
24 | providers: [
25 | {
26 | provide: NG_VALUE_ACCESSOR,
27 | useExisting: forwardRef(() => DatePickerComponent),
28 | multi: true,
29 | },
30 | ]
31 | })
32 | export class DatePickerComponent implements OnInit, ControlValueAccessor {
33 | @Input() selectedDate: Date;
34 | @Input() min: string;
35 | @Input() max: string;
36 | @Input() placeholder: string;
37 | @Input() inputTabIndex: number;
38 | @Input() disableInput: boolean = false;
39 | @Input() disableButton: boolean = false;
40 | @Input() disablePicker: boolean = false;
41 | @Input() doNotCloseOnDateSet: boolean = false;
42 | @Input() styles: StyleObject = new StyleObject();
43 |
44 | @Output() selectedDateChange = new EventEmitter();
45 |
46 | @ViewChild('input', { static: false }) input: ElementRef;
47 |
48 | @HostListener('document:click', ['$event'])
49 | offClick(event) {
50 | if (!this.eRef.nativeElement.contains(event.target)) {
51 | this.pickerVisible = false;
52 | }
53 | }
54 |
55 | pickerVisible: boolean = false;
56 | isMobile: boolean;
57 | invalid: boolean;
58 |
59 | get formattedDate() {
60 | return this.dateService.formatMMDDYYYY(this.selectedDate);
61 | }
62 |
63 | get mobileFormattedDate() {
64 | return this.dateService.formatMobileYYYYMMDD(this.selectedDate);
65 | }
66 |
67 | constructor(
68 | private isMobileService: IsMobileService,
69 | private dateService: DateService,
70 | private eRef: ElementRef,
71 | private renderer: Renderer
72 | ) {
73 | this.isMobile = isMobileService.isMobile;
74 | this.placeholder = this.placeholder || '';
75 | }
76 |
77 | writeValue(value: Date) {
78 | this.selectedDate = value;
79 | }
80 |
81 | registerOnChange(handler) {
82 | this.selectedDateChange.subscribe(handler);
83 | }
84 |
85 | registerOnTouched() { }
86 |
87 | // for use with the native html5 element. only emit's new valid dates.
88 | setDate(date: string) {
89 | this.invalid = !Date.parse(`${date} 00:00:00`);
90 |
91 | if (!this.invalid) {
92 | // set the time to zero so that values emitted on mobile are the same as on desktop
93 | this.selectedDate = new Date(`${date} 00:00:00`);
94 | this.selectedDateChange.emit(this.selectedDate);
95 | }
96 | }
97 |
98 | ngOnInit() {
99 | if (typeof this.selectedDate == 'string') {
100 | this.selectedDate = new Date(this.selectedDate);
101 | }
102 | }
103 |
104 | newDatePicked(date: Date): void {
105 | this.invalid = false;
106 |
107 | this.selectedDateChange.emit(date);
108 | this.selectedDate = date;
109 | }
110 |
111 | setPickerVisible(close: boolean): void {
112 | this.pickerVisible = close;
113 | }
114 |
115 | focus(): void {
116 | this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/components/dateTimePicker/dateTimePicker.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
20 |
21 |
29 |
30 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/components/dateTimePicker/dateTimePicker.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { DateTimePickerComponent } from './dateTimePicker.component';
2 |
3 | describe('a input component', () => {
4 | let dateTimePickerComponent: DateTimePickerComponent;
5 | let mockDateService: any;
6 | let eRef: any;
7 |
8 |
9 | // register all needed dependencies
10 | beforeEach(() => {
11 | // @ts-ignore
12 | mockDateService = jasmine.createSpyObj('mockDateService', ['getDateList', 'getMonths', 'getAvailableYears']);
13 |
14 | mockDateService.getDateList.and.returnValue([]);
15 | dateTimePickerComponent = new DateTimePickerComponent({}, mockDateService, eRef);
16 | });
17 |
18 | it('should have an instance', () => {
19 | expect(dateTimePickerComponent).toBeTruthy();
20 | });
21 |
22 | it('should hide the picker', () => {
23 | let visibility = false;
24 |
25 | dateTimePickerComponent.setPickerVisible(visibility);
26 |
27 | expect(dateTimePickerComponent.pickerVisible).toBe(false);
28 | });
29 |
30 | });
31 |
--------------------------------------------------------------------------------
/src/components/dateTimePicker/dateTimePicker.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, Output, EventEmitter, ViewEncapsulation, ElementRef, forwardRef, HostListener } from '@angular/core';
2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
3 | import { IsMobileService } from '../../services/isMobile.service';
4 | import { DateService } from '../../services/date.service';
5 | import { StyleObject } from '../../models/styleObject.model';
6 |
7 | @Component({
8 | selector: 'ngx-datetime-picker',
9 | templateUrl: './dateTimePicker.component.html',
10 | encapsulation: ViewEncapsulation.None,
11 | providers: [
12 | {
13 | provide: NG_VALUE_ACCESSOR,
14 | useExisting: forwardRef(() => DateTimePickerComponent),
15 | multi: true,
16 | },
17 | ],
18 | })
19 | export class DateTimePickerComponent implements OnInit, ControlValueAccessor {
20 | public pickerVisible: boolean = false;
21 | public isMobile: boolean;
22 | public invalid: boolean;
23 |
24 | @Input() selectedDateTime: Date;
25 | @Input() placeholder: string;
26 | @Input() disableInput: boolean = false;
27 | @Input() disableButton: boolean = false;
28 | @Input() disablePicker: boolean = false;
29 | @Input() doNotCloseOnDateSet: boolean = false;
30 | @Input() min: string = null;
31 | @Input() max: string = null;
32 | @Input() styles: StyleObject = new StyleObject();
33 | @Input() use24HourClock: boolean = false;
34 |
35 | @Output() selectedDateTimeChange = new EventEmitter();
36 |
37 | @HostListener('document:click', ['$event'])
38 | offClick(event) {
39 | if (!this.eRef.nativeElement.contains(event.target)) {
40 | this.pickerVisible = false;
41 | }
42 | }
43 |
44 | get formattedDate() {
45 | if (this.use24HourClock) {
46 | return this.dateService.formatMMDDYYYY_HHMM(this.selectedDateTime);
47 | }
48 | return this.dateService.formatMMDDYYYY_HHMM_AMPM(this.selectedDateTime);
49 | }
50 |
51 | get mobileFormattedDate() {
52 | return this.dateService.formatMobileYYYYMMDDTHHMM(this.selectedDateTime);
53 | }
54 |
55 | constructor(
56 | private isMobileService: IsMobileService,
57 | private dateService: DateService,
58 | private eRef: ElementRef
59 | ) {
60 | this.isMobile = isMobileService.isMobile;
61 | this.placeholder = this.placeholder || '';
62 |
63 | }
64 |
65 | writeValue(value: Date) {
66 | this.selectedDateTime = value;
67 | }
68 |
69 | registerOnChange(handler) {
70 | this.selectedDateTimeChange.subscribe(handler);
71 | }
72 |
73 | registerOnTouched() { }
74 |
75 | setDateTime(dateTime: string) {
76 | this.invalid = !Date.parse(dateTime);
77 |
78 | if (!this.invalid) {
79 | this.selectedDateTime = new Date(dateTime);
80 | this.selectedDateTimeChange.emit(this.selectedDateTime);
81 | }
82 | }
83 |
84 | ngOnInit() {
85 | if (typeof this.selectedDateTime === 'string') {
86 | this.selectedDateTime = new Date(this.selectedDateTime);
87 | }
88 | }
89 |
90 | newDatePicked(date) {
91 | this.invalid = false;
92 | this.selectedDateTime = new Date(date);
93 | this.selectedDateTimeChange.emit(this.selectedDateTime);
94 | }
95 |
96 | setPickerVisible(close: boolean): void {
97 | this.pickerVisible = close;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/components/time/time.component.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
16 |
22 |
23 |
29 |
30 |
32 |
33 |
37 | {{ hour }}
38 |
39 |
40 |
41 |
42 |
44 |
45 |
49 | {{ minute }}
50 |
51 |
52 |
53 |
54 |
55 |
68 |
--------------------------------------------------------------------------------
/src/components/time/time.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TimeComponent } from './time.component';
2 |
3 | describe('a time component', () => {
4 | let timeComponent: TimeComponent;
5 |
6 | // register all needed dependencies
7 | beforeEach(() => {
8 | timeComponent = new TimeComponent();
9 | });
10 |
11 | it('should have an instance', () => {
12 | expect(timeComponent).toBeTruthy();
13 | });
14 |
15 | it('should change the hour to 0 when setting the clock to am and the time is 12 pm', () => {
16 | timeComponent.selectedHour = 12;
17 | timeComponent.selectedMinute = 0;
18 |
19 | timeComponent.selectClockChange('am');
20 |
21 | expect(timeComponent.selectedHour).toBe(0);
22 | expect(timeComponent.formatSelectedHour).toBe('12');
23 |
24 | });
25 |
26 | it('should change the hour appropriately and the formatted hour', () => {
27 | timeComponent.selectedHour = 0;
28 | timeComponent.selectedMinute = 0;
29 |
30 | timeComponent.selectClockChange('pm');
31 |
32 | expect(timeComponent.selectedHour).toBe(12);
33 | expect(timeComponent.formatSelectedHour).toBe('12');
34 |
35 | timeComponent.selectClockChange('am');
36 |
37 | expect(timeComponent.selectedHour).toBe(0);
38 | expect(timeComponent.formatSelectedHour).toBe('12');
39 |
40 | });
41 |
42 | it('should toggle the hour selection menu', () => {
43 | timeComponent.toggleHourMenu();
44 |
45 | expect(timeComponent.hoursOpen).toBe(true);
46 |
47 | timeComponent.toggleHourMenu();
48 |
49 | expect(timeComponent.hoursOpen).toBe(false);
50 | });
51 |
52 | it('should toggle the minute selection menu', () => {
53 | timeComponent.toggleMinuteMenu();
54 |
55 | expect(timeComponent.minutesOpen).toBe(true);
56 |
57 | timeComponent.toggleMinuteMenu();
58 |
59 | expect(timeComponent.minutesOpen).toBe(false);
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/src/components/time/time.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'ngx-time',
5 | templateUrl: './time.component.html',
6 | encapsulation: ViewEncapsulation.None,
7 | })
8 | export class TimeComponent implements OnInit {
9 | @Input() selectedHour: number;
10 | @Output() selectedHourChange = new EventEmitter();
11 |
12 | @Input() selectedMinute: number;
13 | @Output() selectedMinuteChange = new EventEmitter();
14 |
15 | @Input() doNotCloseOnDateSet: boolean = false;
16 | @Input() use24HourClock: boolean = false;
17 |
18 | @Output() closeDatePicker = new EventEmitter();
19 |
20 | public selectedClock: string;
21 |
22 | public hours = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
23 | public minutes = ['0', '5', '10', '15', '20', '25', '30', '35', '40', '45', '50', '55'];
24 |
25 | public minutesOpen: boolean;
26 | public hoursOpen: boolean;
27 |
28 | get formatSelectedMinute(): string {
29 | return this.selectedMinute <= 9 ? '0' + this.selectedMinute : '' + this.selectedMinute;
30 | }
31 |
32 | get formatSelectedHour(): string {
33 | if (!this.use24HourClock) {
34 | if (this.selectedHour === 12 || this.selectedHour === 0) {
35 | return '12';
36 | }
37 |
38 | return (this.selectedHour > 12 ? this.selectedHour - 12 : this.selectedHour) + '';
39 | } else {
40 | return (this.selectedHour < 10 ? '0' + this.selectedHour : this.selectedHour) + '';
41 | }
42 | }
43 |
44 | ngOnInit() {
45 | if (!this.selectedHour) {
46 | this.selectedHour = 12;
47 | }
48 | if (!this.selectedMinute) {
49 | this.selectedMinute = 0;
50 | }
51 | if (this.selectedHour >= 12) {
52 | this.selectedClock = 'pm';
53 | }
54 | if (this.use24HourClock) {
55 | this.hours = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];
56 | this.minutes = ['00', '05', '10', '15', '20', '25', '30', '35', '40', '45', '50', '55'];
57 | this.selectedClock = '';
58 | }
59 | }
60 |
61 | selectHourChange(selected: string): void {
62 | let hour = parseInt(selected);
63 | if (!this.use24HourClock) {
64 | hour = this.selectedClock === 'pm' ? hour + 12 : hour;
65 | }
66 |
67 | this.selectedHourChange.emit(hour);
68 | this.selectedHour = hour;
69 |
70 | if (this.selectedMinute == null) {
71 | this.selectMinuteChange('0');
72 | }
73 |
74 | this.minutesOpen = false;
75 | this.hoursOpen = false;
76 | }
77 |
78 | selectMinuteChange(selected: string): void {
79 | const minute = parseInt(selected);
80 |
81 | this.selectedMinuteChange.emit(minute);
82 | this.selectedMinute = minute;
83 |
84 | this.minutesOpen = false;
85 | this.hoursOpen = false;
86 | }
87 |
88 | selectClockChange(clock: string): void {
89 | if (this.selectedClock !== clock) {
90 | this.selectedClock = clock;
91 |
92 | if (this.selectedClock === 'pm' && this.selectedHour <= 11) {
93 | this.selectedHour = this.selectedHour + 12;
94 | } else if (this.selectedClock === 'am' && this.selectedHour >= 12) {
95 | this.selectedHour = this.selectedHour - 12;
96 | }
97 |
98 | this.selectedHourChange.emit(this.selectedHour);
99 | }
100 | }
101 |
102 | closePicker(): void {
103 | this.closeDatePicker.emit(true);
104 | }
105 |
106 | setTimeToNow(): void {
107 | const now = new Date();
108 |
109 | this.selectedHour = now.getHours();
110 | this.selectedHourChange.emit(this.selectedHour);
111 | this.selectedMinute = now.getMinutes();
112 | this.selectedMinuteChange.emit(this.selectedMinute);
113 |
114 | this.selectedClock = this.selectedHour >= 12 ? 'pm' : 'am';
115 |
116 | if (!this.doNotCloseOnDateSet) {
117 | this.closePicker();
118 | }
119 | }
120 |
121 | public toggleHourMenu(): void {
122 | this.minutesOpen = false;
123 | this.hoursOpen = !this.hoursOpen;
124 | }
125 |
126 | public toggleMinuteMenu(): void {
127 | this.hoursOpen = false;
128 | this.minutesOpen = !this.minutesOpen;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/components/timePicker/timePicker.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
29 |
30 |
32 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/components/timePicker/timePicker.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TimePickerComponent } from './timePicker.component';
2 |
3 | describe('a timePicker component', () => {
4 | let timePickerComponent: TimePickerComponent;
5 | let mockDateService: any;
6 | let eRef: any;
7 |
8 | // register all needed dependencies
9 | beforeEach(() => {
10 | // @ts-ignore
11 | mockDateService = jasmine.createSpyObj('mockDateService', ['getDateList', 'getMonths', 'getAvailableYears']);
12 |
13 | mockDateService.getDateList.and.returnValue([]);
14 | timePickerComponent = new TimePickerComponent({}, mockDateService, eRef);
15 | });
16 |
17 | it('should have an instance', () => {
18 | expect(timePickerComponent).toBeTruthy();
19 | });
20 |
21 | it('should set the selected hour and minute accordingly', () => {
22 | timePickerComponent.mobileFormattedTime = '14:05';
23 |
24 | expect(timePickerComponent.selectedHour).toBe(14);
25 | expect(timePickerComponent.selectedMinute).toBe(5);
26 | });
27 |
28 | it('should set the selected hour and minute accordingly', () => {
29 | timePickerComponent.mobileFormattedTime = '04:15';
30 |
31 | expect(timePickerComponent.selectedHour).toBe(4);
32 | expect(timePickerComponent.selectedMinute).toBe(15);
33 | });
34 |
35 | it('should set the selected hour and minute accordingly', () => {
36 | timePickerComponent.mobileFormattedTime = '';
37 |
38 | expect(timePickerComponent.selectedHour).toBe(0);
39 | expect(timePickerComponent.selectedMinute).toBe(0);
40 | });
41 |
42 | it('should set the selected hour and minute to now', () => {
43 | const now = new Date();
44 |
45 | timePickerComponent.setHourNow(now.getHours());
46 | timePickerComponent.setMinuteNow(now.getMinutes());
47 | timePickerComponent.mobileFormattedTime;
48 |
49 | expect(timePickerComponent.selectedHour).toBe(now.getHours());
50 | expect(timePickerComponent.selectedMinute).toBe(now.getMinutes());
51 | });
52 |
53 | it('should hide the picker when closed', () => {
54 | timePickerComponent.setPickerVisible(false);
55 |
56 | expect(timePickerComponent.pickerVisible).toBe(false);
57 | });
58 |
59 | it('should set the selected hour accordingly, upon user select', () => {
60 | timePickerComponent.mobileFormattedTime = '04:15';
61 |
62 | timePickerComponent.setHourNow(6);
63 | timePickerComponent.mobileFormattedTime;
64 |
65 | expect(timePickerComponent.selectedHour).toBe(6);
66 | expect(timePickerComponent.selectedMinute).toBe(15);
67 | expect(timePickerComponent.selectedTime).toBe('6:15 am');
68 | });
69 |
70 | it('should set the selected minute accordingly, upon user select', () => {
71 | timePickerComponent.mobileFormattedTime = '04:15';
72 |
73 | timePickerComponent.setMinuteNow(30);
74 | timePickerComponent.mobileFormattedTime;
75 |
76 |
77 | expect(timePickerComponent.selectedHour).toBe(4);
78 | expect(timePickerComponent.selectedMinute).toBe(30);
79 | expect(timePickerComponent.selectedTime).toBe('4:30 am');
80 | });
81 | });
82 |
--------------------------------------------------------------------------------
/src/components/timePicker/timePicker.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewEncapsulation, EventEmitter, Input, Output, forwardRef, ElementRef, HostListener } from '@angular/core';
2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
3 | import { IsMobileService } from '../../services/isMobile.service';
4 | import { DateService } from '../../services/date.service';
5 | import { StyleObject } from '../../models/styleObject.model';
6 |
7 | @Component({
8 | selector: 'ngx-time-picker',
9 | templateUrl: './timePicker.component.html',
10 | encapsulation: ViewEncapsulation.None,
11 | providers: [
12 | {
13 | provide: NG_VALUE_ACCESSOR,
14 | useExisting: forwardRef(() => TimePickerComponent),
15 | multi: true,
16 | },
17 | ],
18 | })
19 | export class TimePickerComponent implements ControlValueAccessor {
20 | @Input() selectedTime: string;
21 | @Input() placeholder: string;
22 | @Input() disableInput: boolean = false;
23 | @Input() disableButton: boolean = false;
24 | @Input() disablePicker: boolean = false;
25 | @Input() doNotCloseOnDateSet: boolean = false;
26 | @Input() styles: StyleObject = new StyleObject();
27 | @Input() use24HourClock: boolean = false;
28 |
29 | @Output() selectedTimeChange: EventEmitter;
30 |
31 | @HostListener('document:click', ['$event'])
32 | offClick(event) {
33 | if (!this.eRef.nativeElement.contains(event.target)) {
34 | this.pickerVisible = false;
35 | }
36 | }
37 |
38 | public pickerVisible: boolean = false;
39 | public isMobile: boolean;
40 |
41 | get formattedTime(): string {
42 | if (this.selectedTime == null) {
43 | return '';
44 | }
45 |
46 | this.selectedHour = parseInt(this.selectedTime.split(':')[0]);
47 | this.selectedMinute = parseInt(this.selectedTime.split(':')[1]);
48 |
49 | if (this.use24HourClock) {
50 | return this.dateService.formatHHMM(this.selectedHour, this.selectedMinute);
51 | }
52 |
53 | return this.dateService.formatHHMM_AMPM(this.selectedHour, this.selectedMinute);
54 | }
55 |
56 | get mobileFormattedTime(): string {
57 | if (this.selectedTime == null) {
58 | return '';
59 | }
60 |
61 | this.selectedHour = parseInt(this.selectedTime.split(':')[0]);
62 | this.selectedMinute = parseInt(this.selectedTime.split(':')[1]);
63 |
64 | return `${(this.selectedHour < 10 ? '0' + this.selectedHour : this.selectedHour)}:${(this.selectedMinute < 10 ? '0' + this.selectedMinute : this.selectedMinute)}`
65 | }
66 |
67 | set mobileFormattedTime(value: string) {
68 | const hour = value.split(':')[0];
69 | const minute = value.split(':')[1];
70 |
71 | if (parseInt(hour)) {
72 | this.selectedHour = parseInt(hour);
73 | } else {
74 | this.selectedHour = 0;
75 | }
76 |
77 | if (parseInt(minute)) {
78 | this.selectedMinute = parseInt(minute);
79 | } else {
80 | this.selectedMinute = 0;
81 | }
82 |
83 | this.selectedTime = `${hour}:${minute} ${parseInt(hour) <= 11 ? 'am' : 'pm'}`
84 | }
85 | public selectedHour: number;
86 | public selectedMinute: number;
87 |
88 | constructor(
89 | private isMobileService: IsMobileService,
90 | private dateService: DateService,
91 | private eRef: ElementRef
92 | ) {
93 | this.selectedTimeChange = new EventEmitter();
94 |
95 | this.isMobile = isMobileService.isMobile;
96 | this.placeholder = this.placeholder || '';
97 | }
98 |
99 | writeValue(value: string): void {
100 | this.selectedTime = value;
101 | }
102 |
103 | registerOnChange(handler): void {
104 | this.selectedTimeChange.subscribe(handler);
105 | }
106 |
107 | registerOnTouched(): void {
108 |
109 | }
110 |
111 | setMobileFormattedTime(time: string) {
112 | this.selectedTimeChange.emit(time);
113 | this.selectedTime = time;
114 | }
115 |
116 | setFormattedTime(formattedTime: string) {
117 | this.selectedTime = formattedTime;
118 | this.selectedTimeChange.emit(formattedTime);
119 | }
120 |
121 | setHourNow(hour: any): void {
122 | const clock = hour <= 11 ? 'am' : 'pm';
123 |
124 | if (this.selectedTime == null || this.selectedTime === '') {
125 | this.selectedTime = `${hour}:00 ${clock}`
126 | } else {
127 | const prevMinute = parseInt(this.selectedTime.split(':')[1]);
128 |
129 | this.selectedTime = `${hour}:${prevMinute} ${clock}`
130 | }
131 |
132 | this.selectedTimeChange.emit(this.selectedTime);
133 | }
134 |
135 | setMinuteNow(minute: any): void {
136 | if (this.selectedTime == null || this.selectedTime === '') {
137 | this.selectedTime = `12:${minute} pm`
138 | } else {
139 | const prevHour = parseInt(this.selectedTime.split(':')[0]);
140 |
141 | this.selectedTime = `${prevHour}:${minute} ${prevHour <= 11 ? 'am' : 'pm'}`
142 | }
143 | this.selectedTimeChange.emit(this.selectedTime);
144 | }
145 |
146 | setPickerVisible(close: boolean): void {
147 | this.pickerVisible = close;
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, ModuleWithProviders } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { DateComponent } from './components/date/date.component';
4 | import { DatePickerComponent } from './components/datePicker/datePicker.component';
5 | import { TimeComponent } from './components/time/time.component';
6 | import { DateTimePickerComponent } from './components/dateTimePicker/dateTimePicker.component';
7 | import { TimePickerComponent } from './components/timePicker/timePicker.component';
8 | import { DateService } from './services/date.service';
9 | import { IsMobileService } from './services/isMobile.service';
10 | import { Renderer } from './services/renderer.service';
11 |
12 | export * from './components/datePicker/datePicker.component';
13 | export * from './components/dateTimePicker/dateTimePicker.component';
14 | export * from './components/timePicker/timePicker.component';
15 | export * from './services/date.service';
16 |
17 | @NgModule({
18 | declarations: [
19 | TimeComponent,
20 | DateComponent,
21 | DatePickerComponent,
22 | DateTimePickerComponent,
23 | TimePickerComponent
24 | ],
25 | exports: [
26 | TimeComponent,
27 | DateComponent,
28 | DatePickerComponent,
29 | DateTimePickerComponent,
30 | TimePickerComponent
31 | ],
32 | imports: [
33 | CommonModule
34 | ],
35 | providers: [
36 | DateService,
37 | IsMobileService,
38 | Renderer
39 | ],
40 | })
41 |
42 | export class DateTimePickerModule {
43 | static forRoot(): ModuleWithProviders {
44 | return {
45 | ngModule: DateTimePickerModule,
46 | providers: []
47 | };
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/models/dayOfTheMonth.interface.ts:
--------------------------------------------------------------------------------
1 | interface dayOfTheMonth {
2 | day: number;
3 | dayOfTheWeek: number;
4 | month: number;
5 | date: Date;
6 | }
7 |
8 | export { dayOfTheMonth };
9 |
--------------------------------------------------------------------------------
/src/models/styleObject.model.ts:
--------------------------------------------------------------------------------
1 | export class StyleObject {
2 | public button?= {};
3 | public date?= {};
4 | public input?= {};
5 | }
6 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | // This file includes polyfills needed by Angular and is loaded before the app.
2 | // You can add your own extra polyfills to this file.
3 | import 'core-js/es/symbol';
4 | import 'core-js/es/object';
5 | import 'core-js/es/function';
6 | import 'core-js/es/parse-int';
7 | import 'core-js/es/parse-float';
8 | import 'core-js/es/number';
9 | import 'core-js/es/math';
10 | import 'core-js/es/string';
11 | import 'core-js/es/date';
12 | import 'core-js/es/array';
13 | import 'core-js/es/regexp';
14 | import 'core-js/es/map';
15 | import 'core-js/es/set';
16 | import 'core-js/es/reflect';
17 |
18 | // If you need to support the browsers/features below, uncomment the import
19 | // and run `npm install import-name-here';
20 | // Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
21 |
22 | // Needed for: IE9
23 | // import 'classlist.js';
24 |
25 | // Animations
26 | // Needed for: All but Chrome and Firefox, Not supported in IE9
27 | // import 'web-animations-js';
28 |
29 | // Date, currency, decimal and percent pipes
30 | // Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
31 | // import 'intl';
32 |
33 | // NgClass on SVG elements
34 | // Needed for: IE10, IE11
35 | // import 'classlist.js';
36 |
--------------------------------------------------------------------------------
/src/scss/_button.scss:
--------------------------------------------------------------------------------
1 | .ngx-picker--btn {
2 | color: $btn-text-color;
3 | background-color: $btn-bg;
4 | border: none;
5 | padding: 1em;
6 | font-size: 1em;
7 | &__selected {
8 | background-color: $btn-selected-bg;
9 | }
10 | &:hover {
11 | transition: .25s ease;
12 | background-color: $btn-hover-bg;
13 | cursor: pointer;
14 | }
15 | &:focus {
16 | outline-color: transparent;
17 | }
18 | }
19 |
20 | // Matching button border radius to picker's radius
21 | .ngx-picker--btn__month:hover { border-top-left-radius: $base-border-radius; }
22 | .ngx-picker--btn__done:hover { border-bottom-right-radius: $base-border-radius; }
23 | .ngx-picker--btn__now:hover { border-bottom-left-radius: $base-border-radius; }
24 | .ngx-picker--btn__next:hover { border-top-right-radius: $base-border-radius; }
--------------------------------------------------------------------------------
/src/scss/_custom.scss:
--------------------------------------------------------------------------------
1 | // Put your variable overrides here
--------------------------------------------------------------------------------
/src/scss/_date.scss:
--------------------------------------------------------------------------------
1 | // Calendar
2 | .calendar {
3 | position: absolute;
4 | background-color: $header-footer-bg;
5 | max-width: 16em;
6 | min-width: 16em;
7 | font-size: $calendar-size-ratio;
8 | font-family: $font-family;
9 | border-radius: $base-border-radius;
10 | box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
11 | }
12 |
13 | .calendar--previous-and-next {
14 | position: absolute;
15 | right: 0;
16 | top: 0;
17 | display: flex;
18 | justify-content: flex-end;
19 | }
20 |
21 | .calendar--footer {
22 | display: flex;
23 | justify-content: space-between;
24 | background-color: $header-footer-bg;
25 | padding: 0;
26 | border-radius: $base-border-radius;
27 | width: 100%;
28 | }
29 |
30 | .ngx-datetime-picker .calendar--footer {
31 | content-visibility: hidden;
32 | }
33 |
34 | .calendar--years-select,
35 | .calendar--months-select {
36 | overflow-y: auto;
37 | overflow-x: hidden;
38 | max-height: $calendar-menu-height;
39 | background-color: $menu-background;
40 | width: 100%;
41 | }
42 |
43 | .calendar--year,
44 | .calendar--month {
45 | display: block;
46 | padding: .5em 1em;
47 | width: 100%;
48 | overflow: hidden;
49 | color: $month-and-year-text;
50 |
51 | &__selected {
52 | color: $month-and-year-text-selected;
53 | font-weight: bold;
54 | background-color: $month-and-year-text-selected-bg;
55 | }
56 |
57 | &:hover {
58 | cursor: pointer;
59 | background-color: $month-and-year-text-hover-bg;
60 | font-weight: 400;
61 | color: $month-and-year-text-hover;
62 | }
63 | }
64 |
65 | // Days of the week (Su - Sa)
66 | .calendar--days-of-week {
67 | display: flex;
68 | justify-content: space-around;
69 | background-color: $week-bg;
70 | }
71 |
72 | .calendar--day-of-week {
73 | position: relative;
74 | padding: .5em;
75 | text-align: center;
76 | text-transform: uppercase;
77 | color: $week-text;
78 | font-size: .9em;
79 | }
80 |
81 | // Individual day selection (1 - 31)
82 | .calendar--days-select {
83 | display: flex;
84 | flex-wrap: wrap;
85 | flex-direction: row;
86 | justify-content: space-between;
87 | background-color: $days-select-bg;
88 | }
89 |
90 | .calendar--day {
91 | text-align: center;
92 | width: 2.25em;
93 | padding: .5em 0;
94 | font-weight: 500;
95 | color: $day-text;
96 |
97 | &__muted {
98 | color: $day-text-muted;
99 | }
100 |
101 | &__selected {
102 | background-color: $day-text-selected-bg;
103 | color: $day-text-selected;
104 | }
105 |
106 | &:hover {
107 | cursor: pointer;
108 | color: $day-text-hover;
109 | background-color: $day-text-hover-bg;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/scss/_picker.scss:
--------------------------------------------------------------------------------
1 | .ngx-picker {
2 | display: flex;
3 | position: relative;
4 | .calendar {
5 | position: absolute;
6 | left: 0;
7 | top: 2.5em;
8 | z-index: 3;
9 | }
10 | }
--------------------------------------------------------------------------------
/src/scss/_time.scss:
--------------------------------------------------------------------------------
1 | // Time
2 | .time-picker {
3 | display: flex;
4 | justify-content: center;
5 | align-items: center;
6 | flex-wrap: wrap;
7 | max-width: 16em;
8 | min-width: 16em;
9 | background-color: $time-picker-header-bg;
10 | }
11 |
12 | .time-picker--footer {
13 | display: flex;
14 | justify-content: space-between;
15 | background-color: $header-footer-bg;
16 | padding: 0;
17 | border-radius: $base-border-radius;
18 | width: 100%;
19 | }
20 |
21 | .time--values {
22 | display: flex;
23 | justify-content: space-between;
24 | flex-direction: row;
25 | flex-wrap: wrap;
26 | max-width: 16em;
27 | min-width: 16em;
28 | background-color: $time-picker-selection-bg;
29 | }
30 |
31 | .time--value {
32 | width: 2.66em;
33 | font-size: 1em;
34 | padding: .5em 0;
35 | text-align: center;
36 | color: $time-value-text;
37 |
38 | &__selected {
39 | color: $time-value-text-selected;
40 | background-color: $time-value-text-selected-bg;
41 | }
42 |
43 | &:hover {
44 | transition: .25s ease;
45 | cursor: pointer;
46 | background-color: $time-value-text-hover-bg;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/scss/_variables.scss:
--------------------------------------------------------------------------------
1 | //Branding
2 | $color-primary: #fff !default;
3 | $color-primary-dark: darken($color-primary, 10%) !default;
4 | $color-primary-light: lighten($color-primary, 45%) !default;
5 |
6 | $color-secondary: #eee !default;
7 |
8 | // Menus
9 | $menu-background: #fff !default;
10 |
11 | // Text
12 | $font-family: 'Arial', sans-serif !default;
13 | $font-base: 16px !default;
14 |
15 | $text: #000 !default;
16 | $text-inverted: #fff !default;
17 | $text-muted: #999 !default;
18 |
19 | // Borders
20 | $base-border-radius: .2em !default;
21 |
22 | // Calendar Size Options
23 | $calendar-size-ratio: $font-base !default;
24 | $calendar-menu-height: 14.85em !default;
25 |
26 | // Calendar Color Options
27 |
28 | // Header & Footer
29 | $header-footer-text: $text !default;
30 | $header-footer-bg: $color-primary-dark !default;
31 |
32 | // Buttons
33 | $btn-text-color: $header-footer-text !default;
34 | $btn-bg: transparent !default;
35 | $btn-hover-bg: $color-secondary !default;
36 | $btn-selected-bg: $color-secondary !default;
37 |
38 | // Month & Year Selection Menus
39 | $month-and-year-text: $text !default;
40 | $month-and-year-text-hover: $text !default;
41 | $month-and-year-text-hover-bg: $color-secondary !default;
42 | $month-and-year-text-selected: $text !default;
43 | $month-and-year-text-selected-bg: $color-secondary !default;
44 |
45 | // Day of Week Header (Su - Sa)
46 | $week-bg: $color-secondary !default;
47 | $week-text: $text !default;
48 |
49 | // Day of Month Selection (1-31)
50 | $days-select-bg: $menu-background !default;
51 |
52 | $day-text: $text !default;
53 | $day-text-hover: $text !default;
54 | $day-text-hover-bg: $color-secondary !default;
55 | $day-text-selected: $text !default;
56 | $day-text-selected-bg: $color-secondary !default;
57 | $day-text-muted: $text-muted !default;
58 |
59 | // Time Color Options
60 | $time-picker-header-bg: $header-footer-bg !default;
61 | $time-picker-selection-bg: $color-secondary !default;
62 |
63 | $time-value-text: $text !default;
64 | $time-value-text-hover: $text !default;
65 | $time-value-text-hover-bg: $color-primary-dark !default;
66 | $time-value-text-selected: $text !default;
67 | $time-value-text-selected-bg: $color-primary-dark !default;
--------------------------------------------------------------------------------
/src/scss/date.component.scss:
--------------------------------------------------------------------------------
1 | @import 'custom';
2 | @import 'variables';
3 | @import 'button';
4 | @import 'date';
5 | @import 'picker';
6 | @import 'time';
7 |
8 | // Override for Angular's [hidden], so off-clicking works.
9 | [hidden] {
10 | display: none !important;
11 | }
12 |
--------------------------------------------------------------------------------
/src/services/date.service.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { DateService } from './date.service';
4 |
5 | describe('AppComponent', () => {
6 | let dateService: DateService;
7 |
8 | beforeEach(() => {
9 | dateService = new DateService();
10 | });
11 |
12 | it('Should not be null', () => {
13 | expect(dateService).toBeTruthy();
14 | });
15 |
16 | it('Should return count to be 35 when given Feb 2017', () => {
17 | const response = dateService.getDateList(2, 2017);
18 |
19 | expect(response.length).toBe(35);
20 | expect(response[0].day).toBe(29);
21 | expect(response[34].day).toBe(4);
22 |
23 | });
24 |
25 | it('Should return the last 3 days in January 2017 when given Feb 2017', () => {
26 | const response = dateService.getPreviousMonthDays(2, 2017);
27 |
28 | expect(response.length).toBe(3);
29 | expect(response[0].day).toBe(29);
30 | expect(response[0].month).toBe(1);
31 | expect(response[1].day).toBe(30);
32 | expect(response[1].month).toBe(1);
33 | expect(response[2].day).toBe(31);
34 | expect(response[2].month).toBe(1);
35 | });
36 |
37 | it('Should return the first 4 days in march when given Feb 2017', () => {
38 | const response = dateService.getNextMonthDays(2, 2017);
39 |
40 | expect(response.length).toBe(4);
41 |
42 | expect(response[0].day).toBe(1);
43 | expect(response[0].month).toBe(3);
44 | expect(response[3].day).toBe(4);
45 | expect(response[3].month).toBe(3);
46 | });
47 |
48 | it('should return "May" when givin 5/1/2017', () => {
49 | expect(dateService.getMonthText(new Date('5/1/2017'))).toEqual('May');
50 | });
51 |
52 | it('should return 12 months', () => {
53 | expect(dateService.getMonths().length).toEqual(12);
54 | });
55 |
56 | it('should return all the days in Feb 2017', () => {
57 | const response = dateService.getCurrentMonthDays(2, 2017);
58 |
59 | expect(response.length).toBe(28);
60 |
61 | expect(response[0].day).toBe(1);
62 | expect(response[0].date.toUTCString()).toBe(new Date('2/1/2017').toUTCString());
63 | expect(response[0].month).toBe(2);
64 | expect(response[27].day).toBe(28);
65 | expect(response[27].month).toBe(2);
66 | expect(response[27].date.toUTCString()).toBe(new Date('2/28/2017').toUTCString());
67 |
68 | });
69 |
70 |
71 | it('should return years going back 80 years and forward 10', () => {
72 | const response = dateService.getAvailableYears();
73 |
74 | expect(response.length).toBe(86);
75 | });
76 |
77 | it('should format the supplied date to MM/DD/YYYY', () => {
78 | const formattedDate = dateService.formatMMDDYYYY(new Date('2/23/2017'));
79 |
80 | expect(formattedDate).toBe('2/23/2017');
81 | });
82 |
83 | it('should format the supplied date to MM/DD/YYYY HH:MM pm', () => {
84 | const formattedDate = dateService.formatMMDDYYYY_HHMM_AMPM(new Date('2/23/2017 16:45:00'));
85 |
86 | expect(formattedDate).toBe('2/23/2017 4:45 pm');
87 | });
88 |
89 | it('should format the supplied date to MM/DD/YYYY HH:MM pm', () => {
90 | const formattedDate = dateService.formatMMDDYYYY_HHMM_AMPM(new Date('2/23/2017 8:45:00'));
91 |
92 | expect(formattedDate).toBe('2/23/2017 8:45 am');
93 | });
94 |
95 | it('should format the supplied date to MM/DD/YYYY 12:00 pm', () => {
96 | const formattedDate = dateService.formatMMDDYYYY_HHMM_AMPM(new Date('2/23/2017 12:00:00'));
97 |
98 | expect(formattedDate).toBe('2/23/2017 12:00 pm');
99 | });
100 |
101 | it('should format the supplied date to MM/DD/YYYY 12:00 pm', () => {
102 | const formattedDate = dateService.formatMMDDYYYY_HHMM_AMPM(new Date('2/23/2017 0:00:00'));
103 |
104 | expect(formattedDate).toBe('2/23/2017 12:00 am');
105 | });
106 |
107 | it('should return an empty string when the supplied a string', () => {
108 | const formattedDate = dateService.formatMMDDYYYY('warrior cats');
109 |
110 | expect(formattedDate).toBe('');
111 | });
112 |
113 | it('should return the string value of 2013-10-09T15:38 when supplied that datetime', () => {
114 | const formattedDate = dateService.formatMobileYYYYMMDDTHHMM(new Date(2013, 9, 9, 15, 38));
115 |
116 | expect(formattedDate).toBe('2013-10-09T15:38');
117 | });
118 |
119 | describe('canSelectYear', () => {
120 | it('should return false if the year is below the year of the minimum date', () => {
121 | expect(dateService.canSelectYear(2017, '1/1/2018', null)).toBe(false);
122 | });
123 |
124 | it('should return false if the year is above the year of the maximum date', () => {
125 | expect(dateService.canSelectYear(2019, null, '1/1/2018')).toBe(false);
126 | });
127 |
128 | it('should return true if the year is equal to the year of the minimum date', () => {
129 | expect(dateService.canSelectYear(2018, '1/1/2018', null)).toBe(true);
130 | });
131 |
132 | it('should return true if the year is equal to the year of the maximum date', () => {
133 | expect(dateService.canSelectYear(2018, null, '1/1/2018')).toBe(true);
134 | });
135 |
136 | it('should return true if there is no min or max date', () => {
137 | expect(dateService.canSelectYear(2018, null, null)).toBe(true);
138 | });
139 | });
140 |
141 | describe('canSelectMonth', () => {
142 | // dates use 0-based indexing. All month integers are one less than the equivalent value in the min/max string
143 | it('should return false if the month is below the month of the minimum date', () => {
144 | expect(dateService.canSelectMonth(4, 2018, '6/1/2018', null)).toBe(false);
145 | });
146 |
147 | it('should return false if the month is above the month of the maximum date', () => {
148 | expect(dateService.canSelectMonth(6, 2018, null, '6/1/2018')).toBe(false);
149 | });
150 |
151 | it('should return true if the month is equal to the month of the minimum date', () => {
152 | expect(dateService.canSelectMonth(5, 2018, '6/1/2018', null)).toBe(true);
153 | });
154 |
155 | it('should return true if the month is equal to the month of the maximum date', () => {
156 | expect(dateService.canSelectMonth(5, 2018, null, '6/1/2018')).toBe(true);
157 | });
158 |
159 | it('should return true if the year is above the minimum year', () => {
160 | expect(dateService.canSelectMonth(4, 2019, '6/1/2018', null)).toBe(true);
161 | });
162 |
163 | it('should return true if the year is below the maximum year', () => {
164 | expect(dateService.canSelectMonth(6, 2017, null, '6/1/2018')).toBe(true);
165 | });
166 |
167 | it('should return false if the year is below the minimum year', () => {
168 | expect(dateService.canSelectMonth(6, 2017, '6/1/2018', null)).toBe(false);
169 | });
170 |
171 | it('should return false if the year is above the maximum year', () => {
172 | expect(dateService.canSelectMonth(4, 2019, null, '6/1/2018')).toBe(false);
173 | });
174 |
175 | it('should return true if there is no min or max date', () => {
176 | expect(dateService.canSelectMonth(4, 2018, null, null)).toBe(true);
177 | });
178 | });
179 |
180 | describe('canSelectDay', () => {
181 | // dates use 0-based indexing. All month integers are one less than the equivalent value in the min/max string
182 | it('should return false if the day is below the day of the minimum date', () => {
183 | expect(dateService.canSelectDay(14, 5, 2018, '6/15/2018', null)).toBe(false);
184 | });
185 |
186 | it('should return false if the day is above the day of the maximum date', () => {
187 | expect(dateService.canSelectDay(16, 5, 2018, null, '6/15/2018')).toBe(false);
188 | });
189 |
190 | it('should return true if the day is equal to the day of the minimum date', () => {
191 | expect(dateService.canSelectDay(15, 5, 2018, '6/15/2018', null)).toBe(true);
192 | });
193 |
194 | it('should return true if the day is equal to the day of the maximum date', () => {
195 | expect(dateService.canSelectDay(15, 5, 2018, null, '6/15/2018')).toBe(true);
196 | });
197 |
198 | it('should return true if the year is above the minimum year', () => {
199 | expect(dateService.canSelectDay(14, 5, 2019, '6/15/2018', null)).toBe(true);
200 | });
201 |
202 | it('should return true if the year is below the maximum year', () => {
203 | expect(dateService.canSelectDay(16, 5, 2017, null, '6/15/2018')).toBe(true);
204 | });
205 |
206 | it('should return false if the year is below the minimum year', () => {
207 | expect(dateService.canSelectDay(16, 5, 2017, '6/15/2018', null)).toBe(false);
208 | });
209 |
210 | it('should return false if the year is above the maximum year', () => {
211 | expect(dateService.canSelectDay(14, 5, 2019, null, '6/15/2018')).toBe(false);
212 | });
213 |
214 | it('should return true if the month is above the minimum month', () => {
215 | expect(dateService.canSelectDay(14, 6, 2018, '6/15/2018', null)).toBe(true);
216 | });
217 |
218 | it('should return true if the month is below the maximum month', () => {
219 | expect(dateService.canSelectDay(16, 4, 2018, null, '6/15/2018')).toBe(true);
220 | });
221 |
222 | it('should return false if the month is below the minimum month', () => {
223 | expect(dateService.canSelectDay(16, 4, 2018, '6/15/2018', null)).toBe(false);
224 | });
225 |
226 | it('should return false if the month is above the maximum month', () => {
227 | expect(dateService.canSelectDay(14, 6, 2018, null, '6/15/2018')).toBe(false);
228 | });
229 |
230 | it('should return true if there is no min or max date', () => {
231 | expect(dateService.canSelectDay(15, 5, 2018, null, null)).toBe(true);
232 | });
233 | });
234 | });
235 |
--------------------------------------------------------------------------------
/src/services/date.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { dayOfTheMonth } from '../models/dayOfTheMonth.interface';
3 |
4 | @Injectable()
5 | export class DateService {
6 | private months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
7 |
8 | constructor() { }
9 |
10 | private addLeadingZero(value: number): string {
11 | if (value < 10) {
12 | return `0${value.toString()}`;
13 | }
14 | return value.toString();
15 | }
16 |
17 | formatMobileYYYYMMDD(date: Date): string {
18 | if (!date || typeof date == 'string') {
19 | return '';
20 | }
21 | return `${date.getFullYear()}-${this.addLeadingZero(date.getMonth() + 1)}-${this.addLeadingZero(date.getDate())}`;
22 | }
23 |
24 | formatMobileYYYYMMDDTHHMM(date: Date): string {
25 | if (!date || typeof date == 'string') {
26 | return '';
27 | }
28 | return `${this.formatMobileYYYYMMDD(date)}T${this.addLeadingZero(date.getHours())}:${this.addLeadingZero(date.getMinutes())}`;
29 | }
30 |
31 | formatMobileDDMMMYYYY(date: Date): string {
32 | if (!date || typeof date == 'string') {
33 | return '';
34 | }
35 | return `${this.addLeadingZero(date.getDate())}-${this.months[date.getMonth() + 1].substr(0, 3).toUpperCase()}-${date.getFullYear()}`;
36 | }
37 |
38 | formatMobileDDMMMYYYYTHHMM(date: Date): string {
39 | if (!date || typeof date == 'string') {
40 | return '';
41 | }
42 | return `${this.formatMobileDDMMMYYYY(date)}T${this.addLeadingZero(date.getHours())}:${this.addLeadingZero(date.getMinutes())}`;
43 | }
44 |
45 | formatMMDDYYYY(date: Date): string {
46 | if (!date || typeof date == 'string') {
47 | return '';
48 | }
49 | return `${(date.getMonth() + 1)}/${date.getDate()}/${date.getFullYear()}`;
50 | }
51 |
52 | formatMMDDYYYY_HHMM_AMPM(date: Date): string {
53 | if (!date || typeof date == 'string') {
54 | return '';
55 | }
56 | const hours = date.getHours();
57 | const minutes = date.getMinutes();
58 |
59 | return `${this.formatMMDDYYYY(date)} ${this.formatHHMM_AMPM(hours, minutes)}`;
60 | }
61 |
62 | formatMMDDYYYY_HHMM(date: Date): string {
63 | if (!date || typeof date == 'string') {
64 | return '';
65 | }
66 | const hours = date.getHours();
67 | const minutes = date.getMinutes();
68 |
69 | return `${this.formatMMDDYYYY(date)} ${this.formatHHMM(hours, minutes)}`;
70 | }
71 |
72 | formatDDMMMYYYY(date: Date): string {
73 | if (!date || typeof date == 'string') {
74 | return '';
75 | }
76 | return `${date.getDate()}/${this.months[date.getMonth() + 1].substr(0, 3).toUpperCase()}/${date.getFullYear()}`;
77 |
78 | }
79 |
80 | formatDDMMMYYYY_HHMM_AMPM(date: Date): string {
81 | if (!date || typeof date == 'string') {
82 | return '';
83 | }
84 | const hours = date.getHours();
85 | const minutes = date.getMinutes();
86 |
87 | return `${this.formatDDMMMYYYY(date)} ${this.formatHHMM_AMPM(hours, minutes)}`;
88 | }
89 |
90 | formatDDMMMYYYY_HHMM(date: Date): string {
91 | if (!date || typeof date == 'string') {
92 | return '';
93 | }
94 | const hours = date.getHours();
95 | const minutes = date.getMinutes();
96 |
97 | return `${this.formatDDMMMYYYY(date)} ${this.formatHHMM(hours, minutes)}`;
98 | }
99 |
100 | formatHHMM_AMPM(hour: number, minute: number, clock?: string): string {
101 | if (hour == null || minute == null) {
102 | return '';
103 | }
104 |
105 | if (typeof clock === 'undefined') {
106 | clock = hour >= 12 ? 'pm' : 'am';
107 | }
108 |
109 | if (hour > 12) {
110 | hour = hour - 12;
111 | } else if (hour === 0) {
112 | hour = 12;
113 | }
114 |
115 | let formattedMinute = (!minute ? '00' : (minute <= 9 ? `0${minute}` : minute));
116 |
117 | return `${hour}:${formattedMinute} ${clock}`;
118 | }
119 |
120 | formatHHMM(hour: number, minute: number): string {
121 | if (hour == null || minute == null) {
122 | return '';
123 | }
124 | let formattedHour = (!hour ? '00' : (hour <= 9 ? `0${hour}` : hour));
125 | let formattedMinute = (!minute ? '00' : (minute <= 9 ? `0${minute}` : minute));
126 |
127 | return `${formattedHour}:${formattedMinute}`;
128 | }
129 |
130 | getCurrentMonthDays(month: number, year: number): dayOfTheMonth[] {
131 | let dayOfTheMonth = new Date(year, month - 1, 1);
132 | let nextMonth = new Date(year, month - 1, 1);
133 |
134 | let returnedDays: dayOfTheMonth[] = [];
135 |
136 | nextMonth.setMonth(nextMonth.getMonth() + 1);
137 |
138 | while (dayOfTheMonth.getMonth() != nextMonth.getMonth()) {
139 | const dayToAdd = {
140 | day: dayOfTheMonth.getDate(),
141 | dayOfTheWeek: dayOfTheMonth.getDay(),
142 | month: dayOfTheMonth.getMonth() + 1,
143 | date: new Date((dayOfTheMonth.getMonth() + 1) + '/' + dayOfTheMonth.getDate() + '/' + dayOfTheMonth.getFullYear())
144 |
145 | };
146 | returnedDays.push(dayToAdd);
147 | dayOfTheMonth.setDate(dayOfTheMonth.getDate() + 1);
148 | }
149 | return returnedDays;
150 | }
151 |
152 | getDateList(Month: number, Year: number) {
153 | return [...this.getPreviousMonthDays(Month, Year),
154 | ...this.getCurrentMonthDays(Month, Year),
155 | ...this.getNextMonthDays(Month, Year)];
156 | }
157 |
158 |
159 | getPreviousMonthDays(Month: number, Year: number): dayOfTheMonth[] {
160 | let day = new Date(Month + '/1/' + Year);
161 | let returnedDays: dayOfTheMonth[] = [];
162 |
163 | let dayOfTheWeek = day.getDay();
164 |
165 | while (dayOfTheWeek != 0) {
166 | day.setDate(day.getDate() - 1);
167 | returnedDays = [{
168 | day: day.getDate(),
169 | dayOfTheWeek: day.getDay(),
170 | month: day.getMonth() + 1,
171 | date: new Date((day.getMonth() + 1) + '/' + day.getDate() + '/' + day.getFullYear())
172 | }, ...returnedDays];
173 | dayOfTheWeek = day.getDay();
174 | }
175 |
176 | return returnedDays;
177 | }
178 |
179 | getNextMonthDays(Month: number, Year: number): dayOfTheMonth[] {
180 | let day = new Date(Month + '/1/' + Year);
181 | day.setMonth(day.getMonth() + 1);
182 | day.setDate(day.getDate() - 1);
183 |
184 | let returnedDays: dayOfTheMonth[] = [];
185 |
186 | let dayOfTheWeek = day.getDay();
187 |
188 | while (dayOfTheWeek != 6) {
189 | day.setDate(day.getDate() + 1);
190 |
191 | returnedDays = [...returnedDays, {
192 | day: day.getDate(),
193 | dayOfTheWeek: day.getDay(),
194 | month: day.getMonth() + 1,
195 | date: new Date((day.getMonth() + 1) + '/' + day.getDate() + '/' + day.getFullYear())
196 | }];
197 | dayOfTheWeek = day.getDay();
198 | }
199 |
200 | return returnedDays;
201 | }
202 |
203 | getMonths(): string[] {
204 | return this.months;
205 | }
206 |
207 | getMonthText(date: Date): string {
208 | if (date == null) {
209 | date = new Date();
210 | }
211 | return this.months[date.getMonth()];
212 | }
213 |
214 | getAvailableYears(): number[] {
215 | const currentYear = new Date().getFullYear();
216 | let startYear = currentYear - 80;
217 | let returnYears: number[] = [];
218 |
219 | while (startYear <= (currentYear + 5)) {
220 | returnYears.push(startYear);
221 | startYear = startYear + 1;
222 | }
223 | return returnYears;
224 | }
225 |
226 | canSelectYear(year: number, min: string, max: string): boolean {
227 | if (!min && !max) {
228 | return true;
229 | }
230 |
231 | if (min && year < new Date(min).getFullYear()) {
232 | return false;
233 | }
234 |
235 | if (max && year > new Date(max).getFullYear()) {
236 | return false;
237 | }
238 |
239 | return true;
240 | }
241 |
242 | canSelectMonth(month: number, year: number, min: string, max: string): boolean {
243 | if (!min && !max) {
244 | return true;
245 | }
246 |
247 | if ((min && year === new Date(min).getFullYear())
248 | || (max && year === new Date(max).getFullYear())) {
249 |
250 | if (min && year === new Date(min).getFullYear() && month < new Date(min).getMonth()) {
251 | return false;
252 | }
253 |
254 | if (max && year === new Date(max).getFullYear() && month > new Date(max).getMonth()) {
255 | return false;
256 | }
257 |
258 | return true;
259 | } else {
260 | return this.canSelectYear(year, min, max);
261 | }
262 | }
263 |
264 | canSelectDay(day: number, month: number, year: number, min: string, max: string): boolean {
265 | if (!min && !max) {
266 | return true;
267 | }
268 |
269 | if ((min && (year === new Date(min).getFullYear() && month === new Date(min).getMonth()))
270 | || (max && (year === new Date(max).getFullYear() && month === new Date(max).getMonth()))) {
271 |
272 | if (min && year === new Date(min).getFullYear() && month === new Date(min).getMonth() && day < new Date(min).getDate()) {
273 | return false;
274 | }
275 |
276 | if (max && year === new Date(max).getFullYear() && month === new Date(max).getMonth() && day > new Date(max).getDate()) {
277 | return false;
278 | }
279 |
280 | return true;
281 | } else {
282 | return this.canSelectMonth(month, year, min, max);
283 | }
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/src/services/isMobile.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | @Injectable()
4 | export class IsMobileService {
5 | public isMobile: boolean;
6 |
7 | constructor() {
8 | this.isMobile = !!(window.navigator.userAgent.match(/Android/i)
9 | || navigator.userAgent.match(/webOS/i)
10 | || navigator.userAgent.match(/iPhone/i)
11 | || navigator.userAgent.match(/iPad/i)
12 | || navigator.userAgent.match(/iPod/i)
13 | || navigator.userAgent.match(/BlackBerry/i)
14 | || navigator.userAgent.match(/Windows Phone/i)
15 | || navigator.userAgent.match(/Opera Mini/i)
16 | || navigator.userAgent.match(/IEMobile/i));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/services/renderer.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, PLATFORM_ID, Inject, ElementRef } from '@angular/core';
2 | import { isPlatformBrowser } from '@angular/common';
3 |
4 | @Injectable()
5 | export class Renderer {
6 | constructor(
7 | @Inject(PLATFORM_ID) private platformId: Object
8 | ) {}
9 |
10 | invokeElementMethod(eleRef: ElementRef, method: string): void {
11 | if (isPlatformBrowser(this.platformId)) {
12 | eleRef.nativeElement[method]();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/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';
4 | import 'zone.js/dist/long-stack-trace-zone';
5 | import 'zone.js/dist/proxy.js';
6 | import 'zone.js/dist/sync-test';
7 | import 'zone.js/dist/jasmine-patch';
8 | import 'zone.js/dist/async-test';
9 | import 'zone.js/dist/fake-async-test';
10 | import { getTestBed } from '@angular/core/testing';
11 | import {
12 | BrowserDynamicTestingModule,
13 | platformBrowserDynamicTesting
14 | } from '@angular/platform-browser-dynamic/testing';
15 |
16 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
17 | declare var __karma__: any;
18 | declare var require: any;
19 |
20 | // Prevent Karma from running prematurely.
21 | __karma__.loaded = function () { };
22 |
23 | // First, initialize the Angular testing environment.
24 | getTestBed().initTestEnvironment(
25 | BrowserDynamicTestingModule,
26 | platformBrowserDynamicTesting()
27 | );
28 | // Then we find all the tests.
29 | const context = require.context('./', true, /\.spec\.ts$/);
30 | // And load the modules.
31 | context.keys().map(context);
32 | // Finally, start Karma to run the tests.
33 | __karma__.start();
34 |
--------------------------------------------------------------------------------
/src/typings/isMobile.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'ismobilejs' {
2 | var any: boolean;
3 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./src/app/",
4 | "downlevelIteration": true,
5 | "importHelpers": true,
6 | "outDir": "./dist",
7 | "sourceMap": false,
8 | "declaration": true,
9 | "moduleResolution": "node",
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "target": "es2015",
13 | "typeRoots": [
14 | "node_modules/@types"
15 | ],
16 | "lib": [
17 | "es2017",
18 | "dom"
19 | ],
20 | "paths": {
21 | "@angular/*": [
22 | "./ngx-datetimepicker/node_modules/@angular/*"
23 | ]
24 | },
25 | "module": "esnext"
26 | },
27 | "angularCompilerOptions": {
28 | "skipTemplateCodegen": true
29 | },
30 | "include": [
31 | "./src/"
32 | ],
33 | "exclude": [
34 | "**/app.module.ts",
35 | "**/app.component.ts"
36 | ]
37 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "callable-types": true,
7 | "class-name": true,
8 | "comment-format": [
9 | true,
10 | "check-space"
11 | ],
12 | "curly": true,
13 | "eofline": true,
14 | "forin": true,
15 | "import-blacklist": [
16 | true
17 | ],
18 | "import-spacing": true,
19 | "indent": [
20 | true,
21 | "spaces"
22 | ],
23 | "interface-over-type-literal": true,
24 | "label-position": true,
25 | "max-line-length": [
26 | true,
27 | 140
28 | ],
29 | "member-access": false,
30 | "member-ordering": [
31 | true,
32 | "static-before-instance",
33 | "variables-before-functions"
34 | ],
35 | "no-arg": true,
36 | "no-bitwise": true,
37 | "no-console": [
38 | true,
39 | "debug",
40 | "info",
41 | "time",
42 | "timeEnd",
43 | "trace"
44 | ],
45 | "no-construct": true,
46 | "no-debugger": true,
47 | "no-duplicate-variable": true,
48 | "no-empty": false,
49 | "no-empty-interface": true,
50 | "no-eval": true,
51 | "no-inferrable-types": false,
52 | "no-shadowed-variable": true,
53 | "no-string-literal": false,
54 | "no-string-throw": true,
55 | "no-switch-case-fall-through": true,
56 | "no-trailing-whitespace": true,
57 | "no-unused-expression": true,
58 | "no-use-before-declare": true,
59 | "no-var-keyword": true,
60 | "object-literal-sort-keys": false,
61 | "one-line": [
62 | true,
63 | "check-open-brace",
64 | "check-catch",
65 | "check-else",
66 | "check-whitespace"
67 | ],
68 | "prefer-const": true,
69 | "quotemark": [
70 | true,
71 | "single"
72 | ],
73 | "radix": true,
74 | "semicolon": [
75 | "always"
76 | ],
77 | "triple-equals": [
78 | true,
79 | "allow-null-check"
80 | ],
81 | "typedef-whitespace": [
82 | true,
83 | {
84 | "call-signature": "nospace",
85 | "index-signature": "nospace",
86 | "parameter": "nospace",
87 | "property-declaration": "nospace",
88 | "variable-declaration": "nospace"
89 | }
90 | ],
91 | "typeof-compare": true,
92 | "unified-signatures": true,
93 | "variable-name": false,
94 | "whitespace": [
95 | true,
96 | "check-branch",
97 | "check-decl",
98 | "check-operator",
99 | "check-separator",
100 | "check-type"
101 | ],
102 | "directive-selector": [
103 | true,
104 | "attribute",
105 | "app",
106 | "camelCase"
107 | ],
108 | "no-inputs-metadata-property": true,
109 | "no-outputs-metadata-property": true,
110 | "no-host-metadata-property": true,
111 | "no-input-rename": true,
112 | "no-output-rename": true,
113 | "use-lifecycle-interface": true,
114 | "use-pipe-transform-interface": true,
115 | "component-class-suffix": true,
116 | "directive-class-suffix": true,
117 | "no-access-missing-member": true,
118 | "templates-use-public": true,
119 | "invoke-injectable": true
120 | }
121 | }
--------------------------------------------------------------------------------