├── .editorconfig ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── angular.json ├── demo_color_picker.png ├── demo_datetime_picker.png ├── docs ├── 1.f3ad16d61e599dffd711.js ├── 2.b9fbe9f85b8d11834a4f.js ├── 3rdpartylicenses.txt ├── 7.41fa91ff381ded94ebbf.js ├── 8.0f3e80e189c7f862342c.js ├── 9.694bbf98a74c44edd123.js ├── assets │ ├── GitHub-Mark-Light-32px.png │ └── btn_donate_LG.gif ├── es2015-polyfills.0a4d3eb5b680939663ec.js ├── favicon.ico ├── index.html ├── main.4dff0bcdd07c8fab6561.js ├── polyfills.11b44398b075dad4d051.js ├── runtime.b578ea872a61529dd0e6.js └── styles.e2e6fbb9b2b944613a94.css ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── projects ├── color-picker │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── lib │ │ │ ├── color-picker.module.ts │ │ │ ├── components │ │ │ │ ├── color-canvas │ │ │ │ │ ├── base-color-canvas.ts │ │ │ │ │ ├── color-canvas.component.html │ │ │ │ │ ├── color-canvas.component.scss │ │ │ │ │ ├── color-canvas.component.ts │ │ │ │ │ └── color-slider │ │ │ │ │ │ ├── color-slider.component.html │ │ │ │ │ │ ├── color-slider.component.scss │ │ │ │ │ │ └── color-slider.component.ts │ │ │ │ ├── color-collection │ │ │ │ │ ├── color-collection.component.html │ │ │ │ │ ├── color-collection.component.scss │ │ │ │ │ └── color-collection.component.ts │ │ │ │ ├── color-palette │ │ │ │ │ ├── color-palette.component.html │ │ │ │ │ ├── color-palette.component.scss │ │ │ │ │ └── color-palette.component.ts │ │ │ │ ├── color-picker │ │ │ │ │ ├── color-input.component.ts │ │ │ │ │ ├── color-picker-content.component.html │ │ │ │ │ ├── color-picker-content.component.scss │ │ │ │ │ └── color-picker.component.ts │ │ │ │ ├── color-toggle │ │ │ │ │ ├── color-toggle.component.html │ │ │ │ │ ├── color-toggle.component.scss │ │ │ │ │ └── color-toggle.component.ts │ │ │ │ └── index.ts │ │ │ ├── directives │ │ │ │ ├── index.ts │ │ │ │ └── numeric-color-input.directive.ts │ │ │ ├── helpers │ │ │ │ ├── color-helpers.ts │ │ │ │ └── index.ts │ │ │ ├── models │ │ │ │ ├── color-input-format.ts │ │ │ │ ├── color.model.ts │ │ │ │ └── index.ts │ │ │ └── services │ │ │ │ ├── color-adapter.ts │ │ │ │ ├── color-formats.ts │ │ │ │ └── index.ts │ │ ├── public-api.ts │ │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ └── tslint.json ├── datetime-picker │ ├── LICENSE │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── lib │ │ │ ├── calendar-header.html │ │ │ ├── calendar.html │ │ │ ├── calendar.scss │ │ │ ├── calendar.ts │ │ │ ├── core │ │ │ │ ├── date-adapter.ts │ │ │ │ ├── native-date-adapter.ts │ │ │ │ ├── native-date-formats.ts │ │ │ │ └── native-date.module.ts │ │ │ ├── datetime-content.component.html │ │ │ ├── datetime-content.component.scss │ │ │ ├── datetime-input.ts │ │ │ ├── datetime-picker.component.ts │ │ │ ├── datetime-picker.module.ts │ │ │ ├── month-view.html │ │ │ ├── month-view.ts │ │ │ ├── multi-year-view.html │ │ │ ├── multi-year-view.ts │ │ │ ├── timepicker.component.html │ │ │ ├── timepicker.component.scss │ │ │ ├── timepicker.component.ts │ │ │ ├── timepicker.module.ts │ │ │ ├── utils │ │ │ │ └── date-utils.ts │ │ │ ├── year-view.html │ │ │ └── year-view.ts │ │ ├── public-api.ts │ │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ └── tslint.json └── moment-adapter │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── index.ts │ ├── lib │ │ ├── moment-adapter.module.ts │ │ ├── moment-adapter.ts │ │ └── moment-formats.ts │ └── public-api.ts │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ └── tslint.json ├── src ├── app │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.module.ts │ ├── demo-colorpicker │ │ ├── demo-colorpicker.component.html │ │ ├── demo-colorpicker.component.scss │ │ ├── demo-colorpicker.component.ts │ │ └── demo-colorpicker.module.ts │ ├── demo-datetime │ │ ├── demo-datetime.component.html │ │ ├── demo-datetime.component.scss │ │ ├── demo-datetime.component.ts │ │ └── demo-datetime.module.ts │ ├── demo-time │ │ ├── demo-time.component.html │ │ ├── demo-time.component.scss │ │ ├── demo-time.component.ts │ │ └── demo-time.module.ts │ ├── home │ │ ├── home.component.html │ │ ├── home.component.scss │ │ ├── home.component.ts │ │ └── index.ts │ └── shared │ │ └── _common.scss ├── assets │ ├── .gitkeep │ ├── GitHub-Mark-Light-32px.png │ └── btn_donate_LG.gif ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.scss ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | -------------------------------------------------------------------------------- /.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 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '10' 5 | 6 | env: 7 | - NG_CLI_ANALYTICS=false 8 | 9 | script: 10 | - npm run build 11 | 12 | notifications: 13 | email: false 14 | 15 | cache: 16 | directories: 17 | - node_modules 18 | 19 | after_success: 20 | - npm run codecov 21 | 22 | 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this library will be documented in this file. 3 | 4 | ## [2.0.3] - (Unrealeased) 5 | ### Fixed 6 | - Fix bugs ([#13](https://github.com/h2qutc/ngx-mat-datetime-picker/issues/13), [#20](https://github.com/h2qutc/ngx-mat-datetime-picker/issues/20), [#22](https://github.com/h2qutc/ngx-mat-datetime-picker/issues/22)). 7 | 8 | ### Changed 9 | - @Input **disableSecond** become @Input **showSeconds**. 10 | - Remove button Cancel (x). 11 | - Enable closing picker when clicking the backdrop. 12 | - Add @Input **enableMeridian** - Whether to display 12H or 24H mode. 13 | 14 | ## [2.0.2] - 2019-03-16 15 | ### Fixed 16 | - Fix bugs ([#37](https://github.com/h2qutc/ngx-mat-datetime-picker/issues/37), [#38](https://github.com/h2qutc/ngx-mat-datetime-picker/issues/38), [#39](https://github.com/h2qutc/ngx-mat-datetime-picker/issues/39)). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 HO Hong Quan 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⛔️ DEPRECATED 2 | ## This is no longer supported, please consider using the repository [angular-material-components/datetime-picker](https://github.com/h2qutc/angular-material-components) instead. 3 | 4 | # Ngx Material DatetimePicker, Timepicker for @angular/material 7.x, 8.x, 9.x 5 | 6 | [![Build Status](https://travis-ci.com/h2qutc/ngx-mat-datetime-picker.svg?branch=master)](https://travis-ci.com/h2qutc/ngx-mat-datetime-picker) 7 | [![codecov](https://codecov.io/gh/h2qutc/ngx-mat-datetime-picker/branch/master/graph/badge.svg)](https://codecov.io/gh/h2qutc/ngx-mat-datetime-picker) 8 | [![License](https://img.shields.io/npm/l/ngx-mat-datetime-picker.svg)](https://www.npmjs.com/package/ngx-mat-datetime-picker) 9 | [![npm version](https://badge.fury.io/js/ngx-mat-datetime-picker.svg)](https://badge.fury.io/for/js/ngx-mat-datetime-picker) 10 | 11 | ## Description 12 | 13 | A DatetimePicker like @angular/material [Datepicker](https://material.angular.io/components/datepicker/overview) by adding support for choosing time. 14 | 15 | [![button](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SAAY32BP5KPPC&source=url) 16 | 17 | ## DEMO 18 | 19 | @see [LIVE DEMO](https://h2qutc.github.io/ngx-mat-datetime-picker/) 20 | 21 | ![Alt Text](demo.png) 22 | 23 | ## Getting started 24 | 25 | #### Angular 7.x, 8.x: Install the version v2.x 26 | 27 | ``` 28 | npm install --save ngx-mat-datetime-picker@2.x 29 | ``` 30 | @see [DEMO stackblitz for Angular 7, Angular 8](https://stackblitz.com/edit/demo-ngx-mat-datetime-picker) 31 | 32 | #### Angular 9.x: Install the version v3.x 33 | 34 | ``` 35 | npm install --save ngx-mat-datetime-picker@3.x 36 | ``` 37 | @see [DEMO stackblitz for Angular 9](https://stackblitz.com/edit/demo-ngx-mat-datetime-picker-angular9) 38 | 39 | ## Setup 40 | Basically the same way the @angular/material Datepicker is configured and imported. 41 | 42 | ``` 43 | import { NgxMatDatetimePickerModule, NgxMatTimepickerModule } from 'ngx-mat-datetime-picker'; 44 | @NgModule({ 45 | ... 46 | imports: [ 47 | BrowserModule, 48 | HttpClientModule, 49 | BrowserAnimationsModule, 50 | MatDatepickerModule, 51 | MatInputModule, 52 | NgxMatTimepickerModule, 53 | FormsModule, 54 | ReactiveFormsModule, 55 | MatButtonModule, 56 | NgxMatDatetimePickerModule, 57 | ], 58 | ... 59 | }) 60 | export class AppModule { } 61 | ``` 62 | @see [src/app/app.module.ts](src/app/app.module.ts) 63 | 64 | ## Using the component 65 | 66 | The same API as @angular/material Datepicker (@see [API docs](https://material.angular.io/components/datepicker/api)) 67 | 68 | ### Datetime Picker (ngx-mat-datetime-picker) 69 | 70 | ``` 71 | 72 | 74 | 75 | 77 | 78 | 79 | ``` 80 | 81 | ### Timepicker (ngx-mat-timepicker) 82 | 83 | ``` 84 | 85 | 86 | 87 | 88 | 89 | 90 | ``` 91 | 92 | #### List of @Input 93 | 94 | | @Input | Type | Default value | Description | 95 | |--------------- |---------- |--------------- |---------------------------------------------------------------------- | 96 | | **disabled** | boolean | null | If true, the picker is readonly and can't be modified | 97 | | **showSpinners** | boolean | true | If true, the spinners above and below input are visible | 98 | | **showSeconds** | boolean | true | If true, it is not possible to select seconds | 99 | | **stepHour** | number | 1 | The number of hours to add/substract when clicking hour spinners | 100 | | **stepMinute** | number | 1 | The number of minutes to add/substract when clicking minute spinners | 101 | | **stepSecond** | number | 1 | The number of seconds to add/substract when clicking second spinners | 102 | | **color** | ThemePalette | undefined | Color palette to use on the datepicker's calendar. | 103 | | **enableMeridian** | boolean | false | Whether to display 12H or 24H mode. | 104 | | **touchUi** | boolean | false | Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather than a popup and elements have more padding to allow for bigger touch targets. | 105 | 106 | ## Choosing a date implementation and date format settings 107 | 108 | The datepicker was built to be date implementation agnostic. This means that it can be made to work with a variety of different date implementations. However it also means that developers need to make sure to provide the appropriate pieces for the datepicker to work with their chosen implementation. 109 | 110 | The easiest way to ensure this is to import one of the provided date modules: 111 | 112 | | | **NgxMatNativeDateModule** | **NgxMatMomentModule** | 113 | |----------------------- |---------------------------- |------------------------------------------------------------------------------------- | 114 | | **Date type** | Date | Moment | 115 | | **Supported locales** | en-US | [See project for details](https://github.com/moment/moment/tree/develop/src/locale) | 116 | | **Dependencies** | None | [Moment.js](https://momentjs.com/) | 117 | | **Import from** | ngx-mat-datetime-picker | [ngx-mat-moment-adapter](https://www.npmjs.com/package/ngx-mat-moment-adapter) | 118 | 119 | To use NgxMatMomentModule: 120 | ``` 121 | npm install --save ngx-mat-moment-adapter 122 | ``` 123 | 124 | Please note: NgxMatNativeDateModule is based off the functionality available in JavaScript's native Date object. Thus it is not suitable for many locales. One of the biggest shortcomings of the native Date object is the inability to set the parse format. 125 | 126 | We highly recommend using the **NgxMatMomentModule** or a custom **NgxMatDateAdapter** that works with the formatting/parsing library of your choice. 127 | 128 | For example: 129 | 130 | Creating a custom date adapter: 131 | 132 | ``` 133 | @Injectable() 134 | export class CustomDateAdapter extends NgxMatDateAdapter {...} 135 | // D can be Date, Moment or customized type 136 | ``` 137 | 138 | Creating a custom date adapter module 139 | ``` 140 | @NgModule({ 141 | providers: [ 142 | { 143 | provide: NgxMatDateAdapter, 144 | useClass: CustomDateAdapter, 145 | deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS] 146 | } 147 | ], 148 | }) 149 | export class CustomDateModule { } 150 | ``` 151 | 152 | 153 | ## Theming 154 | - @see @angular/material [Using a pre-built theme](https://material.angular.io/guide/theming#using-a-pre-built-theme) 155 | - Add the Material Design icon font to your index.html 156 | ``` 157 | 158 | ``` 159 | 160 | ## License 161 | MIT 162 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-material-components": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "style": "sass" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/angular-material-components", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "src/tsconfig.app.json", 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets" 28 | ], 29 | "styles": [ 30 | "src/styles.scss" 31 | ], 32 | "scripts": [], 33 | "es5BrowserSupport": true 34 | }, 35 | "configurations": { 36 | "production": { 37 | "fileReplacements": [ 38 | { 39 | "replace": "src/environments/environment.ts", 40 | "with": "src/environments/environment.prod.ts" 41 | } 42 | ], 43 | "optimization": true, 44 | "outputHashing": "all", 45 | "sourceMap": false, 46 | "extractCss": true, 47 | "namedChunks": false, 48 | "aot": true, 49 | "extractLicenses": true, 50 | "vendorChunk": false, 51 | "buildOptimizer": true, 52 | "budgets": [ 53 | { 54 | "type": "initial", 55 | "maximumWarning": "2mb", 56 | "maximumError": "5mb" 57 | } 58 | ] 59 | } 60 | } 61 | }, 62 | "serve": { 63 | "builder": "@angular-devkit/build-angular:dev-server", 64 | "options": { 65 | "browserTarget": "angular-material-components:build" 66 | }, 67 | "configurations": { 68 | "production": { 69 | "browserTarget": "angular-material-components:build:production" 70 | } 71 | } 72 | }, 73 | "extract-i18n": { 74 | "builder": "@angular-devkit/build-angular:extract-i18n", 75 | "options": { 76 | "browserTarget": "angular-material-components:build" 77 | } 78 | }, 79 | "test": { 80 | "builder": "@angular-devkit/build-angular:karma", 81 | "options": { 82 | "main": "src/test.ts", 83 | "polyfills": "src/polyfills.ts", 84 | "tsConfig": "src/tsconfig.spec.json", 85 | "karmaConfig": "src/karma.conf.js", 86 | "styles": [ 87 | "src/styles.scss" 88 | ], 89 | "scripts": [], 90 | "assets": [ 91 | "src/favicon.ico", 92 | "src/assets" 93 | ] 94 | } 95 | }, 96 | "lint": { 97 | "builder": "@angular-devkit/build-angular:tslint", 98 | "options": { 99 | "tsConfig": [ 100 | "src/tsconfig.app.json", 101 | "src/tsconfig.spec.json" 102 | ], 103 | "exclude": [ 104 | "**/node_modules/**" 105 | ] 106 | } 107 | } 108 | } 109 | }, 110 | "angular-material-components-e2e": { 111 | "root": "e2e/", 112 | "projectType": "application", 113 | "prefix": "", 114 | "architect": { 115 | "e2e": { 116 | "builder": "@angular-devkit/build-angular:protractor", 117 | "options": { 118 | "protractorConfig": "e2e/protractor.conf.js", 119 | "devServerTarget": "angular-material-components:serve" 120 | }, 121 | "configurations": { 122 | "production": { 123 | "devServerTarget": "angular-material-components:serve:production" 124 | } 125 | } 126 | }, 127 | "lint": { 128 | "builder": "@angular-devkit/build-angular:tslint", 129 | "options": { 130 | "tsConfig": "e2e/tsconfig.e2e.json", 131 | "exclude": [ 132 | "**/node_modules/**" 133 | ] 134 | } 135 | } 136 | } 137 | }, 138 | "NgxMatDatetimePicker": { 139 | "root": "projects/datetime-picker", 140 | "sourceRoot": "projects/datetime-picker/src", 141 | "projectType": "library", 142 | "prefix": "NgxMat", 143 | "architect": { 144 | "build": { 145 | "builder": "@angular-devkit/build-ng-packagr:build", 146 | "options": { 147 | "tsConfig": "projects/datetime-picker/tsconfig.lib.json", 148 | "project": "projects/datetime-picker/ng-package.json" 149 | } 150 | }, 151 | "test": { 152 | "builder": "@angular-devkit/build-angular:karma", 153 | "options": { 154 | "main": "projects/datetime-picker/src/test.ts", 155 | "tsConfig": "projects/datetime-picker/tsconfig.spec.json", 156 | "karmaConfig": "projects/datetime-picker/karma.conf.js" 157 | } 158 | }, 159 | "lint": { 160 | "builder": "@angular-devkit/build-angular:tslint", 161 | "options": { 162 | "tsConfig": [ 163 | "projects/datetime-picker/tsconfig.lib.json", 164 | "projects/datetime-picker/tsconfig.spec.json" 165 | ], 166 | "exclude": [ 167 | "**/node_modules/**" 168 | ] 169 | } 170 | } 171 | } 172 | }, 173 | "NgxMatMomentAdapter": { 174 | "root": "projects/moment-adapter", 175 | "sourceRoot": "projects/moment-adapter/src", 176 | "projectType": "library", 177 | "prefix": "NgxMat", 178 | "architect": { 179 | "build": { 180 | "builder": "@angular-devkit/build-ng-packagr:build", 181 | "options": { 182 | "tsConfig": "projects/moment-adapter/tsconfig.lib.json", 183 | "project": "projects/moment-adapter/ng-package.json" 184 | } 185 | }, 186 | "test": { 187 | "builder": "@angular-devkit/build-angular:karma", 188 | "options": { 189 | "main": "projects/moment-adapter/src/test.ts", 190 | "tsConfig": "projects/moment-adapter/tsconfig.spec.json", 191 | "karmaConfig": "projects/moment-adapter/karma.conf.js" 192 | } 193 | }, 194 | "lint": { 195 | "builder": "@angular-devkit/build-angular:tslint", 196 | "options": { 197 | "tsConfig": [ 198 | "projects/moment-adapter/tsconfig.lib.json", 199 | "projects/moment-adapter/tsconfig.spec.json" 200 | ], 201 | "exclude": [ 202 | "**/node_modules/**" 203 | ] 204 | } 205 | } 206 | } 207 | }, 208 | "NgxMatColorPicker": { 209 | "root": "projects/color-picker", 210 | "sourceRoot": "projects/color-picker/src", 211 | "projectType": "library", 212 | "prefix": "NgxMat", 213 | "architect": { 214 | "build": { 215 | "builder": "@angular-devkit/build-ng-packagr:build", 216 | "options": { 217 | "tsConfig": "projects/color-picker/tsconfig.lib.json", 218 | "project": "projects/color-picker/ng-package.json" 219 | } 220 | }, 221 | "test": { 222 | "builder": "@angular-devkit/build-angular:karma", 223 | "options": { 224 | "main": "projects/color-picker/src/test.ts", 225 | "tsConfig": "projects/color-picker/tsconfig.spec.json", 226 | "karmaConfig": "projects/color-picker/karma.conf.js" 227 | } 228 | }, 229 | "lint": { 230 | "builder": "@angular-devkit/build-angular:tslint", 231 | "options": { 232 | "tsConfig": [ 233 | "projects/color-picker/tsconfig.lib.json", 234 | "projects/color-picker/tsconfig.spec.json" 235 | ], 236 | "exclude": [ 237 | "**/node_modules/**" 238 | ] 239 | } 240 | } 241 | } 242 | } 243 | }, 244 | "defaultProject": "angular-material-components", 245 | "schematics": { 246 | "@schematics/angular:module": { 247 | "prefix": "ngx-mat", 248 | "spec": false 249 | }, 250 | "@schematics/angular:component": { 251 | "prefix": "ngx-mat", 252 | "styleext": "scss", 253 | "spec": false 254 | }, 255 | "@schematics/angular:directive": { 256 | "prefix": "ngx-mat", 257 | "spec": false 258 | }, 259 | "@schematics/angular:pipe": { 260 | "prefix": "ngx-mat", 261 | "spec": false 262 | }, 263 | "@schematics/angular:service": { 264 | "spec": false 265 | } 266 | } 267 | } -------------------------------------------------------------------------------- /demo_color_picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/demo_color_picker.png -------------------------------------------------------------------------------- /demo_datetime_picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/demo_datetime_picker.png -------------------------------------------------------------------------------- /docs/assets/GitHub-Mark-Light-32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/docs/assets/GitHub-Mark-Light-32px.png -------------------------------------------------------------------------------- /docs/assets/btn_donate_LG.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/docs/assets/btn_donate_LG.gif -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/docs/favicon.ico -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-material-components 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/runtime.b578ea872a61529dd0e6.js: -------------------------------------------------------------------------------- 1 | !function(e){function r(r){for(var n,f,i=r[0],a=r[1],c=r[2],p=0,d=[];p { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to ngx-mat-datetime-picker!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-material-components", 3 | "version": "2.0.3", 4 | "description": "Angular Material Datetime Picker", 5 | "author": "HO Hong Quan", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/h2qutc/ngx-mat-datetime-picker.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/h2qutc/ngx-mat-datetime-picker/issues" 12 | }, 13 | "homepage": "https://github.com/h2qutc/ngx-mat-datetime-picker", 14 | "keywords": [ 15 | "angular", 16 | "angular2", 17 | "angular-material", 18 | "angular material datetime picker", 19 | "angular material time picker", 20 | "datetime-picker", 21 | "datepicker", 22 | "timepicker", 23 | "ngx-mat", 24 | "ngx-mat-datetime-picker", 25 | "ngx-material-datetime-picker", 26 | "ngx-mat-timepicker" 27 | ], 28 | "license": "MIT", 29 | "scripts": { 30 | "ng": "ng", 31 | "start": "ng serve", 32 | "build": "ng build --prod", 33 | "test": "ng test", 34 | "lint": "ng lint", 35 | "e2e": "ng e2e", 36 | "codecov": "codecov" 37 | }, 38 | "private": false, 39 | "dependencies": { 40 | "@angular/animations": "~7.2.0", 41 | "@angular/cdk": "~7.3.7", 42 | "@angular/common": "~7.2.0", 43 | "@angular/compiler": "~7.2.0", 44 | "@angular/core": "~7.2.0", 45 | "@angular/forms": "~7.2.0", 46 | "@angular/material": "~7.3.7", 47 | "@angular/platform-browser": "~7.2.0", 48 | "@angular/platform-browser-dynamic": "~7.2.0", 49 | "@angular/router": "8.2.13", 50 | "core-js": "^2.5.4", 51 | "moment": "^2.24.0", 52 | "ngx-mat-moment-adapter": "0.0.1", 53 | "rxjs": "~6.3.3", 54 | "tslib": "^1.10.0", 55 | "zone.js": "~0.8.26" 56 | }, 57 | "devDependencies": { 58 | "@angular-devkit/build-angular": "~0.13.0", 59 | "@angular-devkit/build-ng-packagr": "~0.13.0", 60 | "@angular/cli": "~7.3.5", 61 | "@angular/compiler-cli": "~7.2.0", 62 | "@angular/language-service": "~7.2.0", 63 | "@types/jasmine": "~2.8.8", 64 | "@types/jasminewd2": "~2.0.3", 65 | "@types/node": "~8.9.4", 66 | "codecov": "^3.6.1", 67 | "codelyzer": "~4.5.0", 68 | "jasmine-core": "~2.99.1", 69 | "jasmine-spec-reporter": "~4.2.1", 70 | "karma": "~4.0.0", 71 | "karma-chrome-launcher": "~2.2.0", 72 | "karma-coverage-istanbul-reporter": "~2.0.1", 73 | "karma-jasmine": "~1.1.2", 74 | "karma-jasmine-html-reporter": "^0.2.2", 75 | "ng-packagr": "^4.2.0", 76 | "protractor": "~5.4.0", 77 | "ts-node": "~7.0.0", 78 | "tsickle": "^0.37.0", 79 | "tslib": "^1.9.0", 80 | "tslint": "~5.11.0", 81 | "typescript": "~3.2.2" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /projects/color-picker/README.md: -------------------------------------------------------------------------------- 1 | # Angular Material Color Picker 2 | 3 | ## Description 4 | 5 | A Angular Material Color Picker. 6 | 7 | [![button](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SAAY32BP5KPPC&source=url) 8 | 9 | ## DEMO 10 | 11 | @see [DEMO stackblitz](https://stackblitz.com/edit/demo-ngx-mat-color-picker) 12 | 13 | ![Alt Text](demo_color_picker.png) 14 | 15 | ## Getting started 16 | ``` 17 | npm install --save @angular-material-components/color-picker 18 | ``` 19 | 20 | ## Setup 21 | 22 | ``` 23 | import { MAT_COLOR_FORMATS, NgxMatColorPickerModule, NGX_MAT_COLOR_FORMATS } from '@angular-material-components/color-picker'; 24 | 25 | @NgModule({ 26 | ... 27 | imports: [ 28 | ... 29 | NgxMatColorPickerModule 30 | ], 31 | providers: [ 32 | { provide: MAT_COLOR_FORMATS, useValue: NGX_MAT_COLOR_FORMATS } 33 | ], 34 | ... 35 | }) 36 | export class AppModule { } 37 | ``` 38 | @see [src/app/app.module.ts](src/app/app.module.ts) 39 | 40 | ## Using the component 41 | 42 | The same API as @angular/material Datepicker (@see [API docs](https://material.angular.io/components/datepicker/api)) 43 | 44 | ### Color Picker (ngx-mat-color-picker) 45 | 46 | ``` 47 | 48 | 49 | 50 | 51 | 52 | ``` 53 | 54 | #### List of @Input 55 | 56 | | @Input | Type | Default value | Description | 57 | |--------------- |---------- |--------------- |---------------------------------------------------------------------- | 58 | | **disabled** | boolean | null | If true, the picker is readonly and can't be modified | 59 | | **color** | ThemePalette | undefined | Color palette to use on the datepicker's calendar. 60 | | **touchUi** | boolean | false | Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather than a popup and elements have more padding to allow for bigger touch targets. | 61 | 62 | ## Theming 63 | - @see @angular/material [Using a pre-built theme](https://material.angular.io/guide/theming#using-a-pre-built-theme) 64 | - Add the Material Design icon font to your index.html 65 | ``` 66 | 67 | ``` 68 | 69 | ## License 70 | MIT -------------------------------------------------------------------------------- /projects/color-picker/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/color-picker'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/color-picker/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/@angular-material-components/color-picker", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/color-picker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angular-material-components/color-picker", 3 | "version": "2.0.0", 4 | "description": "Angular Material Color Picker", 5 | "author": "HO Hong Quan", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/h2qutc/ngx-mat-datetime-picker.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/h2qutc/ngx-mat-datetime-picker/issues" 12 | }, 13 | "homepage": "https://github.com/h2qutc/ngx-mat-datetime-picker", 14 | "keywords": [ 15 | "angular", 16 | "angular2", 17 | "angular-material", 18 | "angular material color picker", 19 | "color-picker", 20 | "colorpicker", 21 | "ngx-mat", 22 | "ngx-mat-color-picker", 23 | "ngx-material-color-picker" 24 | ], 25 | "license": "MIT", 26 | "peerDependencies": { 27 | "@angular/platform-browser": "0.0.0-NG", 28 | "@angular/common": "0.0.0-NG", 29 | "@angular/core": "0.0.0-NG", 30 | "@angular/forms": "0.0.0-NG", 31 | "@angular/material": "0.0.0-PLACEHOLDER", 32 | "@angular/cdk": "0.0.0-PLACEHOLDER", 33 | "tslib": "0.0.0-TSLIB" 34 | } 35 | } -------------------------------------------------------------------------------- /projects/color-picker/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './public-api'; -------------------------------------------------------------------------------- /projects/color-picker/src/lib/color-picker.module.ts: -------------------------------------------------------------------------------- 1 | import { PortalModule } from '@angular/cdk/portal'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NgModule } from '@angular/core'; 4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatCardModule } from '@angular/material/card'; 7 | import { MatDialogModule } from '@angular/material/dialog'; 8 | import { MatIconModule } from '@angular/material/icon'; 9 | import { MatInputModule } from '@angular/material/input'; 10 | import { MatRadioModule } from '@angular/material/radio'; 11 | import { 12 | NgxMatColorCanvasComponent, 13 | NgxMatColorCollectionComponent, 14 | NgxMatColorPaletteComponent, 15 | NgxMatColorPickerComponent, 16 | NgxMatColorPickerInput, NgxMatColorSliderComponent, 17 | NgxMatColorToggleComponent, 18 | NGX_MAT_COLOR_PICKER_SCROLL_STRATEGY_FACTORY_PROVIDER, 19 | NgxMatColorPickerContentComponent 20 | } from './components'; 21 | import { NumericColorInputDirective } from './directives'; 22 | import { ColorAdapter } from './services'; 23 | 24 | @NgModule({ 25 | declarations: [ 26 | NgxMatColorPaletteComponent, 27 | NgxMatColorCanvasComponent, 28 | NgxMatColorCollectionComponent, 29 | NgxMatColorSliderComponent, 30 | NumericColorInputDirective, 31 | NgxMatColorPickerContentComponent, 32 | NgxMatColorPickerComponent, 33 | NgxMatColorToggleComponent, 34 | NgxMatColorPickerInput 35 | ], 36 | imports: [ 37 | CommonModule, 38 | MatInputModule, 39 | MatButtonModule, 40 | MatCardModule, 41 | MatRadioModule, 42 | FormsModule, 43 | ReactiveFormsModule, 44 | MatDialogModule, 45 | PortalModule, 46 | MatIconModule 47 | ], 48 | exports: [ 49 | NgxMatColorToggleComponent, 50 | NgxMatColorPickerInput, 51 | NgxMatColorPickerComponent 52 | ], 53 | entryComponents: [ 54 | NgxMatColorPickerContentComponent 55 | ], 56 | providers: [ 57 | ColorAdapter, 58 | NGX_MAT_COLOR_PICKER_SCROLL_STRATEGY_FACTORY_PROVIDER 59 | ] 60 | }) 61 | export class NgxMatColorPickerModule { } 62 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-canvas/base-color-canvas.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter, Output, Input, OnDestroy, AfterViewInit, NgZone } from '@angular/core'; 2 | import { Color } from '../../models'; 3 | import { Subject } from 'rxjs'; 4 | 5 | export abstract class NgxMatBaseColorCanvas implements OnDestroy, AfterViewInit { 6 | 7 | @Output() colorChanged: EventEmitter = new EventEmitter(); 8 | @Input() color: Color; 9 | 10 | canvas: HTMLCanvasElement; 11 | 12 | elementId: string; 13 | 14 | ctx: CanvasRenderingContext2D; 15 | width: number; 16 | height: number; 17 | 18 | x: number = 0; 19 | y: number = 0; 20 | 21 | drag = false; 22 | 23 | protected _destroyed: Subject = new Subject(); 24 | 25 | constructor(protected zone: NgZone, elementId: string) { 26 | this.elementId = elementId; 27 | } 28 | 29 | ngOnDestroy(): void { 30 | this._destroyed.next(); 31 | this._destroyed.complete(); 32 | } 33 | 34 | ngAfterViewInit(): void { 35 | this.canvas = document.getElementById(this.elementId); 36 | this.ctx = this.canvas.getContext('2d'); 37 | this.width = this.canvas.width; 38 | this.height = this.canvas.height; 39 | this.draw(); 40 | } 41 | 42 | protected draw() { 43 | this.ctx.clearRect(0, 0, this.width, this.height); 44 | this.ctx.rect(0, 0, this.width, this.height); 45 | this.fillGradient(); 46 | if (this.y != 0) { 47 | this.redrawIndicator(this.x, this.y); 48 | } 49 | } 50 | 51 | 52 | public onMousedown(e: MouseEvent) { 53 | this.drag = true; 54 | this.changeColor(e); 55 | 56 | this.zone.runOutsideAngular(() => { 57 | this.canvas.addEventListener('mousemove', this.onMousemove.bind(this)); 58 | }) 59 | } 60 | 61 | public onMousemove(e: MouseEvent) { 62 | if (this.drag) { 63 | this.zone.run(() =>{ 64 | this.changeColor(e) ; 65 | }) 66 | } 67 | } 68 | 69 | public onMouseup(e: MouseEvent) { 70 | this.drag = false; 71 | this.canvas.removeEventListener('mousemove', this.onMousemove); 72 | } 73 | 74 | public emitChange(color: Color) { 75 | this.colorChanged.emit(color); 76 | } 77 | 78 | abstract changeColor(e: MouseEvent): void; 79 | abstract fillGradient(): void; 80 | abstract redrawIndicator(x: number, y: number): void; 81 | 82 | } 83 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-canvas/color-canvas.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 6 | 7 | 8 | 9 |
10 | 11 | R 12 | 13 | 14 | 15 | 16 | G 17 | 18 | 19 | 20 | 21 | B 22 | 23 | 24 |
25 |
26 | 27 |
28 | 29 | 30 | HEX6 31 | 32 | 33 | 34 | A 35 | 36 | 37 |
38 |
-------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-canvas/color-canvas.component.scss: -------------------------------------------------------------------------------- 1 | 2 | $sizeButtonPreview: 40px; 3 | 4 | .ngx-mat-color-canvas { 5 | .row { 6 | display: flex; 7 | &:first-of-type { 8 | height: 210px; 9 | margin-bottom: 16px; 10 | .card { 11 | height: 180px; 12 | } 13 | } 14 | 15 | canvas:hover { 16 | cursor: crosshair; 17 | } 18 | 19 | .zone { 20 | display: flex; 21 | 22 | &-strip { 23 | flex-basis: auto; 24 | margin-left: 10px; 25 | } 26 | 27 | &-inputs { 28 | display: flex; 29 | width: 40px; 30 | flex-direction: column; 31 | margin-left: 16px; 32 | margin-top: 16px; 33 | } 34 | } 35 | 36 | &:nth-of-type(2) { 37 | display: flex; 38 | .preview { 39 | min-width: $sizeButtonPreview; 40 | min-height: $sizeButtonPreview; 41 | height: $sizeButtonPreview; 42 | width: $sizeButtonPreview; 43 | } 44 | 45 | .mat-form-field { 46 | margin-left: 16px; 47 | &:first-of-type { 48 | width: 180px; 49 | } 50 | 51 | &:last-of-type { 52 | width: 40px; 53 | } 54 | } 55 | } 56 | } 57 | 58 | .mat-form-field { 59 | &-label { 60 | font-weight: bold; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-canvas/color-canvas.component.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, Component, NgZone, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core'; 2 | import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'; 3 | import { merge } from 'rxjs'; 4 | import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'; 5 | import { getColorAtPosition, matchers, stringInputToObject } from '../../helpers'; 6 | import { Color } from '../../models'; 7 | import { NgxMatBaseColorCanvas } from './base-color-canvas'; 8 | 9 | const RADIUS_NOB = 5; 10 | 11 | @Component({ 12 | selector: 'ngx-mat-color-canvas', 13 | templateUrl: './color-canvas.component.html', 14 | styleUrls: ['./color-canvas.component.scss'], 15 | encapsulation: ViewEncapsulation.None, 16 | host: { 17 | 'class': 'ngx-mat-color-canvas' 18 | } 19 | }) 20 | export class NgxMatColorCanvasComponent extends NgxMatBaseColorCanvas 21 | implements OnInit, AfterViewInit, OnChanges, OnDestroy { 22 | 23 | private _baseColor: Color; 24 | 25 | get rCtrl(): AbstractControl { 26 | return this.formGroup.get('r'); 27 | } 28 | 29 | get gCtrl(): AbstractControl { 30 | return this.formGroup.get('g'); 31 | } 32 | 33 | get bCtrl(): AbstractControl { 34 | return this.formGroup.get('b'); 35 | } 36 | 37 | get aCtrl(): AbstractControl { 38 | return this.formGroup.get('a'); 39 | } 40 | 41 | get hexCtrl(): AbstractControl { 42 | return this.formGroup.get('hex'); 43 | } 44 | 45 | _resetBaseColor = true; 46 | 47 | formGroup: FormGroup; 48 | 49 | rgba: string; 50 | 51 | constructor(protected zone: NgZone) { 52 | super(zone, 'color-block'); 53 | this.formGroup = new FormGroup({ 54 | r: new FormControl(null, [Validators.required]), 55 | g: new FormControl(null, [Validators.required]), 56 | b: new FormControl(null, [Validators.required]), 57 | a: new FormControl(null, [Validators.required]), 58 | hex: new FormControl(null, [Validators.required, Validators.pattern(matchers.hex6)]), 59 | }); 60 | } 61 | 62 | ngOnInit() { 63 | 64 | const rgbaCtrl$ = merge(this.rCtrl.valueChanges, this.gCtrl.valueChanges, 65 | this.bCtrl.valueChanges, this.aCtrl.valueChanges); 66 | rgbaCtrl$.pipe(takeUntil(this._destroyed), debounceTime(400), distinctUntilChanged()) 67 | .subscribe(_ => { 68 | const color = new Color(Number(this.rCtrl.value), 69 | Number(this.gCtrl.value), Number(this.bCtrl.value), Number(this.aCtrl.value)); 70 | this.emitChange(color); 71 | }); 72 | 73 | const hexCtrl$ = this.hexCtrl.valueChanges; 74 | hexCtrl$.pipe(takeUntil(this._destroyed), debounceTime(400), distinctUntilChanged()) 75 | .subscribe(hex => { 76 | const obj = stringInputToObject(hex); 77 | if (obj != null) { 78 | const color = new Color(obj.r, obj.g, obj.b, obj.a); 79 | this.emitChange(color); 80 | } 81 | }) 82 | } 83 | 84 | ngOnChanges(changes: SimpleChanges): void { 85 | if (changes.color && changes.color.currentValue) { 86 | this.updateForm(changes.color.currentValue); 87 | if (this._resetBaseColor) { 88 | this._baseColor = changes.color.currentValue; 89 | } 90 | 91 | this._resetBaseColor = true; 92 | 93 | if (!changes.color.firstChange) { 94 | this.draw(); 95 | } 96 | } 97 | } 98 | 99 | private updateForm(val: Color): void { 100 | const config = { emitEvent: false }; 101 | this.rCtrl.setValue(val.r, config); 102 | this.gCtrl.setValue(val.g, config); 103 | this.bCtrl.setValue(val.b, config); 104 | this.aCtrl.setValue(val.a, config); 105 | this.hexCtrl.setValue(val.hex, config); 106 | } 107 | 108 | public redrawIndicator(x: number, y: number) { 109 | this.ctx.beginPath(); 110 | this.ctx.strokeStyle = 'white'; 111 | this.ctx.arc(x, y, RADIUS_NOB, 0, 2 * Math.PI, false); 112 | this.ctx.stroke(); 113 | this.ctx.closePath(); 114 | } 115 | 116 | public fillGradient() { 117 | this.ctx.fillStyle = this._baseColor ? this._baseColor.rgba : 'rgba(255,255,255,1)'; 118 | this.ctx.fillRect(0, 0, this.width, this.height); 119 | 120 | const grdWhite = this.ctx.createLinearGradient(0, 0, this.width, 0); 121 | grdWhite.addColorStop(0, 'rgba(255,255,255,1)'); 122 | grdWhite.addColorStop(1, 'rgba(255,255,255,0)'); 123 | this.ctx.fillStyle = grdWhite; 124 | this.ctx.fillRect(0, 0, this.width, this.height); 125 | 126 | const grdBlack = this.ctx.createLinearGradient(0, 0, 0, this.height); 127 | grdBlack.addColorStop(0, 'rgba(0,0,0,0)'); 128 | grdBlack.addColorStop(1, 'rgba(0,0,0,1)'); 129 | this.ctx.fillStyle = grdBlack; 130 | this.ctx.fillRect(0, 0, this.width, this.height); 131 | } 132 | 133 | public onSliderColorChanged(c: Color) { 134 | this._baseColor = c; 135 | this.color = c; 136 | this.fillGradient(); 137 | this.emitChange(c); 138 | } 139 | 140 | public changeColor(e: MouseEvent): void { 141 | this.x = e.offsetX; 142 | this.y = e.offsetY; 143 | this._resetBaseColor = false; 144 | this.draw(); 145 | const { r, g, b } = getColorAtPosition(this.ctx, e.offsetX, e.offsetY); 146 | this.emitChange(new Color(r, g, b)); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-canvas/color-slider/color-slider.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-canvas/color-slider/color-slider.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/projects/color-picker/src/lib/components/color-canvas/color-slider/color-slider.component.scss -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-canvas/color-slider/color-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Output, EventEmitter, NgZone } from '@angular/core'; 2 | import { Color } from '../../../models'; 3 | import { getColorAtPosition } from '../../../helpers'; 4 | import { NgxMatBaseColorCanvas } from '../base-color-canvas'; 5 | 6 | @Component({ 7 | selector: 'ngx-mat-color-slider', 8 | templateUrl: './color-slider.component.html', 9 | styleUrls: ['./color-slider.component.scss'] 10 | }) 11 | export class NgxMatColorSliderComponent extends NgxMatBaseColorCanvas implements OnInit { 12 | 13 | constructor(protected zone: NgZone) { 14 | super(zone,'color-strip'); 15 | } 16 | 17 | ngOnInit() { 18 | 19 | } 20 | 21 | ngAfterViewInit(): void { 22 | super.ngAfterViewInit(); 23 | } 24 | 25 | public fillGradient() { 26 | const grd = this.ctx.createLinearGradient(0, 0, 0, this.height); 27 | grd.addColorStop(0, 'rgba(255, 0, 0, 1)'); 28 | grd.addColorStop(0.17, 'rgba(255, 255, 0, 1)'); 29 | grd.addColorStop(0.34, 'rgba(0, 255, 0, 1)'); 30 | grd.addColorStop(0.51, 'rgba(0, 255, 255, 1)'); 31 | grd.addColorStop(0.68, 'rgba(0, 0, 255, 1)'); 32 | grd.addColorStop(0.85, 'rgba(255, 0, 255, 1)'); 33 | grd.addColorStop(1, 'rgba(255, 0, 0, 1)'); 34 | 35 | this.ctx.fillStyle = grd; 36 | this.ctx.fill(); 37 | } 38 | 39 | public redrawIndicator(x: number, y: number) { 40 | this.ctx.beginPath(); 41 | this.ctx.strokeStyle = 'white'; 42 | this.ctx.lineWidth = 2; 43 | this.ctx.arc(7.5, y, 7.5, 0, 2 * Math.PI, false); 44 | this.ctx.stroke(); 45 | this.ctx.closePath(); 46 | } 47 | 48 | public changeColor(e: MouseEvent) { 49 | this.x = e.offsetX; 50 | this.y = e.offsetY; 51 | this.draw(); 52 | const { r, g, b } = getColorAtPosition(this.ctx, e.offsetX, e.offsetY); 53 | this.emitChange(new Color(r, g, b)); 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-collection/color-collection.component.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 |
7 | 10 |
-------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-collection/color-collection.component.scss: -------------------------------------------------------------------------------- 1 | .ngx-mat-color-collection{ 2 | .btn-color{ 3 | height: 20px; 4 | width: 20px; 5 | margin-right: 11px; 6 | box-shadow: none; 7 | opacity: 0.3; 8 | &.active{ 9 | box-shadow: 0 3px 5px -1px rgba(0,0,0,.2), 0 6px 10px 0 rgba(0,0,0,.14), 0 1px 18px 0 rgba(0,0,0,.12); 10 | opacity: 1; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-collection/color-collection.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, OnInit, Output, ViewEncapsulation, Input } from '@angular/core'; 2 | import { Color } from '../../models'; 3 | import { BASIC_COLORS, stringInputToObject } from '../../helpers'; 4 | 5 | @Component({ 6 | selector: 'ngx-mat-color-collection', 7 | templateUrl: './color-collection.component.html', 8 | styleUrls: ['./color-collection.component.scss'], 9 | encapsulation: ViewEncapsulation.None, 10 | host: { 11 | 'class': 'ngx-mat-color-collection' 12 | } 13 | }) 14 | export class NgxMatColorCollectionComponent implements OnInit { 15 | 16 | @Output() colorChanged: EventEmitter = new EventEmitter(); 17 | 18 | @Input() 19 | set color(c: Color) { 20 | if (c) { 21 | this.selectedColor = c.toHexString(); 22 | } 23 | } 24 | 25 | selectedColor: string; 26 | 27 | colors1: string[] = BASIC_COLORS.slice(0, 8); 28 | colors2: string[] = BASIC_COLORS.slice(8, 16); 29 | 30 | constructor() { } 31 | 32 | ngOnInit() { 33 | } 34 | 35 | select(hex: string) { 36 | this.selectedColor = hex; 37 | const { r, g, b, a } = stringInputToObject(hex); 38 | this.colorChanged.emit(new Color(r, g, b, a)); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-palette/color-palette.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-palette/color-palette.component.scss: -------------------------------------------------------------------------------- 1 | .ngx-mat-color-palette { 2 | .actions { 3 | margin-top: 10px; 4 | display: flex; 5 | .left { 6 | display: flex; 7 | flex-direction: column; 8 | margin-right: 15px; 9 | .preview { 10 | flex: 2 1 auto; 11 | margin-bottom: 10px; 12 | } 13 | } 14 | 15 | .right { 16 | display: flex; 17 | width: 40px; 18 | flex-direction: column; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-palette/color-palette.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewEncapsulation, Output, EventEmitter, Input } from '@angular/core'; 2 | import { Color } from '../../models'; 3 | 4 | @Component({ 5 | selector: 'ngx-mat-color-palette', 6 | templateUrl: 'color-palette.component.html', 7 | styleUrls: ['color-palette.component.scss'], 8 | encapsulation: ViewEncapsulation.None, 9 | host: { 10 | 'class': 'ngx-mat-color-palette' 11 | } 12 | }) 13 | export class NgxMatColorPaletteComponent implements OnInit { 14 | 15 | @Output() colorChanged: EventEmitter = new EventEmitter(); 16 | 17 | @Input() color: Color; 18 | 19 | constructor() { } 20 | 21 | ngOnInit() { 22 | } 23 | 24 | public handleColorChanged(color: Color) { 25 | this.colorChanged.emit(color); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-picker/color-input.component.ts: -------------------------------------------------------------------------------- 1 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; 2 | import { DOWN_ARROW } from '@angular/cdk/keycodes'; 3 | import { Directive, ElementRef, EventEmitter, forwardRef, Inject, Input, OnDestroy, OnInit, Optional, Output } from '@angular/core'; 4 | import { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator, ValidatorFn, Validators } from '@angular/forms'; 5 | import { ThemePalette } from '@angular/material/core'; 6 | import { MatFormField } from '@angular/material/form-field'; 7 | import { MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input'; 8 | import { Subscription } from 'rxjs'; 9 | import { createMissingDateImplError } from '../../helpers'; 10 | import { Color } from '../../models'; 11 | import { ColorAdapter, MatColorFormats, MAT_COLOR_FORMATS } from '../../services'; 12 | import { NgxMatColorPickerComponent } from './color-picker.component'; 13 | 14 | export class NgxMatColorPickerInputEvent { 15 | /** The new value for the target colorpicker input. */ 16 | value: Color | null; 17 | 18 | constructor( 19 | /** Reference to the colorpicker input component that emitted the event. */ 20 | public target: NgxMatColorPickerInput, 21 | /** Reference to the native input element associated with the colorpicker input. */ 22 | public targetElement: HTMLElement) { 23 | this.value = this.target.value; 24 | } 25 | } 26 | 27 | 28 | export const MAT_COLORPICKER_VALUE_ACCESSOR: any = { 29 | provide: NG_VALUE_ACCESSOR, 30 | useExisting: forwardRef(() => NgxMatColorPickerInput), 31 | multi: true 32 | }; 33 | 34 | 35 | export const MAT_COLORPICKER_VALIDATORS: any = { 36 | provide: NG_VALIDATORS, 37 | useExisting: forwardRef(() => NgxMatColorPickerInput), 38 | multi: true 39 | }; 40 | 41 | @Directive({ 42 | selector: 'input[ngxMatColorPicker]', 43 | providers: [ 44 | MAT_COLORPICKER_VALUE_ACCESSOR, 45 | MAT_COLORPICKER_VALIDATORS, 46 | { provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: NgxMatColorPickerInput }, 47 | ], 48 | host: { 49 | '[attr.aria-haspopup]': '_picker ? "dialog" : null', 50 | '[attr.aria-owns]': '(_picker?.opened && _picker.id) || null', 51 | '[disabled]': 'disabled', 52 | '(input)': '_onInput($event.target.value)', 53 | '(change)': '_onChange()', 54 | '(blur)': '_onBlur()', 55 | '(keydown)': '_onKeydown($event)', 56 | }, 57 | exportAs: 'ngxMatColorPickerInput', 58 | }) 59 | export class NgxMatColorPickerInput implements ControlValueAccessor, OnInit, OnDestroy, Validator { 60 | 61 | @Input() 62 | set ngxMatColorPicker(value: NgxMatColorPickerComponent) { 63 | if (!value) { 64 | return; 65 | } 66 | 67 | this._picker = value; 68 | this._picker.registerInput(this); 69 | this._pickerSubscription.unsubscribe(); 70 | 71 | this._pickerSubscription = this._picker._selectedChanged.subscribe((selected: Color) => { 72 | this.value = selected; 73 | this._cvaOnChange(selected); 74 | this._onTouched(); 75 | this.colorInput.emit(new NgxMatColorPickerInputEvent(this, this._elementRef.nativeElement)); 76 | this.colorChange.emit(new NgxMatColorPickerInputEvent(this, this._elementRef.nativeElement)); 77 | }); 78 | } 79 | _picker: NgxMatColorPickerComponent; 80 | 81 | /** Whether the colorpicker-input is disabled. */ 82 | @Input() 83 | get disabled(): boolean { return !!this._disabled; } 84 | set disabled(value: boolean) { 85 | const newValue = coerceBooleanProperty(value); 86 | const element = this._elementRef.nativeElement; 87 | 88 | if (this._disabled !== newValue) { 89 | this._disabled = newValue; 90 | this._disabledChange.emit(newValue); 91 | } 92 | 93 | // We need to null check the `blur` method, because it's undefined during SSR. 94 | if (newValue && element.blur) { 95 | // Normally, native input elements automatically blur if they turn disabled. This behavior 96 | // is problematic, because it would mean that it triggers another change detection cycle, 97 | // which then causes a changed after checked error if the input element was focused before. 98 | element.blur(); 99 | } 100 | } 101 | private _disabled: boolean; 102 | 103 | /** The value of the input. */ 104 | @Input() 105 | get value(): Color | null { return this._value; } 106 | set value(value: Color | null) { 107 | const oldValue = this.value; 108 | this._value = value; 109 | this._formatValue(value); 110 | 111 | if (!this._adapter.sameColor(oldValue, value)) { 112 | this._valueChange.emit(value); 113 | } 114 | 115 | } 116 | private _value: Color | null; 117 | 118 | /** Emits when a `change` event is fired on this ``. */ 119 | @Output() readonly colorChange: EventEmitter = 120 | new EventEmitter(); 121 | 122 | /** Emits when an `input` event is fired on this ``. */ 123 | @Output() readonly colorInput: EventEmitter = 124 | new EventEmitter(); 125 | 126 | /** Emits when the disabled state has changed */ 127 | _disabledChange = new EventEmitter(); 128 | 129 | /** Emits when the value changes (either due to user input or programmatic change). */ 130 | _valueChange = new EventEmitter(); 131 | 132 | _onTouched = () => { }; 133 | 134 | private _cvaOnChange: (value: any) => void = () => { }; 135 | 136 | private _validatorOnChange = () => { }; 137 | 138 | private _pickerSubscription = Subscription.EMPTY; 139 | 140 | /** The combined form control validator for this input. */ 141 | private _validator: ValidatorFn | null = 142 | Validators.compose([]); 143 | 144 | /** Whether the last value set on the input was valid. */ 145 | private _lastValueValid = false; 146 | 147 | constructor(private _elementRef: ElementRef, 148 | @Optional() private _formField: MatFormField, 149 | @Optional() @Inject(MAT_COLOR_FORMATS) private _colorFormats: MatColorFormats, 150 | private _adapter: ColorAdapter) { 151 | if (!this._colorFormats) { 152 | throw createMissingDateImplError('MAT_COLOR_FORMATS'); 153 | } 154 | } 155 | 156 | /** Returns the palette used by the input's form field, if any. */ 157 | public getThemePalette(): ThemePalette { 158 | return this._formField ? this._formField.color : undefined; 159 | } 160 | 161 | 162 | registerOnValidatorChange(fn: () => void): void { 163 | this._validatorOnChange = fn; 164 | } 165 | 166 | 167 | validate(c: AbstractControl): ValidationErrors | null { 168 | return this._validator ? this._validator(c) : null; 169 | } 170 | 171 | /** 172 | * @deprecated 173 | * @breaking-change 8.0.0 Use `getConnectedOverlayOrigin` instead 174 | */ 175 | getPopupConnectionElementRef(): ElementRef { 176 | return this.getConnectedOverlayOrigin(); 177 | } 178 | 179 | /** 180 | * Gets the element that the colorpicker popup should be connected to. 181 | * @return The element to connect the popup to. 182 | */ 183 | getConnectedOverlayOrigin(): ElementRef { 184 | return this._formField ? this._formField.getConnectedOverlayOrigin() : this._elementRef; 185 | } 186 | 187 | 188 | ngOnInit() { 189 | } 190 | 191 | ngOnDestroy(): void { 192 | this._pickerSubscription.unsubscribe(); 193 | this._valueChange.complete(); 194 | this._disabledChange.complete(); 195 | } 196 | 197 | // Implemented as part of ControlValueAccessor. 198 | writeValue(value: Color): void { 199 | this.value = value; 200 | } 201 | 202 | // Implemented as part of ControlValueAccessor. 203 | registerOnChange(fn: (value: any) => void): void { 204 | this._cvaOnChange = fn; 205 | } 206 | 207 | // Implemented as part of ControlValueAccessor. 208 | registerOnTouched(fn: () => void): void { 209 | this._onTouched = fn; 210 | } 211 | 212 | // Implemented as part of ControlValueAccessor. 213 | setDisabledState(isDisabled: boolean): void { 214 | this.disabled = isDisabled; 215 | } 216 | 217 | _onChange() { 218 | this.colorChange.emit(new NgxMatColorPickerInputEvent(this, this._elementRef.nativeElement)); 219 | } 220 | 221 | _onKeydown(event: KeyboardEvent) { 222 | const isAltDownArrow = event.altKey && event.keyCode === DOWN_ARROW; 223 | 224 | if (this._picker && isAltDownArrow && !this._elementRef.nativeElement.readOnly) { 225 | this._picker.open(); 226 | event.preventDefault(); 227 | } 228 | } 229 | 230 | /** Handles blur events on the input. */ 231 | _onBlur() { 232 | // Reformat the input only if we have a valid value. 233 | if (this.value) { 234 | this._formatValue(this.value); 235 | } 236 | 237 | this._onTouched(); 238 | } 239 | 240 | /** Formats a value and sets it on the input element. */ 241 | private _formatValue(value: Color | null) { 242 | this._elementRef.nativeElement.value = value ? this._adapter.format(value, this._colorFormats.display.colorInput) : ''; 243 | } 244 | 245 | _onInput(value: string) { 246 | const lastValueWasValid = this._lastValueValid; 247 | const nextValue = this._adapter.parse(value); 248 | 249 | if (!this._adapter.sameColor(nextValue, this._value)) { 250 | this._value = nextValue; 251 | this._cvaOnChange(nextValue); 252 | this._valueChange.emit(nextValue); 253 | this.colorInput.emit(new NgxMatColorPickerInputEvent(this, this._elementRef.nativeElement)); 254 | } else if (lastValueWasValid !== this._lastValueValid) { 255 | this._validatorOnChange(); 256 | } 257 | } 258 | 259 | } 260 | 261 | 262 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-picker/color-picker-content.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-picker/color-picker-content.component.scss: -------------------------------------------------------------------------------- 1 | $mat-datepicker-calendar-padding: 8px; 2 | $mat-datepicker-non-touch-calendar-cell-size: 40px; 3 | $mat-datepicker-non-touch-calendar-width: $mat-datepicker-non-touch-calendar-cell-size * 7 + 4 | $mat-datepicker-calendar-padding * 2; 5 | // Based on the natural height of the calendar in a month with 6 rows of dates 6 | // (largest the calendar will get). 7 | $mat-datepicker-non-touch-calendar-height: 354px; 8 | 9 | // Ideally the calendar would have a constant aspect ratio, no matter its size, and we would base 10 | // these measurements off the aspect ratio. Unfortunately, the aspect ratio does change a little as 11 | // the calendar grows, since some of the elements have pixel-based sizes. These numbers have been 12 | // chosen to minimize extra whitespace at larger sizes, while still ensuring we won't need 13 | // scrollbars at smaller sizes. 14 | $mat-datepicker-touch-landscape-width: 64vh; 15 | $mat-datepicker-touch-landscape-height: 80vh; 16 | $mat-datepicker-touch-portrait-width: 80vw; 17 | $mat-datepicker-touch-portrait-height: 100vw; 18 | $mat-datepicker-touch-min-width: 250px; 19 | $mat-datepicker-touch-min-height: 312px; 20 | $mat-datepicker-touch-max-width: 750px; 21 | $mat-datepicker-touch-max-height: 788px; 22 | 23 | .ngx-mat-colorpicker-content { 24 | display: block; 25 | border-radius: 4px; 26 | box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12); 27 | background-color: #fff; 28 | color: rgba(0, 0, 0, 0.87); 29 | padding: 16px; 30 | 31 | .ngx-mat-color-palette { 32 | width: $mat-datepicker-non-touch-calendar-width; 33 | height: $mat-datepicker-non-touch-calendar-height; 34 | } 35 | } 36 | 37 | .ngx-mat-colorpicker-content-touch { 38 | display: block; 39 | // make sure the dialog scrolls rather than being cropped on ludicrously small screens 40 | max-height: 80vh; 41 | overflow: auto; 42 | 43 | // TODO(mmalerba): hack to offset the padding of the dialog. Can be removed when we switch away 44 | // from using dialog. 45 | margin: -24px; 46 | 47 | .ngx-mat-color-palette { 48 | min-width: $mat-datepicker-touch-min-width; 49 | min-height: $mat-datepicker-touch-min-height; 50 | max-width: $mat-datepicker-touch-max-width; 51 | max-height: $mat-datepicker-touch-max-height; 52 | } 53 | } 54 | 55 | @media all and (orientation: landscape) { 56 | .mat-datepicker-content-touch .ngx-mat-color-palette { 57 | width: $mat-datepicker-touch-landscape-width; 58 | height: $mat-datepicker-touch-landscape-height; 59 | } 60 | } 61 | 62 | @media all and (orientation: portrait) { 63 | .mat-datepicker-content-touch .ngx-mat-color-palette { 64 | width: $mat-datepicker-touch-portrait-width; 65 | height: $mat-datepicker-touch-portrait-height; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-toggle/color-toggle.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-toggle/color-toggle.component.scss: -------------------------------------------------------------------------------- 1 | .mat-form-field-appearance-legacy { 2 | .mat-form-field-prefix, 3 | .mat-form-field-suffix { 4 | .ngx-mat-color-toggle-default-icon { 5 | width: 1em; 6 | } 7 | } 8 | } 9 | 10 | .mat-form-field:not(.mat-form-field-appearance-legacy) { 11 | .mat-form-field-prefix, 12 | .mat-form-field-suffix { 13 | .ngx-mat-color-toggle-default-icon { 14 | display: block; 15 | width: 1.5em; 16 | height: 1.5em; 17 | } 18 | 19 | .mat-icon-button .ngx-mat-color-toggle-default-icon { 20 | margin: auto; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/color-toggle/color-toggle.component.ts: -------------------------------------------------------------------------------- 1 | import { AfterContentInit, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; 2 | import { MatButton } from '@angular/material/button'; 3 | import { merge, of, Subscription } from 'rxjs'; 4 | import { NgxMatColorPickerComponent } from '../color-picker/color-picker.component'; 5 | 6 | @Component({ 7 | selector: 'ngx-mat-color-toggle', 8 | templateUrl: './color-toggle.component.html', 9 | styleUrls: ['./color-toggle.component.scss'], 10 | host: { 11 | 'class': 'ngx-mat-color-toggle', 12 | // Always set the tabindex to -1 so that it doesn't overlap with any custom tabindex the 13 | // consumer may have provided, while still being able to receive focus. 14 | '[attr.tabindex]': '-1', 15 | '[class.ngx-mat-color-toggle-active]': 'picker && picker.opened', 16 | '[class.mat-accent]': 'picker && picker.color === "accent"', 17 | '[class.mat-warn]': 'picker && picker.color === "warn"', 18 | '(focus)': '_button.focus()', 19 | }, 20 | exportAs: 'ngxMatColorPickerToggle', 21 | encapsulation: ViewEncapsulation.None 22 | }) 23 | export class NgxMatColorToggleComponent implements OnInit, AfterContentInit, OnChanges, OnDestroy { 24 | 25 | private _stateChanges = Subscription.EMPTY; 26 | 27 | @Input('for') picker: NgxMatColorPickerComponent; 28 | @Input() tabIndex: number; 29 | 30 | @Input() get disabled(): boolean { 31 | if (this._disabled == null && this.picker) { 32 | return this.picker.disabled; 33 | } 34 | } 35 | set disabled(value: boolean) { 36 | this._disabled = value; 37 | } 38 | private _disabled: boolean; 39 | 40 | @ViewChild('button') _button: MatButton; 41 | 42 | constructor(private _cd: ChangeDetectorRef) { } 43 | 44 | ngOnInit() { 45 | } 46 | 47 | ngOnChanges(changes: SimpleChanges): void { 48 | if (changes['picker']) { 49 | this._watchStateChanges(); 50 | } 51 | } 52 | 53 | ngOnDestroy() { 54 | this._stateChanges.unsubscribe(); 55 | } 56 | 57 | ngAfterContentInit() { 58 | this._watchStateChanges(); 59 | } 60 | 61 | public open(event: Event): void { 62 | if (this.picker && !this.disabled) { 63 | this.picker.open(); 64 | event.stopPropagation(); 65 | } 66 | } 67 | 68 | private _watchStateChanges() { 69 | const disabled$ = this.picker ? this.picker._disabledChange : of(); 70 | const inputDisabled$ = this.picker && this.picker._pickerInput ? 71 | this.picker._pickerInput._disabledChange : of(); 72 | 73 | const pickerToggled$ = this.picker ? 74 | merge(this.picker.openedStream, this.picker.closedStream) : of(); 75 | this._stateChanges.unsubscribe(); 76 | 77 | this._stateChanges = merge(disabled$, inputDisabled$, pickerToggled$).subscribe(() => this._cd.markForCheck()); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './color-palette/color-palette.component'; 2 | export * from './color-canvas/color-canvas.component'; 3 | export * from './color-collection/color-collection.component'; 4 | export * from './color-canvas/color-slider/color-slider.component'; 5 | export * from './color-picker/color-picker.component'; 6 | export * from './color-picker/color-input.component'; 7 | export * from './color-toggle/color-toggle.component'; 8 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/directives/index.ts: -------------------------------------------------------------------------------- 1 | export * from './numeric-color-input.directive'; 2 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/directives/numeric-color-input.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostListener } from '@angular/core'; 2 | import { NUMERIC_REGEX } from '../helpers'; 3 | 4 | @Directive({ 5 | selector: '[ngxMatNumericColorInput]' 6 | }) 7 | export class NumericColorInputDirective { 8 | 9 | constructor() { } 10 | 11 | @HostListener('input', ['$event']) 12 | onInput($event: any) { 13 | this._formatInput($event.target); 14 | } 15 | 16 | /** 17 | * Format input 18 | * @param input 19 | */ 20 | private _formatInput(input: any) { 21 | let val = Number(input.value.replace(NUMERIC_REGEX, '')); 22 | val = isNaN(val) ? 0 : val; 23 | input.value = val; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/helpers/color-helpers.ts: -------------------------------------------------------------------------------- 1 | 2 | const trimLeft = /^\s+/; 3 | const trimRight = /\s+$/; 4 | const tinyCounter = 0; 5 | const mathRound = Math.round; 6 | const mathMin = Math.min; 7 | const mathMax = Math.max; 8 | const mathRandom = Math.random; 9 | 10 | export const NUMERIC_REGEX = /[^0-9]/g; 11 | export const MAX_RGB = 255; 12 | export const MIN_RGB = 0; 13 | 14 | 15 | /** List basic colors */ 16 | export const BASIC_COLORS = ["#ffffff", "#ffff00", "#ff00ff", "#ff0000", 17 | "#c0c0c0", "#808080", "#808000", "#800080", 18 | "#800000", "#00ffff", "#00ff00", "#008080", 19 | "#008000", "#0000ff", "#000080", "#000000" 20 | ]; 21 | 22 | /** 23 | * Get color at position 24 | * @param ctx 25 | * @param x 26 | * @param y 27 | */ 28 | export function getColorAtPosition(ctx: CanvasRenderingContext2D, x: number, y: number): { r: number, g: number, b: number } { 29 | const imageData: Uint8ClampedArray = ctx.getImageData(x, y, 1, 1).data; 30 | return { r: imageData[0], g: imageData[1], b: imageData[2] }; 31 | } 32 | 33 | // `rgbaToHex` 34 | // Converts an RGBA color plus alpha transparency to hex 35 | // Assumes r, g, b are contained in the set [0, 255] and 36 | // a in [0, 1]. Returns a 4 or 8 character rgba hex 37 | export function rgbaToHex(r: number, g: number, b: number, a: number, allow4Char?: boolean): string { 38 | var hex = [ 39 | pad2(mathRound(r).toString(16)), 40 | pad2(mathRound(g).toString(16)), 41 | pad2(mathRound(b).toString(16)), 42 | pad2(convertDecimalToHex(a)) 43 | ]; 44 | 45 | // Return a 4 character hex if possible 46 | if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) { 47 | return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0); 48 | } 49 | 50 | return hex.join(""); 51 | } 52 | 53 | // Force a hex value to have 2 characters 54 | export function pad2(c): string { 55 | return c.length == 1 ? '0' + c : '' + c; 56 | } 57 | 58 | // Converts a decimal to a hex value 59 | export function convertDecimalToHex(d) { 60 | return Math.round(parseFloat(d) * 255).toString(16); 61 | } 62 | 63 | // Converts a hex value to a decimal 64 | function convertHexToDecimal(h) { 65 | return (parseIntFromHex(h) / 255); 66 | } 67 | 68 | // Parse a base-16 hex value into a base-10 integer 69 | function parseIntFromHex(val) { 70 | return parseInt(val, 16); 71 | } 72 | 73 | // `rgbToHex` 74 | // Converts an RGB color to hex 75 | // Assumes r, g, and b are contained in the set [0, 255] 76 | // Returns a 3 or 6 character hex 77 | export function rgbToHex(r: number, g: number, b: number, allow3Char?: boolean) { 78 | 79 | var hex = [ 80 | pad2(mathRound(r).toString(16)), 81 | pad2(mathRound(g).toString(16)), 82 | pad2(mathRound(b).toString(16)) 83 | ]; 84 | 85 | // Return a 3 character hex if possible 86 | if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { 87 | return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); 88 | } 89 | 90 | return hex.join(""); 91 | } 92 | 93 | // Actual matching. 94 | // Parentheses and commas are optional, but not required. 95 | // Whitespace can take the place of commas or opening parent 96 | const CSS_INTEGER = "[-\\+]?\\d+%?"; 97 | const CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; 98 | const CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; 99 | const PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; 100 | const PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; 101 | 102 | export const matchers = { 103 | CSS_UNIT: new RegExp(CSS_UNIT), 104 | rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), 105 | rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), 106 | hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), 107 | hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), 108 | hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), 109 | hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), 110 | hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, 111 | hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, 112 | hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, 113 | hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ 114 | }; 115 | 116 | // `stringInputToObject` 117 | // Permissive string parsing. Take in a number of formats, and output an object 118 | // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` 119 | export function stringInputToObject(color: string): { r: number, g: number, b: number, a: number } { 120 | 121 | color = color.replace(trimLeft, '').replace(trimRight, '').toLowerCase(); 122 | 123 | // Try to match string input using regular expressions. 124 | // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] 125 | // Just return an object and let the conversion functions handle that. 126 | // This way the result will be the same whether the tinycolor is initialized with string or object. 127 | let match; 128 | let obj; 129 | if ((match = matchers.rgb.exec(color))) { 130 | return { r: match[1], g: match[2], b: match[3], a: 1 }; 131 | } 132 | if ((match = matchers.rgba.exec(color))) { 133 | return { r: match[1], g: match[2], b: match[3], a: match[4] }; 134 | } 135 | 136 | if ((match = matchers.hex8.exec(color))) { 137 | return { 138 | r: parseIntFromHex(match[1]), 139 | g: parseIntFromHex(match[2]), 140 | b: parseIntFromHex(match[3]), 141 | a: convertHexToDecimal(match[4]), 142 | }; 143 | } 144 | if ((match = matchers.hex6.exec(color))) { 145 | return { 146 | r: parseIntFromHex(match[1]), 147 | g: parseIntFromHex(match[2]), 148 | b: parseIntFromHex(match[3]), 149 | a: 1 150 | }; 151 | } 152 | if ((match = matchers.hex4.exec(color))) { 153 | return { 154 | r: parseIntFromHex(match[1] + '' + match[1]), 155 | g: parseIntFromHex(match[2] + '' + match[2]), 156 | b: parseIntFromHex(match[3] + '' + match[3]), 157 | a: convertHexToDecimal(match[4] + '' + match[4]), 158 | }; 159 | } 160 | if ((match = matchers.hex3.exec(color))) { 161 | return { 162 | r: parseIntFromHex(match[1] + '' + match[1]), 163 | g: parseIntFromHex(match[2] + '' + match[2]), 164 | b: parseIntFromHex(match[3] + '' + match[3]), 165 | a: 1 166 | }; 167 | } 168 | 169 | return null; 170 | } 171 | 172 | export function createMissingDateImplError(provider: string) { 173 | return Error( 174 | `NgxMatColorPicker: No provider found for ${provider}. You must define MAT_COLOR_FORMATS in your module`); 175 | } -------------------------------------------------------------------------------- /projects/color-picker/src/lib/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './color-helpers'; -------------------------------------------------------------------------------- /projects/color-picker/src/lib/models/color-input-format.ts: -------------------------------------------------------------------------------- 1 | export type ColorInputFormat = 'rgb' | 'hex' | 'hex6' | 'hex3' | 'hex4' | 'hex8'; 2 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/models/color.model.ts: -------------------------------------------------------------------------------- 1 | import { MAX_RGB, rgbaToHex, rgbToHex } from '../helpers'; 2 | import { ColorInputFormat } from './color-input-format'; 3 | 4 | export class Color { 5 | 6 | public r: number; 7 | public g: number; 8 | public b: number; 9 | public a: number; 10 | public roundA: number; 11 | 12 | public hex: string; 13 | public rgba: string; 14 | 15 | constructor(_r: number, _g: number, _b: number, _a?: number) { 16 | this.r = _r > MAX_RGB ? MAX_RGB : _r; 17 | this.g = _g > MAX_RGB ? MAX_RGB : _g; 18 | this.b = _b > MAX_RGB ? MAX_RGB : _b; 19 | if (_a != null) { 20 | this.a = _a > 1 ? 1 : _a; 21 | } else { 22 | this.a = 1; 23 | } 24 | this.roundA = Math.round(this.a); 25 | this.hex = rgbToHex(this.r, this.g, this.b); 26 | this.rgba = this.toRgba(); 27 | } 28 | 29 | public toHex(allow3Char?: boolean, ): string { 30 | return rgbToHex(this.r, this.g, this.b, allow3Char); 31 | } 32 | 33 | public toRgba(): string { 34 | return `rgba(${this.r},${this.g},${this.b},${this.a})`; 35 | } 36 | 37 | public toHexString(allow3Char?: boolean): string { 38 | return '#' + this.toHex(allow3Char); 39 | } 40 | 41 | public toRgbString(): string { 42 | return (this.a === 1) ? 43 | "rgb(" + Math.round(this.r) + ", " + Math.round(this.g) + ", " + Math.round(this.b) + ")" : 44 | "rgba(" + Math.round(this.r) + ", " + Math.round(this.g) + ", " + Math.round(this.b) + ", " + this.roundA + ")"; 45 | } 46 | 47 | public toHex8(allow4Char): string { 48 | return rgbaToHex(this.r, this.g, this.b, this.a, allow4Char); 49 | } 50 | 51 | public toHex8String(allow4Char?: boolean): string { 52 | return '#' + this.toHex8(allow4Char); 53 | } 54 | 55 | public toString(format: ColorInputFormat): string { 56 | let formatSet = !!format; 57 | 58 | let formattedString; 59 | let hasAlpha = this.a < 1 && this.a >= 0; 60 | let needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" 61 | || format === "hex3" || format === "hex4" || format === "hex8"); 62 | 63 | if (needsAlphaFormat) { 64 | return this.toRgbString(); 65 | } 66 | if (format === "rgb") { 67 | formattedString = this.toRgbString(); 68 | } 69 | if (format === "hex" || format === "hex6") { 70 | formattedString = this.toHexString(); 71 | } 72 | if (format === "hex3") { 73 | formattedString = this.toHexString(true); 74 | } 75 | if (format === "hex4") { 76 | formattedString = this.toHex8String(true); 77 | } 78 | if (format === "hex8") { 79 | formattedString = this.toHex8String(); 80 | } 81 | 82 | return formattedString || this.toHexString(); 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /projects/color-picker/src/lib/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './color.model'; 2 | export * from './color-input-format'; -------------------------------------------------------------------------------- /projects/color-picker/src/lib/services/color-adapter.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { stringInputToObject } from '../helpers'; 3 | import { Color, ColorInputFormat } from '../models'; 4 | 5 | @Injectable() 6 | export class ColorAdapter { 7 | 8 | constructor() { } 9 | 10 | sameColor(a: Color, b: Color) { 11 | if (a == null && b == null) return true; 12 | if (a != null && b != null) return a.rgba === b.rgba; 13 | return false; 14 | } 15 | 16 | format(c: Color, format: ColorInputFormat): string { 17 | return c.toString(format); 18 | } 19 | 20 | parse(value: string): Color | null { 21 | const obj = stringInputToObject(value); 22 | if (obj) { 23 | return new Color(obj.r, obj.g, obj.b, obj.a); 24 | } 25 | return null; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/services/color-formats.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | import { ColorInputFormat } from '../models'; 3 | 4 | export type MatColorFormats = { 5 | display: { 6 | colorInput: ColorInputFormat; 7 | } 8 | } 9 | 10 | export const NGX_MAT_COLOR_FORMATS: MatColorFormats = { 11 | display: { 12 | colorInput: 'hex' 13 | } 14 | } 15 | 16 | export const MAT_COLOR_FORMATS = new InjectionToken('mat-color-formats'); 17 | -------------------------------------------------------------------------------- /projects/color-picker/src/lib/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './color-adapter'; 2 | export * from './color-formats'; -------------------------------------------------------------------------------- /projects/color-picker/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of color-picker 3 | */ 4 | 5 | export * from './lib/color-picker.module'; 6 | export * from './lib/components'; 7 | export * from './lib/directives'; 8 | export * from './lib/helpers'; 9 | export * from './lib/models'; 10 | export * from './lib/services'; 11 | 12 | -------------------------------------------------------------------------------- /projects/color-picker/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js/dist/zone'; 5 | import 'zone.js/dist/zone-testing'; 6 | import { getTestBed } from '@angular/core/testing'; 7 | import { 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting 10 | } from '@angular/platform-browser-dynamic/testing'; 11 | 12 | declare const require: any; 13 | 14 | // First, initialize the Angular testing environment. 15 | getTestBed().initTestEnvironment( 16 | BrowserDynamicTestingModule, 17 | platformBrowserDynamicTesting() 18 | ); 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | // And load the modules. 22 | context.keys().map(context); 23 | -------------------------------------------------------------------------------- /projects/color-picker/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2018" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "annotateForClosureCompiler": true, 22 | "skipTemplateCodegen": true, 23 | "strictMetadataEmit": true, 24 | "fullTemplateTypeCheck": true, 25 | "strictInjectionParameters": true, 26 | "enableResourceInlining": true 27 | }, 28 | "exclude": [ 29 | "src/test.ts", 30 | "**/*.spec.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /projects/color-picker/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/color-picker/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "ngxMat", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "ngx-mat", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/datetime-picker/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 HO Hong Quan 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. -------------------------------------------------------------------------------- /projects/datetime-picker/README.md: -------------------------------------------------------------------------------- 1 | # Ngx Material DatetimePicker, Timepicker for @angular/material 7.x, 8.x 2 | 3 | [![Build Status](https://travis-ci.com/h2qutc/ngx-mat-datetime-picker.svg?branch=master)](https://travis-ci.com/h2qutc/ngx-mat-datetime-picker) 4 | [![codecov](https://codecov.io/gh/h2qutc/ngx-mat-datetime-picker/branch/master/graph/badge.svg)](https://codecov.io/gh/h2qutc/ngx-mat-datetime-picker) 5 | [![License](https://img.shields.io/npm/l/ngx-mat-datetime-picker.svg)](https://www.npmjs.com/package/ngx-mat-datetime-picker) 6 | [![npm version](https://badge.fury.io/js/ngx-mat-datetime-picker.svg)](https://badge.fury.io/for/js/ngx-mat-datetime-picker) 7 | 8 | ## Description 9 | 10 | A DatetimePicker like @angular/material [Datepicker](https://material.angular.io/components/datepicker/overview) by adding support for choosing time. 11 | 12 | [![button](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SAAY32BP5KPPC&source=url) 13 | 14 | ## DEMO 15 | 16 | @see [LIVE DEMO](https://h2qutc.github.io/ngx-mat-datetime-picker/) 17 | 18 | @see [DEMO stackblitz](https://stackblitz.com/edit/demo-ngx-mat-datetime-picker) 19 | 20 | ![Alt Text](demo_datetime_picker.png) 21 | 22 | ## Getting started 23 | ``` 24 | npm install --save @angular-material-components/datetime-picker 25 | ``` 26 | 27 | ## Setup 28 | Basically the same way the @angular/material Datepicker is configured and imported. 29 | 30 | ``` 31 | import { NgxMatDatetimePickerModule, NgxMatTimepickerModule } from 'ngx-mat-datetime-picker'; 32 | @NgModule({ 33 | ... 34 | imports: [ 35 | BrowserModule, 36 | HttpClientModule, 37 | BrowserAnimationsModule, 38 | MatDatepickerModule, 39 | MatInputModule, 40 | NgxMatTimepickerModule, 41 | FormsModule, 42 | ReactiveFormsModule, 43 | MatButtonModule, 44 | NgxMatDatetimePickerModule, 45 | ], 46 | ... 47 | }) 48 | export class AppModule { } 49 | ``` 50 | @see [src/app/app.module.ts](src/app/app.module.ts) 51 | 52 | ## Using the component 53 | 54 | The same API as @angular/material Datepicker (@see [API docs](https://material.angular.io/components/datepicker/api)) 55 | 56 | ### Datetime Picker (ngx-mat-datetime-picker) 57 | 58 | ``` 59 | 60 | 62 | 63 | 65 | 66 | 67 | ``` 68 | 69 | ### Timepicker (ngx-mat-timepicker) 70 | 71 | ``` 72 | 73 | 74 | 75 | 76 | 77 | 78 | ``` 79 | 80 | #### List of @Input 81 | 82 | | @Input | Type | Default value | Description | 83 | |--------------- |---------- |--------------- |---------------------------------------------------------------------- | 84 | | **disabled** | boolean | null | If true, the picker is readonly and can't be modified | 85 | | **showSpinners** | boolean | true | If true, the spinners above and below input are visible | 86 | | **showSeconds** | boolean | true | If true, it is not possible to select seconds | 87 | | **stepHour** | number | 1 | The number of hours to add/substract when clicking hour spinners | 88 | | **stepMinute** | number | 1 | The number of minutes to add/substract when clicking minute spinners | 89 | | **stepSecond** | number | 1 | The number of seconds to add/substract when clicking second spinners | 90 | | **color** | ThemePalette | undefined | Color palette to use on the datepicker's calendar. | 91 | | **enableMeridian** | boolean | false | Whether to display 12H or 24H mode. | 92 | | **touchUi** | boolean | false | Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather than a popup and elements have more padding to allow for bigger touch targets. | 93 | 94 | ## Choosing a date implementation and date format settings 95 | 96 | The datepicker was built to be date implementation agnostic. This means that it can be made to work with a variety of different date implementations. However it also means that developers need to make sure to provide the appropriate pieces for the datepicker to work with their chosen implementation. 97 | 98 | The easiest way to ensure this is to import one of the provided date modules: 99 | 100 | | | **NgxMatNativeDateModule** | **NgxMatMomentModule** | 101 | |----------------------- |---------------------------- |------------------------------------------------------------------------------------- | 102 | | **Date type** | Date | Moment | 103 | | **Supported locales** | en-US | [See project for details](https://github.com/moment/moment/tree/develop/src/locale) | 104 | | **Dependencies** | None | [Moment.js](https://momentjs.com/) | 105 | | **Import from** | ngx-mat-datetime-picker | [ngx-mat-moment-adapter](https://www.npmjs.com/package/ngx-mat-moment-adapter) | 106 | 107 | To use NgxMatMomentModule: 108 | ``` 109 | npm install --save @angular-material-components/moment-adapter 110 | ``` 111 | 112 | Please note: NgxMatNativeDateModule is based off the functionality available in JavaScript's native Date object. Thus it is not suitable for many locales. One of the biggest shortcomings of the native Date object is the inability to set the parse format. 113 | 114 | We highly recommend using the **NgxMatMomentModule** or a custom **NgxMatDateAdapter** that works with the formatting/parsing library of your choice. 115 | 116 | For example: 117 | 118 | Creating a custom date adapter: 119 | 120 | ``` 121 | @Injectable() 122 | export class CustomDateAdapter extends NgxMatDateAdapter {...} 123 | // D can be Date, Moment or customized type 124 | ``` 125 | 126 | Creating a custom date adapter module 127 | ``` 128 | @NgModule({ 129 | providers: [ 130 | { 131 | provide: NgxMatDateAdapter, 132 | useClass: CustomDateAdapter, 133 | deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS] 134 | } 135 | ], 136 | }) 137 | export class CustomDateModule { } 138 | ``` 139 | 140 | 141 | ## Theming 142 | - @see @angular/material [Using a pre-built theme](https://material.angular.io/guide/theming#using-a-pre-built-theme) 143 | - Add the Material Design icon font to your index.html 144 | ``` 145 | 146 | ``` 147 | 148 | ## License 149 | MIT -------------------------------------------------------------------------------- /projects/datetime-picker/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/ngx-mat-datetime-picker'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/datetime-picker/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/@angular-material-components/datetime-picker", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/datetime-picker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angular-material-components/datetime-picker", 3 | "version": "2.0.0", 4 | "description": "Angular Material Datetime Picker", 5 | "author": "HO Hong Quan", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/h2qutc/ngx-mat-datetime-picker.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/h2qutc/ngx-mat-datetime-picker/issues" 12 | }, 13 | "homepage": "https://github.com/h2qutc/ngx-mat-datetime-picker", 14 | "keywords": [ 15 | "angular", 16 | "angular2", 17 | "angular-material", 18 | "angular material datetime picker", 19 | "angular material time picker", 20 | "datetime-picker", 21 | "datepicker", 22 | "timepicker", 23 | "ngx-mat", 24 | "ngx-mat-datetime-picker", 25 | "ngx-material-datetime-picker", 26 | "ngx-mat-timepicker" 27 | ], 28 | "license": "MIT", 29 | "peerDependencies": { 30 | "@angular/platform-browser": "0.0.0-NG", 31 | "@angular/common": "0.0.0-NG", 32 | "@angular/core": "0.0.0-NG", 33 | "@angular/forms": "0.0.0-NG", 34 | "@angular/material": "0.0.0-PLACEHOLDER", 35 | "@angular/cdk": "0.0.0-PLACEHOLDER", 36 | "tslib": "0.0.0-TSLIB" 37 | } 38 | } -------------------------------------------------------------------------------- /projects/datetime-picker/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './public-api'; 2 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/calendar-header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 10 | 11 |
12 | 13 | 14 | 15 | 19 | 20 | 24 |
25 |
26 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/calendar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 15 | 16 | 17 | 26 | 27 | 28 | 37 | 38 |
39 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/calendar.scss: -------------------------------------------------------------------------------- 1 | @mixin mat-fill { 2 | top: 0; 3 | left: 0; 4 | right: 0; 5 | bottom: 0; 6 | position: absolute; 7 | } 8 | 9 | $mat-calendar-padding: 8px !default; 10 | $mat-calendar-header-divider-width: 1px !default; 11 | $mat-calendar-controls-vertical-padding: 5%; 12 | // We use the same padding as the month / year label, but subtract 16px since there is padding 13 | // between the edge of the button and the text. This ensures that the button text lines up with 14 | // the month / year label text. 15 | $mat-calendar-controls-side-margin: calc(33% / 7 - 16px); 16 | 17 | $mat-calendar-arrow-size: 5px !default; 18 | $mat-calendar-arrow-disabled-opacity: 0.5 !default; 19 | 20 | // Values chosen to approximate https://material.io/icons/#ic_navigate_before and 21 | // https://material.io/icons/#ic_navigate_next as closely as possible. 22 | $mat-calendar-prev-next-icon-border-width: 2px; 23 | $mat-calendar-prev-next-icon-margin: 15.5px; 24 | $mat-calendar-prev-icon-transform: translateX(2px) rotate(-45deg); 25 | $mat-calendar-next-icon-transform: translateX(-2px) rotate(45deg); 26 | 27 | .mat-calendar { 28 | display: block; 29 | } 30 | 31 | .mat-calendar-header { 32 | padding: $mat-calendar-padding $mat-calendar-padding 0 $mat-calendar-padding; 33 | } 34 | 35 | .mat-calendar-content { 36 | padding: 0 $mat-calendar-padding $mat-calendar-padding $mat-calendar-padding; 37 | outline: none; 38 | } 39 | 40 | .mat-calendar-controls { 41 | display: flex; 42 | margin: $mat-calendar-controls-vertical-padding $mat-calendar-controls-side-margin; 43 | } 44 | 45 | .mat-calendar-spacer { 46 | flex: 1 1 auto; 47 | } 48 | 49 | .mat-calendar-period-button { 50 | min-width: 0; 51 | } 52 | 53 | .mat-calendar-arrow { 54 | display: inline-block; 55 | width: 0; 56 | height: 0; 57 | border-left: $mat-calendar-arrow-size solid transparent; 58 | border-right: $mat-calendar-arrow-size solid transparent; 59 | border-top-width: $mat-calendar-arrow-size; 60 | border-top-style: solid; 61 | margin: 0 0 0 $mat-calendar-arrow-size; 62 | vertical-align: middle; 63 | 64 | &.mat-calendar-invert { 65 | transform: rotate(180deg); 66 | } 67 | 68 | [dir='rtl'] & { 69 | margin: 0 $mat-calendar-arrow-size 0 0; 70 | } 71 | } 72 | 73 | .mat-calendar-previous-button, 74 | .mat-calendar-next-button { 75 | position: relative; 76 | 77 | &::after { 78 | @include mat-fill; 79 | content: ''; 80 | margin: $mat-calendar-prev-next-icon-margin; 81 | border: 0 solid currentColor; 82 | border-top-width: $mat-calendar-prev-next-icon-border-width; 83 | } 84 | 85 | [dir='rtl'] & { 86 | transform: rotate(180deg); 87 | } 88 | } 89 | 90 | .mat-calendar-previous-button::after { 91 | border-left-width: $mat-calendar-prev-next-icon-border-width; 92 | transform: $mat-calendar-prev-icon-transform; 93 | } 94 | 95 | .mat-calendar-next-button::after { 96 | border-right-width: $mat-calendar-prev-next-icon-border-width; 97 | transform: $mat-calendar-next-icon-transform; 98 | } 99 | 100 | .mat-calendar-table { 101 | border-spacing: 0; 102 | border-collapse: collapse; 103 | width: 100%; 104 | } 105 | 106 | .mat-calendar-table-header th { 107 | text-align: center; 108 | padding: 0 0 $mat-calendar-padding 0; 109 | } 110 | 111 | .mat-calendar-table-header-divider { 112 | position: relative; 113 | height: $mat-calendar-header-divider-width; 114 | 115 | // We use an absolutely positioned pseudo-element as the divider line for the table header so we 116 | // can extend it all the way to the edge of the calendar. 117 | &::after { 118 | content: ''; 119 | position: absolute; 120 | top: 0; 121 | left: -$mat-calendar-padding; 122 | right: -$mat-calendar-padding; 123 | height: $mat-calendar-header-divider-width; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/core/date-adapter.ts: -------------------------------------------------------------------------------- 1 | import { DateAdapter } from '@angular/material/core'; 2 | 3 | export abstract class NgxMatDateAdapter extends DateAdapter { 4 | /** 5 | * Gets the hour component of the given date. 6 | * @param date The date to extract the month from. 7 | * @returns The hour component. 8 | */ 9 | abstract getHour(date: D): number; 10 | 11 | /** 12 | * Gets the minute component of the given date. 13 | * @param date The date to extract the month from. 14 | * @returns The minute component. 15 | */ 16 | abstract getMinute(date: D): number; 17 | 18 | /** 19 | * Gets the second component of the given date. 20 | * @param date The date to extract the month from. 21 | * @returns The second component. 22 | */ 23 | abstract getSecond(date: D): number; 24 | 25 | /** 26 | * Set the hour component of the given date. 27 | * @param date The date to extract the month from. 28 | * @param value The value to set. 29 | */ 30 | abstract setHour(date: D, value: number): void; 31 | 32 | /** 33 | * Set the second component of the given date. 34 | * @param date The date to extract the month from. 35 | * @param value The value to set. 36 | */ 37 | abstract setMinute(date: D, value: number): void; 38 | 39 | /** 40 | * Set the second component of the given date. 41 | * @param date The date to extract the month from. 42 | * @param value The value to set. 43 | */ 44 | abstract setSecond(date: D, value: number): void; 45 | 46 | /** 47 | * Check if two date have same time 48 | * @param a Date 1 49 | * @param b Date 2 50 | */ 51 | isSameTime(a: D, b: D): boolean { 52 | if (a == null || b == null) return true; 53 | return this.getHour(a) === this.getHour(b) 54 | && this.getMinute(a) === this.getMinute(b) 55 | && this.getSecond(a) === this.getSecond(b); 56 | } 57 | 58 | /** 59 | * Copy time from a date to a another date 60 | * @param toDate 61 | * @param fromDate 62 | */ 63 | copyTime(toDate: D, fromDate: D) { 64 | this.setHour(toDate, this.getHour(fromDate)); 65 | this.setMinute(toDate, this.getMinute(fromDate)); 66 | this.setSecond(toDate, this.getSecond(fromDate)); 67 | } 68 | 69 | /** 70 | * Compares two dates. 71 | * @param first The first date to compare. 72 | * @param second The second date to compare. 73 | * @returns 0 if the dates are equal, a number less than 0 if the first date is earlier, 74 | * a number greater than 0 if the first date is later. 75 | */ 76 | compareDateWithTime(first: D, second: D, showSeconds?: boolean): number { 77 | let res = super.compareDate(first, second) || 78 | this.getHour(first) - this.getHour(second) || 79 | this.getMinute(first) - this.getMinute(second); 80 | if (showSeconds) { 81 | res = res || this.getSecond(first) - this.getSecond(second); 82 | } 83 | return res; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/core/native-date-formats.ts: -------------------------------------------------------------------------------- 1 | import { MatDateFormats } from '@angular/material/core'; 2 | 3 | /** 4 | * @license 5 | * Copyright Google LLC All Rights Reserved. 6 | * 7 | * Use of this source code is governed by an MIT-style license that can be 8 | * found in the LICENSE file at https://angular.io/license 9 | */ 10 | 11 | const DEFAULT_DATE_INPUT = { 12 | year: 'numeric', month: 'numeric', day: 'numeric', 13 | hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" 14 | } 15 | 16 | export const NGX_MAT_NATIVE_DATE_FORMATS: MatDateFormats = { 17 | parse: { 18 | dateInput: DEFAULT_DATE_INPUT, 19 | }, 20 | display: { 21 | dateInput: DEFAULT_DATE_INPUT, 22 | monthYearLabel: { year: 'numeric', month: 'short' }, 23 | dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' }, 24 | monthYearA11yLabel: { year: 'numeric', month: 'long' }, 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/core/native-date.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google LLC All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | */ 8 | 9 | import { PlatformModule } from '@angular/cdk/platform'; 10 | import { NgModule } from '@angular/core'; 11 | import { MAT_DATE_FORMATS } from '@angular/material/core'; 12 | import { NgxMatDateAdapter } from './date-adapter'; 13 | import { NgxMatNativeDateAdapter } from './native-date-adapter'; 14 | import { NGX_MAT_NATIVE_DATE_FORMATS } from './native-date-formats'; 15 | 16 | 17 | @NgModule({ 18 | imports: [PlatformModule], 19 | providers: [ 20 | { provide: NgxMatDateAdapter, useClass: NgxMatNativeDateAdapter }, 21 | ], 22 | }) 23 | export class NgxNativeDateModule { } 24 | 25 | @NgModule({ 26 | imports: [NgxNativeDateModule], 27 | providers: [{ provide: MAT_DATE_FORMATS, useValue: NGX_MAT_NATIVE_DATE_FORMATS }], 28 | }) 29 | export class NgxMatNativeDateModule { } 30 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/datetime-content.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 |
10 | 14 | 15 |
16 |
17 | 21 |
22 |
-------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/datetime-content.component.scss: -------------------------------------------------------------------------------- 1 | .mat-datepicker-content { 2 | display: block; 3 | border-radius: 4px; 4 | box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12); 5 | background-color: #fff; 6 | color: rgba(0, 0, 0, 0.87); 7 | .mat-calendar { 8 | width: 296px; 9 | } 10 | .time-container { 11 | display: flex; 12 | position: relative; 13 | padding-top: 5px; 14 | justify-content: center; 15 | &.disable-seconds{ 16 | .ngx-mat-timepicker{ 17 | .table{ 18 | margin-left: 9px; 19 | } 20 | } 21 | } 22 | &::before { 23 | content: ''; 24 | position: absolute; 25 | top: 0; 26 | left: 0; 27 | right: 0; 28 | height: 1px; 29 | background-color: rgba(0, 0, 0, 0.12); 30 | } 31 | } 32 | 33 | .actions { 34 | display: flex; 35 | padding: 5px 15px 10px 15px; 36 | justify-content: flex-end; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/datetime-picker.module.ts: -------------------------------------------------------------------------------- 1 | import { PortalModule } from '@angular/cdk/portal'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NgModule } from '@angular/core'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatDatepickerModule, MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER } from '@angular/material/datepicker'; 7 | import { MatDialogModule } from '@angular/material/dialog'; 8 | import { MatIconModule } from '@angular/material/icon'; 9 | import { MatInputModule } from '@angular/material/input'; 10 | import { NgxMatCalendar, NgxMatCalendarHeader } from './calendar'; 11 | import { NgxMatDatetimeInput } from './datetime-input'; 12 | import { NgxMatDatetimeContent, NgxMatDatetimePicker } from './datetime-picker.component'; 13 | import { NgxMatMonthView } from './month-view'; 14 | import { NgxMatMultiYearView } from './multi-year-view'; 15 | import { NgxMatTimepickerModule } from './timepicker.module'; 16 | import { NgxMatYearView } from './year-view'; 17 | 18 | @NgModule({ 19 | imports: [ 20 | CommonModule, 21 | MatDatepickerModule, 22 | MatDialogModule, 23 | PortalModule, 24 | FormsModule, 25 | MatIconModule, 26 | MatButtonModule, 27 | MatInputModule, 28 | NgxMatTimepickerModule 29 | ], 30 | exports: [ 31 | NgxMatDatetimePicker, 32 | NgxMatDatetimeInput, 33 | NgxMatCalendar, 34 | NgxMatMonthView, 35 | NgxMatYearView, 36 | NgxMatMultiYearView, 37 | NgxMatCalendarHeader 38 | ], 39 | declarations: [ 40 | NgxMatDatetimePicker, 41 | NgxMatDatetimeContent, 42 | NgxMatDatetimeInput, 43 | NgxMatCalendar, 44 | NgxMatMonthView, 45 | NgxMatYearView, 46 | NgxMatMultiYearView, 47 | NgxMatCalendarHeader 48 | ], 49 | entryComponents: [ 50 | NgxMatDatetimeContent, 51 | NgxMatCalendarHeader 52 | ], 53 | providers: [ 54 | MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER 55 | ] 56 | }) 57 | export class NgxMatDatetimePickerModule { } 58 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/month-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/multi-year-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/multi-year-view.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google LLC All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | */ 8 | 9 | import { 10 | DOWN_ARROW, 11 | END, 12 | ENTER, 13 | HOME, 14 | LEFT_ARROW, 15 | PAGE_DOWN, 16 | PAGE_UP, 17 | RIGHT_ARROW, 18 | UP_ARROW, 19 | SPACE, 20 | } from '@angular/cdk/keycodes'; 21 | import { 22 | AfterContentInit, 23 | ChangeDetectionStrategy, 24 | ChangeDetectorRef, 25 | Component, 26 | EventEmitter, 27 | Input, 28 | Optional, 29 | Output, 30 | ViewChild, 31 | ViewEncapsulation, 32 | } from '@angular/core'; 33 | import {Directionality} from '@angular/cdk/bidi'; 34 | import { MatCalendarBody, MatCalendarCell } from '@angular/material/datepicker'; 35 | import { NgxMatDateAdapter } from './core/date-adapter'; 36 | 37 | export const yearsPerPage = 24; 38 | 39 | export const yearsPerRow = 4; 40 | 41 | /** 42 | * An internal component used to display a year selector in the datepicker. 43 | * @docs-private 44 | */ 45 | @Component({ 46 | selector: 'ngx-mat-multi-year-view', 47 | templateUrl: 'multi-year-view.html', 48 | exportAs: 'ngxMatMultiYearView', 49 | encapsulation: ViewEncapsulation.None, 50 | changeDetection: ChangeDetectionStrategy.OnPush 51 | }) 52 | export class NgxMatMultiYearView implements AfterContentInit { 53 | /** The date to display in this multi-year view (everything other than the year is ignored). */ 54 | @Input() 55 | get activeDate(): D { return this._activeDate; } 56 | set activeDate(value: D) { 57 | let oldActiveDate = this._activeDate; 58 | const validDate = 59 | this._getValidDateOrNull(this._dateAdapter.deserialize(value)) || this._dateAdapter.today(); 60 | this._activeDate = this._dateAdapter.clampDate(validDate, this.minDate, this.maxDate); 61 | 62 | if (!isSameMultiYearView( 63 | this._dateAdapter, oldActiveDate, this._activeDate, this.minDate, this.maxDate)) { 64 | this._init(); 65 | } 66 | } 67 | private _activeDate: D; 68 | 69 | /** The currently selected date. */ 70 | @Input() 71 | get selected(): D | null { return this._selected; } 72 | set selected(value: D | null) { 73 | this._selected = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); 74 | this._selectedYear = this._selected && this._dateAdapter.getYear(this._selected); 75 | } 76 | private _selected: D | null; 77 | 78 | /** The minimum selectable date. */ 79 | @Input() 80 | get minDate(): D | null { return this._minDate; } 81 | set minDate(value: D | null) { 82 | this._minDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); 83 | } 84 | private _minDate: D | null; 85 | 86 | /** The maximum selectable date. */ 87 | @Input() 88 | get maxDate(): D | null { return this._maxDate; } 89 | set maxDate(value: D | null) { 90 | this._maxDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); 91 | } 92 | private _maxDate: D | null; 93 | 94 | /** A function used to filter which dates are selectable. */ 95 | @Input() dateFilter: (date: D) => boolean; 96 | 97 | /** Emits when a new year is selected. */ 98 | @Output() readonly selectedChange: EventEmitter = new EventEmitter(); 99 | 100 | /** Emits the selected year. This doesn't imply a change on the selected date */ 101 | @Output() readonly yearSelected: EventEmitter = new EventEmitter(); 102 | 103 | /** Emits when any date is activated. */ 104 | @Output() readonly activeDateChange: EventEmitter = new EventEmitter(); 105 | 106 | /** The body of calendar table */ 107 | @ViewChild(MatCalendarBody) _matCalendarBody: MatCalendarBody; 108 | 109 | /** Grid of calendar cells representing the currently displayed years. */ 110 | _years: MatCalendarCell[][]; 111 | 112 | /** The year that today falls on. */ 113 | _todayYear: number; 114 | 115 | /** The year of the selected date. Null if the selected date is null. */ 116 | _selectedYear: number | null; 117 | 118 | constructor(private _changeDetectorRef: ChangeDetectorRef, 119 | @Optional() public _dateAdapter: NgxMatDateAdapter, 120 | @Optional() private _dir?: Directionality) { 121 | 122 | this._activeDate = this._dateAdapter.today(); 123 | } 124 | 125 | ngAfterContentInit() { 126 | this._init(); 127 | } 128 | 129 | /** Initializes this multi-year view. */ 130 | _init() { 131 | this._todayYear = this._dateAdapter.getYear(this._dateAdapter.today()); 132 | 133 | // We want a range years such that we maximize the number of 134 | // enabled dates visible at once. This prevents issues where the minimum year 135 | // is the last item of a page OR the maximum year is the first item of a page. 136 | 137 | // The offset from the active year to the "slot" for the starting year is the 138 | // *actual* first rendered year in the multi-year view. 139 | const activeYear = this._dateAdapter.getYear(this._activeDate); 140 | const minYearOfPage = activeYear - getActiveOffset( 141 | this._dateAdapter, this.activeDate, this.minDate, this.maxDate); 142 | 143 | this._years = []; 144 | for (let i = 0, row: number[] = []; i < yearsPerPage; i++) { 145 | row.push(minYearOfPage + i); 146 | if (row.length == yearsPerRow) { 147 | this._years.push(row.map(year => this._createCellForYear(year))); 148 | row = []; 149 | } 150 | } 151 | this._changeDetectorRef.markForCheck(); 152 | } 153 | 154 | /** Handles when a new year is selected. */ 155 | _yearSelected(year: number) { 156 | this.yearSelected.emit(this._dateAdapter.createDate(year, 0, 1)); 157 | let month = this._dateAdapter.getMonth(this.activeDate); 158 | let daysInMonth = 159 | this._dateAdapter.getNumDaysInMonth(this._dateAdapter.createDate(year, month, 1)); 160 | this.selectedChange.emit(this._dateAdapter.createDate(year, month, 161 | Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth))); 162 | } 163 | 164 | /** Handles keydown events on the calendar body when calendar is in multi-year view. */ 165 | _handleCalendarBodyKeydown(event: KeyboardEvent): void { 166 | const oldActiveDate = this._activeDate; 167 | const isRtl = this._isRtl(); 168 | 169 | switch (event.keyCode) { 170 | case LEFT_ARROW: 171 | this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, isRtl ? 1 : -1); 172 | break; 173 | case RIGHT_ARROW: 174 | this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, isRtl ? -1 : 1); 175 | break; 176 | case UP_ARROW: 177 | this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, -yearsPerRow); 178 | break; 179 | case DOWN_ARROW: 180 | this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, yearsPerRow); 181 | break; 182 | case HOME: 183 | this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, 184 | -getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate)); 185 | break; 186 | case END: 187 | this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, 188 | yearsPerPage - getActiveOffset( 189 | this._dateAdapter, this.activeDate, this.minDate, this.maxDate) - 1); 190 | break; 191 | case PAGE_UP: 192 | this.activeDate = 193 | this._dateAdapter.addCalendarYears( 194 | this._activeDate, event.altKey ? -yearsPerPage * 10 : -yearsPerPage); 195 | break; 196 | case PAGE_DOWN: 197 | this.activeDate = 198 | this._dateAdapter.addCalendarYears( 199 | this._activeDate, event.altKey ? yearsPerPage * 10 : yearsPerPage); 200 | break; 201 | case ENTER: 202 | case SPACE: 203 | this._yearSelected(this._dateAdapter.getYear(this._activeDate)); 204 | break; 205 | default: 206 | // Don't prevent default or focus active cell on keys that we don't explicitly handle. 207 | return; 208 | } 209 | if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { 210 | this.activeDateChange.emit(this.activeDate); 211 | } 212 | 213 | this._focusActiveCell(); 214 | // Prevent unexpected default actions such as form submission. 215 | event.preventDefault(); 216 | } 217 | 218 | _getActiveCell(): number { 219 | return getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate); 220 | } 221 | 222 | /** Focuses the active cell after the microtask queue is empty. */ 223 | _focusActiveCell() { 224 | this._matCalendarBody._focusActiveCell(); 225 | } 226 | 227 | /** Creates an MatCalendarCell for the given year. */ 228 | private _createCellForYear(year: number) { 229 | let yearName = this._dateAdapter.getYearName(this._dateAdapter.createDate(year, 0, 1)); 230 | return new MatCalendarCell(year, yearName, yearName, this._shouldEnableYear(year)); 231 | } 232 | 233 | /** Whether the given year is enabled. */ 234 | private _shouldEnableYear(year: number) { 235 | // disable if the year is greater than maxDate lower than minDate 236 | if (year === undefined || year === null || 237 | (this.maxDate && year > this._dateAdapter.getYear(this.maxDate)) || 238 | (this.minDate && year < this._dateAdapter.getYear(this.minDate))) { 239 | return false; 240 | } 241 | 242 | // enable if it reaches here and there's no filter defined 243 | if (!this.dateFilter) { 244 | return true; 245 | } 246 | 247 | const firstOfYear = this._dateAdapter.createDate(year, 0, 1); 248 | 249 | // If any date in the year is enabled count the year as enabled. 250 | for (let date = firstOfYear; this._dateAdapter.getYear(date) == year; 251 | date = this._dateAdapter.addCalendarDays(date, 1)) { 252 | if (this.dateFilter(date)) { 253 | return true; 254 | } 255 | } 256 | 257 | return false; 258 | } 259 | 260 | /** 261 | * @param obj The object to check. 262 | * @returns The given object if it is both a date instance and valid, otherwise null. 263 | */ 264 | private _getValidDateOrNull(obj: any): D | null { 265 | return (this._dateAdapter.isDateInstance(obj) && this._dateAdapter.isValid(obj)) ? obj : null; 266 | } 267 | 268 | /** Determines whether the user has the RTL layout direction. */ 269 | private _isRtl() { 270 | return this._dir && this._dir.value === 'rtl'; 271 | } 272 | } 273 | 274 | export function isSameMultiYearView( 275 | dateAdapter: NgxMatDateAdapter, date1: D, date2: D, minDate: D | null, maxDate: D | null): boolean { 276 | const year1 = dateAdapter.getYear(date1); 277 | const year2 = dateAdapter.getYear(date2); 278 | const startingYear = getStartingYear(dateAdapter, minDate, maxDate); 279 | return Math.floor((year1 - startingYear) / yearsPerPage) === 280 | Math.floor((year2 - startingYear) / yearsPerPage); 281 | } 282 | 283 | /** 284 | * When the multi-year view is first opened, the active year will be in view. 285 | * So we compute how many years are between the active year and the *slot* where our 286 | * "startingYear" will render when paged into view. 287 | */ 288 | export function getActiveOffset( 289 | dateAdapter: NgxMatDateAdapter, activeDate: D, minDate: D | null, maxDate: D | null): number { 290 | const activeYear = dateAdapter.getYear(activeDate); 291 | return euclideanModulo((activeYear - getStartingYear(dateAdapter, minDate, maxDate)), 292 | yearsPerPage); 293 | } 294 | 295 | /** 296 | * We pick a "starting" year such that either the maximum year would be at the end 297 | * or the minimum year would be at the beginning of a page. 298 | */ 299 | function getStartingYear( 300 | dateAdapter: NgxMatDateAdapter, minDate: D | null, maxDate: D | null): number { 301 | let startingYear = 0; 302 | if (maxDate) { 303 | const maxYear = dateAdapter.getYear(maxDate); 304 | startingYear = maxYear - yearsPerPage + 1; 305 | } else if (minDate) { 306 | startingYear = dateAdapter.getYear(minDate); 307 | } 308 | return startingYear; 309 | } 310 | 311 | /** Gets remainder that is non-negative, even if first number is negative */ 312 | function euclideanModulo (a: number, b: number): number { 313 | return (a % b + b) % b; 314 | } 315 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/timepicker.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 11 | 12 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | 44 | 45 | 52 | 53 | 54 | 59 | 60 | 61 | 62 | 67 | 68 | 73 | 74 | 80 | 81 | 82 | 83 | 84 |
6 | 10 | 13 | 19 | 23 |
30 | 31 | 34 | 35 | : 38 | 39 | 42 | 43 | : 46 | 47 | 50 | 51 | 55 | 58 |
63 | 69 | 75 | 79 |
85 |
-------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/timepicker.component.scss: -------------------------------------------------------------------------------- 1 | $fontSizeIcon: 24px; 2 | $sizeButtonMeridian: 36px; 3 | 4 | .ngx-mat-timepicker { 5 | font-size: 13px; 6 | form { 7 | min-width: 90px; 8 | .table { 9 | .tbody { 10 | tr { 11 | td { 12 | text-align: center; 13 | 14 | &.spacer { 15 | font-weight: bold; 16 | } 17 | &.meridian { 18 | .mat-button { 19 | min-width: 64px; 20 | line-height: $sizeButtonMeridian; 21 | border-radius: 4px; 22 | min-width: 0; 23 | border-radius: 50%; 24 | width: $sizeButtonMeridian; 25 | height: $sizeButtonMeridian; 26 | padding: 0; 27 | flex-shrink: 0; 28 | } 29 | } 30 | 31 | .mat-icon-button { 32 | height: $fontSizeIcon; 33 | width: $fontSizeIcon; 34 | line-height: $fontSizeIcon; 35 | .mat-icon { 36 | font-size: $fontSizeIcon; 37 | } 38 | } 39 | .mat-form-field { 40 | width: 20px; 41 | max-width: 20px; 42 | text-align: center; 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/timepicker.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectorRef, Component, forwardRef, Input, OnChanges, OnInit, Optional, SimpleChanges, ViewEncapsulation } from '@angular/core'; 2 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 3 | import { ThemePalette } from '@angular/material/core'; 4 | import { Subject } from 'rxjs'; 5 | import { debounceTime, takeUntil } from 'rxjs/operators'; 6 | import { NgxMatDateAdapter } from './core/date-adapter'; 7 | import { createMissingDateImplError, DEFAULT_HOUR_PLACEHOLDER, DEFAULT_MINUTE_PLACEHOLDER, DEFAULT_SECOND_PLACEHOLDER, DEFAULT_STEP, formatTwoDigitTimeValue, LIMIT_TIMES, MERIDIANS, NUMERIC_REGEX, PATTERN_INPUT_HOUR, PATTERN_INPUT_MINUTE, PATTERN_INPUT_SECOND } from './utils/date-utils'; 8 | 9 | @Component({ 10 | selector: 'ngx-mat-timepicker', 11 | templateUrl: './timepicker.component.html', 12 | styleUrls: ['./timepicker.component.scss'], 13 | host: { 14 | 'class': 'ngx-mat-timepicker' 15 | }, 16 | providers: [ 17 | { 18 | provide: NG_VALUE_ACCESSOR, 19 | useExisting: forwardRef(() => NgxMatTimepickerComponent), 20 | multi: true 21 | } 22 | ], 23 | exportAs: 'ngxMatTimepicker', 24 | encapsulation: ViewEncapsulation.None, 25 | }) 26 | export class NgxMatTimepickerComponent implements ControlValueAccessor, OnInit, OnChanges { 27 | 28 | public form: FormGroup; 29 | 30 | @Input() disabled = false; 31 | @Input() showSpinners = true; 32 | @Input() hourPlaceholder = DEFAULT_HOUR_PLACEHOLDER; 33 | @Input() minutePlaceholder = DEFAULT_MINUTE_PLACEHOLDER; 34 | @Input() secondPlaceholder = DEFAULT_SECOND_PLACEHOLDER; 35 | @Input() stepHour: number = DEFAULT_STEP; 36 | @Input() stepMinute: number = DEFAULT_STEP; 37 | @Input() stepSecond: number = DEFAULT_STEP; 38 | @Input() showSeconds = false; 39 | @Input() enableMeridian = false; 40 | @Input() color: ThemePalette = 'primary'; 41 | 42 | public meridian: string = MERIDIANS.AM; 43 | 44 | /** Hour */ 45 | private get hour() { 46 | let val = Number(this.form.controls['hour'].value); 47 | return isNaN(val) ? 0 : val; 48 | }; 49 | 50 | private get minute() { 51 | let val = Number(this.form.controls['minute'].value); 52 | return isNaN(val) ? 0 : val; 53 | }; 54 | 55 | private get second() { 56 | let val = Number(this.form.controls['second'].value); 57 | return isNaN(val) ? 0 : val; 58 | }; 59 | 60 | /** Whether or not the form is valid */ 61 | public get valid(): boolean { 62 | return this.form.valid; 63 | } 64 | 65 | private _onChange: any = () => { }; 66 | private _onTouched: any = () => { }; 67 | private _disabled: boolean; 68 | private _model: D; 69 | 70 | private _destroyed: Subject = new Subject(); 71 | 72 | public pattern = PATTERN_INPUT_HOUR; 73 | 74 | constructor(@Optional() public _dateAdapter: NgxMatDateAdapter, 75 | private cd: ChangeDetectorRef, private formBuilder: FormBuilder) { 76 | if (!this._dateAdapter) { 77 | throw createMissingDateImplError('NgxMatDateAdapter'); 78 | } 79 | this.form = this.formBuilder.group( 80 | { 81 | hour: [{ value: null, disabled: this.disabled }, [Validators.required, Validators.pattern(PATTERN_INPUT_HOUR)]], 82 | minute: [{ value: null, disabled: this.disabled }, [Validators.required, Validators.pattern(PATTERN_INPUT_MINUTE)]], 83 | second: [{ value: null, disabled: this.disabled }, [Validators.required, Validators.pattern(PATTERN_INPUT_SECOND)]] 84 | }); 85 | } 86 | 87 | ngOnInit() { 88 | this.form.valueChanges.pipe(takeUntil(this._destroyed), debounceTime(400)).subscribe(val => { 89 | this._updateModel(); 90 | }) 91 | } 92 | 93 | ngOnChanges(changes: SimpleChanges) { 94 | if (changes.disabled && !changes.disabled.firstChange) { 95 | this.disabled ? this.form.disable() : this.form.enable(); 96 | } 97 | 98 | } 99 | 100 | ngOnDestroy() { 101 | this._destroyed.next(); 102 | this._destroyed.complete(); 103 | } 104 | 105 | /** 106 | * Writes a new value to the element. 107 | * @param obj 108 | */ 109 | writeValue(val: D): void { 110 | this._model = val || this._dateAdapter.today(); 111 | this._updateHourMinuteSecond(); 112 | } 113 | 114 | /** Registers a callback function that is called when the control's value changes in the UI. */ 115 | registerOnChange(fn: (_: any) => {}): void { 116 | this._onChange = fn; 117 | } 118 | 119 | /** 120 | * Set the function to be called when the control receives a touch event. 121 | */ 122 | registerOnTouched(fn: () => {}): void { 123 | this._onTouched = fn; 124 | } 125 | 126 | /** Enables or disables the appropriate DOM element */ 127 | setDisabledState(isDisabled: boolean): void { 128 | this._disabled = isDisabled; 129 | this.cd.markForCheck(); 130 | } 131 | 132 | /** 133 | * Format input 134 | * @param input 135 | */ 136 | public formatInput(input: HTMLInputElement) { 137 | input.value = input.value.replace(NUMERIC_REGEX, ''); 138 | } 139 | 140 | /** Toggle meridian */ 141 | public toggleMeridian() { 142 | this.meridian = (this.meridian === MERIDIANS.AM) ? MERIDIANS.PM : MERIDIANS.AM; 143 | this.change('hour'); 144 | } 145 | 146 | /** Change property of time */ 147 | public change(prop: string, up?: boolean) { 148 | const next = this._getNextValueByProp(prop, up); 149 | this.form.controls[prop].setValue(formatTwoDigitTimeValue(next), { onlySelf: false, emitEvent: false }); 150 | this._updateModel(); 151 | } 152 | 153 | /** Update controls of form by model */ 154 | private _updateHourMinuteSecond() { 155 | let _hour = this._dateAdapter.getHour(this._model); 156 | const _minute = this._dateAdapter.getMinute(this._model); 157 | const _second = this._dateAdapter.getSecond(this._model); 158 | 159 | if (this.enableMeridian) { 160 | if (_hour > LIMIT_TIMES.meridian) { 161 | _hour = _hour - LIMIT_TIMES.meridian; 162 | this.meridian = MERIDIANS.PM; 163 | } else { 164 | this.meridian = MERIDIANS.AM; 165 | } 166 | } 167 | 168 | this.form.controls['hour'].setValue(formatTwoDigitTimeValue(_hour)); 169 | this.form.controls['minute'].setValue(formatTwoDigitTimeValue(_minute)); 170 | this.form.controls['second'].setValue(formatTwoDigitTimeValue(_second)); 171 | } 172 | 173 | /** Update model */ 174 | private _updateModel() { 175 | let _hour = this.hour; 176 | if (this.enableMeridian && this.meridian === MERIDIANS.PM && _hour !== LIMIT_TIMES.meridian) { 177 | _hour = _hour + LIMIT_TIMES.meridian; 178 | } 179 | 180 | this._dateAdapter.setHour(this._model, _hour); 181 | this._dateAdapter.setMinute(this._model, this.minute); 182 | this._dateAdapter.setSecond(this._model, this.second); 183 | this._onChange(this._model); 184 | } 185 | 186 | /** 187 | * Get next value by property 188 | * @param prop 189 | * @param up 190 | */ 191 | private _getNextValueByProp(prop: string, up?: boolean): number { 192 | const keyProp = prop[0].toUpperCase() + prop.slice(1); 193 | const min = LIMIT_TIMES[`min${keyProp}`]; 194 | let max = LIMIT_TIMES[`max${keyProp}`]; 195 | 196 | if (prop === 'hour' && this.enableMeridian) { 197 | max = LIMIT_TIMES.meridian; 198 | } 199 | 200 | let next; 201 | if (up == null) { 202 | next = this[prop] % (max); 203 | } else { 204 | next = up ? this[prop] + this[`step${keyProp}`] : this[prop] - this[`step${keyProp}`]; 205 | if (prop === 'hour' && this.enableMeridian) { 206 | next = next % (max + 1); 207 | if (next === 0) next = up ? 1 : max; 208 | } else { 209 | next = next % max; 210 | } 211 | if (up) { 212 | next = next > max ? (next - max + min) : next; 213 | } else { 214 | next = next < min ? (next - min + max) : next; 215 | } 216 | 217 | } 218 | 219 | return next; 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/timepicker.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatInputModule } from '@angular/material/input'; 7 | import { NgxMatTimepickerComponent } from './timepicker.component'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | MatInputModule, 13 | ReactiveFormsModule, 14 | FormsModule, 15 | MatIconModule, 16 | MatButtonModule, 17 | ], 18 | exports: [ 19 | NgxMatTimepickerComponent 20 | ], 21 | declarations: [ 22 | NgxMatTimepickerComponent 23 | ] 24 | }) 25 | export class NgxMatTimepickerModule { } 26 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/utils/date-utils.ts: -------------------------------------------------------------------------------- 1 | export const LIMIT_TIMES = { 2 | minHour: 0, 3 | maxHour: 24, 4 | minMinute: 0, 5 | maxMinute: 60, 6 | minSecond: 0, 7 | maxSecond: 60, 8 | meridian: 12 9 | } 10 | 11 | export const MERIDIANS = { 12 | AM: 'AM', 13 | PM: 'PM' 14 | } 15 | 16 | export const DEFAULT_STEP = 1; 17 | export const DEFAULT_HOUR_PLACEHOLDER = ''; 18 | export const DEFAULT_MINUTE_PLACEHOLDER = ''; 19 | export const DEFAULT_SECOND_PLACEHOLDER = ''; 20 | export const NUMERIC_REGEX = /[^0-9]/g; 21 | 22 | export const PATTERN_INPUT_HOUR = /^(2[0-3]|[0-1][0-9]|[0-9])$/; 23 | export const PATTERN_INPUT_MINUTE = /^([0-5][0-9]|[0-9])$/; 24 | export const PATTERN_INPUT_SECOND = /^([0-5][0-9]|[0-9])$/; 25 | 26 | export function formatTwoDigitTimeValue(val: number) { 27 | const txt = val.toString(); 28 | return txt.length > 1 ? txt : `0${txt}`; 29 | } 30 | 31 | export function createMissingDateImplError(provider: string) { 32 | return Error( 33 | `MatDatepicker: No provider found for ${provider}. You must import one of the following ` + 34 | `modules at your application root: NgxMatNativeDateModule, NgxMatMomentModule, or provide a ` + 35 | `custom implementation.`); 36 | } 37 | 38 | /** Formats a range of years. */ 39 | export function formatYearRange(start: string, end: string): string { 40 | return `${start} \u2013 ${end}`; 41 | } 42 | 43 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/year-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/lib/year-view.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google LLC All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | */ 8 | 9 | import { Directionality } from '@angular/cdk/bidi'; 10 | import { DOWN_ARROW, END, ENTER, HOME, LEFT_ARROW, PAGE_DOWN, PAGE_UP, RIGHT_ARROW, SPACE, UP_ARROW } from '@angular/cdk/keycodes'; 11 | import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Inject, Input, Optional, Output, ViewChild, ViewEncapsulation } from '@angular/core'; 12 | import { MatDateFormats, MAT_DATE_FORMATS } from '@angular/material/core'; 13 | import { MatCalendarBody, MatCalendarCell } from '@angular/material/datepicker'; 14 | import { NgxMatDateAdapter } from './core/date-adapter'; 15 | import { createMissingDateImplError } from './utils/date-utils'; 16 | 17 | /** 18 | * An internal component used to display a single year in the datepicker. 19 | * @docs-private 20 | */ 21 | @Component({ 22 | selector: 'ngx-mat-year-view', 23 | templateUrl: 'year-view.html', 24 | exportAs: 'ngxMatYearView', 25 | encapsulation: ViewEncapsulation.None, 26 | changeDetection: ChangeDetectionStrategy.OnPush 27 | }) 28 | export class NgxMatYearView implements AfterContentInit { 29 | /** The date to display in this year view (everything other than the year is ignored). */ 30 | @Input() 31 | get activeDate(): D { return this._activeDate; } 32 | set activeDate(value: D) { 33 | let oldActiveDate = this._activeDate; 34 | const validDate = 35 | this._getValidDateOrNull(this._dateAdapter.deserialize(value)) || this._dateAdapter.today(); 36 | this._activeDate = this._dateAdapter.clampDate(validDate, this.minDate, this.maxDate); 37 | if (this._dateAdapter.getYear(oldActiveDate) !== this._dateAdapter.getYear(this._activeDate)) { 38 | this._init(); 39 | } 40 | } 41 | private _activeDate: D; 42 | 43 | /** The currently selected date. */ 44 | @Input() 45 | get selected(): D | null { return this._selected; } 46 | set selected(value: D | null) { 47 | this._selected = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); 48 | this._selectedMonth = this._getMonthInCurrentYear(this._selected); 49 | } 50 | private _selected: D | null; 51 | 52 | /** The minimum selectable date. */ 53 | @Input() 54 | get minDate(): D | null { return this._minDate; } 55 | set minDate(value: D | null) { 56 | this._minDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); 57 | } 58 | private _minDate: D | null; 59 | 60 | /** The maximum selectable date. */ 61 | @Input() 62 | get maxDate(): D | null { return this._maxDate; } 63 | set maxDate(value: D | null) { 64 | this._maxDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); 65 | } 66 | private _maxDate: D | null; 67 | 68 | /** A function used to filter which dates are selectable. */ 69 | @Input() dateFilter: (date: D) => boolean; 70 | 71 | /** Emits when a new month is selected. */ 72 | @Output() readonly selectedChange: EventEmitter = new EventEmitter(); 73 | 74 | /** Emits the selected month. This doesn't imply a change on the selected date */ 75 | @Output() readonly monthSelected: EventEmitter = new EventEmitter(); 76 | 77 | /** Emits when any date is activated. */ 78 | @Output() readonly activeDateChange: EventEmitter = new EventEmitter(); 79 | 80 | /** The body of calendar table */ 81 | @ViewChild(MatCalendarBody) _matCalendarBody: MatCalendarBody; 82 | 83 | /** Grid of calendar cells representing the months of the year. */ 84 | _months: MatCalendarCell[][]; 85 | 86 | /** The label for this year (e.g. "2017"). */ 87 | _yearLabel: string; 88 | 89 | /** The month in this year that today falls on. Null if today is in a different year. */ 90 | _todayMonth: number | null; 91 | 92 | /** 93 | * The month in this year that the selected Date falls on. 94 | * Null if the selected Date is in a different year. 95 | */ 96 | _selectedMonth: number | null; 97 | 98 | constructor(private _changeDetectorRef: ChangeDetectorRef, 99 | @Optional() @Inject(MAT_DATE_FORMATS) private _dateFormats: MatDateFormats, 100 | @Optional() public _dateAdapter: NgxMatDateAdapter, 101 | @Optional() private _dir?: Directionality) { 102 | if (!this._dateAdapter) { 103 | throw createMissingDateImplError('NgxMatDateAdapter'); 104 | } 105 | if (!this._dateFormats) { 106 | throw createMissingDateImplError('MAT_DATE_FORMATS'); 107 | } 108 | 109 | this._activeDate = this._dateAdapter.today(); 110 | } 111 | 112 | ngAfterContentInit() { 113 | this._init(); 114 | } 115 | 116 | /** Handles when a new month is selected. */ 117 | _monthSelected(month: number) { 118 | const normalizedDate = 119 | this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1); 120 | 121 | this.monthSelected.emit(normalizedDate); 122 | 123 | const daysInMonth = this._dateAdapter.getNumDaysInMonth(normalizedDate); 124 | 125 | this.selectedChange.emit(this._dateAdapter.createDate( 126 | this._dateAdapter.getYear(this.activeDate), month, 127 | Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth))); 128 | } 129 | 130 | /** Handles keydown events on the calendar body when calendar is in year view. */ 131 | _handleCalendarBodyKeydown(event: KeyboardEvent): void { 132 | // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent 133 | // disabled ones from being selected. This may not be ideal, we should look into whether 134 | // navigation should skip over disabled dates, and if so, how to implement that efficiently. 135 | 136 | const oldActiveDate = this._activeDate; 137 | const isRtl = this._isRtl(); 138 | 139 | switch (event.keyCode) { 140 | case LEFT_ARROW: 141 | this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, isRtl ? 1 : -1); 142 | break; 143 | case RIGHT_ARROW: 144 | this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, isRtl ? -1 : 1); 145 | break; 146 | case UP_ARROW: 147 | this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, -4); 148 | break; 149 | case DOWN_ARROW: 150 | this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, 4); 151 | break; 152 | case HOME: 153 | this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, 154 | -this._dateAdapter.getMonth(this._activeDate)); 155 | break; 156 | case END: 157 | this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, 158 | 11 - this._dateAdapter.getMonth(this._activeDate)); 159 | break; 160 | case PAGE_UP: 161 | this.activeDate = 162 | this._dateAdapter.addCalendarYears(this._activeDate, event.altKey ? -10 : -1); 163 | break; 164 | case PAGE_DOWN: 165 | this.activeDate = 166 | this._dateAdapter.addCalendarYears(this._activeDate, event.altKey ? 10 : 1); 167 | break; 168 | case ENTER: 169 | case SPACE: 170 | this._monthSelected(this._dateAdapter.getMonth(this._activeDate)); 171 | break; 172 | default: 173 | // Don't prevent default or focus active cell on keys that we don't explicitly handle. 174 | return; 175 | } 176 | 177 | if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { 178 | this.activeDateChange.emit(this.activeDate); 179 | } 180 | 181 | this._focusActiveCell(); 182 | // Prevent unexpected default actions such as form submission. 183 | event.preventDefault(); 184 | } 185 | 186 | /** Initializes this year view. */ 187 | _init() { 188 | this._selectedMonth = this._getMonthInCurrentYear(this.selected); 189 | this._todayMonth = this._getMonthInCurrentYear(this._dateAdapter.today()); 190 | this._yearLabel = this._dateAdapter.getYearName(this.activeDate); 191 | 192 | let monthNames = this._dateAdapter.getMonthNames('short'); 193 | // First row of months only contains 5 elements so we can fit the year label on the same row. 194 | this._months = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]].map(row => row.map( 195 | month => this._createCellForMonth(month, monthNames[month]))); 196 | this._changeDetectorRef.markForCheck(); 197 | } 198 | 199 | /** Focuses the active cell after the microtask queue is empty. */ 200 | _focusActiveCell() { 201 | this._matCalendarBody._focusActiveCell(); 202 | } 203 | 204 | /** 205 | * Gets the month in this year that the given Date falls on. 206 | * Returns null if the given Date is in another year. 207 | */ 208 | private _getMonthInCurrentYear(date: D | null) { 209 | return date && this._dateAdapter.getYear(date) == this._dateAdapter.getYear(this.activeDate) ? 210 | this._dateAdapter.getMonth(date) : null; 211 | } 212 | 213 | /** Creates an MatCalendarCell for the given month. */ 214 | private _createCellForMonth(month: number, monthName: string) { 215 | let ariaLabel = this._dateAdapter.format( 216 | this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1), 217 | this._dateFormats.display.monthYearA11yLabel); 218 | return new MatCalendarCell( 219 | month, monthName.toLocaleUpperCase(), ariaLabel, this._shouldEnableMonth(month)); 220 | } 221 | 222 | /** Whether the given month is enabled. */ 223 | private _shouldEnableMonth(month: number) { 224 | 225 | const activeYear = this._dateAdapter.getYear(this.activeDate); 226 | 227 | if (month === undefined || month === null || 228 | this._isYearAndMonthAfterMaxDate(activeYear, month) || 229 | this._isYearAndMonthBeforeMinDate(activeYear, month)) { 230 | return false; 231 | } 232 | 233 | if (!this.dateFilter) { 234 | return true; 235 | } 236 | 237 | const firstOfMonth = this._dateAdapter.createDate(activeYear, month, 1); 238 | 239 | // If any date in the month is enabled count the month as enabled. 240 | for (let date = firstOfMonth; this._dateAdapter.getMonth(date) == month; 241 | date = this._dateAdapter.addCalendarDays(date, 1)) { 242 | if (this.dateFilter(date)) { 243 | return true; 244 | } 245 | } 246 | 247 | return false; 248 | } 249 | 250 | /** 251 | * Tests whether the combination month/year is after this.maxDate, considering 252 | * just the month and year of this.maxDate 253 | */ 254 | private _isYearAndMonthAfterMaxDate(year: number, month: number) { 255 | if (this.maxDate) { 256 | const maxYear = this._dateAdapter.getYear(this.maxDate); 257 | const maxMonth = this._dateAdapter.getMonth(this.maxDate); 258 | 259 | return year > maxYear || (year === maxYear && month > maxMonth); 260 | } 261 | 262 | return false; 263 | } 264 | 265 | /** 266 | * Tests whether the combination month/year is before this.minDate, considering 267 | * just the month and year of this.minDate 268 | */ 269 | private _isYearAndMonthBeforeMinDate(year: number, month: number) { 270 | if (this.minDate) { 271 | const minYear = this._dateAdapter.getYear(this.minDate); 272 | const minMonth = this._dateAdapter.getMonth(this.minDate); 273 | 274 | return year < minYear || (year === minYear && month < minMonth); 275 | } 276 | 277 | return false; 278 | } 279 | 280 | /** 281 | * @param obj The object to check. 282 | * @returns The given object if it is both a date instance and valid, otherwise null. 283 | */ 284 | private _getValidDateOrNull(obj: any): D | null { 285 | return (this._dateAdapter.isDateInstance(obj) && this._dateAdapter.isValid(obj)) ? obj : null; 286 | } 287 | 288 | /** Determines whether the user has the RTL layout direction. */ 289 | private _isRtl() { 290 | return this._dir && this._dir.value === 'rtl'; 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-mat-datetime-picker 3 | */ 4 | 5 | export * from './lib/datetime-picker.component'; 6 | export * from './lib/datetime-input'; 7 | export * from './lib/datetime-picker.module'; 8 | export * from './lib/timepicker.component'; 9 | export * from './lib/timepicker.module'; 10 | export * from './lib/core/date-adapter'; 11 | export * from './lib/core/native-date-adapter'; 12 | export * from './lib/core/native-date-formats'; 13 | export * from './lib/core/native-date.module'; 14 | -------------------------------------------------------------------------------- /projects/datetime-picker/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js/dist/zone'; 5 | import 'zone.js/dist/zone-testing'; 6 | import { getTestBed } from '@angular/core/testing'; 7 | import { 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting 10 | } from '@angular/platform-browser-dynamic/testing'; 11 | 12 | declare const require: any; 13 | 14 | // First, initialize the Angular testing environment. 15 | getTestBed().initTestEnvironment( 16 | BrowserDynamicTestingModule, 17 | platformBrowserDynamicTesting() 18 | ); 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | // And load the modules. 22 | context.keys().map(context); 23 | -------------------------------------------------------------------------------- /projects/datetime-picker/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2018" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "annotateForClosureCompiler": true, 22 | "skipTemplateCodegen": true, 23 | "strictMetadataEmit": true, 24 | "fullTemplateTypeCheck": true, 25 | "strictInjectionParameters": true, 26 | "enableResourceInlining": true 27 | }, 28 | "exclude": [ 29 | "src/test.ts", 30 | "**/*.spec.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /projects/datetime-picker/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/datetime-picker/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "ngxMat", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "ngx-mat", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/moment-adapter/README.md: -------------------------------------------------------------------------------- 1 | # NgxMatMomentAdapter 2 | 3 | ## Description 4 | Moment Adapter for [@angular-material-components/datetime-picker](https://www.npmjs.com/package/@angular-material-components/datetime-picker) 5 | 6 | ## Getting started 7 | ``` 8 | npm install --save @angular-material-components/moment-adapter 9 | ``` 10 | 11 | ## Setup 12 | 13 | ``` 14 | import { NgxMatMomentModule } from '@angular-material-components/moment-adapter'; 15 | @NgModule({ 16 | ... 17 | imports: [ 18 | NgxMatMomentModule, 19 | NgxMatDatetimePickerModule, 20 | ], 21 | ... 22 | }) 23 | export class AppModule { } 24 | ``` 25 | 26 | ## License 27 | MIT -------------------------------------------------------------------------------- /projects/moment-adapter/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/ngx-mat-moment-adapter'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/moment-adapter/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/@angular-material-components/moment-adapter", 4 | "lib": { 5 | "entryFile": "src/public-api.ts", 6 | "umdModuleIds": { 7 | "@angular-material-components/datetime-picker": "@angular-material-components/datetime-picker" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /projects/moment-adapter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angular-material-components/moment-adapter", 3 | "version": "2.0.0", 4 | "description": "Angular Material Moment Adapter", 5 | "author": "HO Hong Quan", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/h2qutc/ngx-mat-datetime-picker.git" 10 | }, 11 | "homepage": "https://github.com/h2qutc/ngx-mat-datetime-picker", 12 | "peerDependencies": { 13 | "@angular/common": "0.0.0-NG", 14 | "@angular/core": "0.0.0-NG", 15 | "moment": "^2.18.1" 16 | } 17 | } -------------------------------------------------------------------------------- /projects/moment-adapter/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google LLC All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | */ 8 | 9 | export * from './public-api'; 10 | -------------------------------------------------------------------------------- /projects/moment-adapter/src/lib/moment-adapter.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google LLC All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | */ 8 | 9 | import { NgModule } from '@angular/core'; 10 | import { MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; 11 | import { MAT_MOMENT_DATE_ADAPTER_OPTIONS, NgxMatMomentAdapter } from './moment-adapter'; 12 | import { NGX_MAT_MOMENT_FORMATS } from './moment-formats'; 13 | import { NgxMatDateAdapter } from '@angular-material-components/datetime-picker'; 14 | 15 | @NgModule({ 16 | providers: [ 17 | { 18 | provide: NgxMatDateAdapter, 19 | useClass: NgxMatMomentAdapter, 20 | deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS] 21 | } 22 | ], 23 | }) 24 | export class NgxMomentDateModule { } 25 | 26 | 27 | @NgModule({ 28 | imports: [NgxMomentDateModule], 29 | providers: [{ provide: MAT_DATE_FORMATS, useValue: NGX_MAT_MOMENT_FORMATS }], 30 | }) 31 | export class NgxMatMomentModule { } 32 | -------------------------------------------------------------------------------- /projects/moment-adapter/src/lib/moment-adapter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google LLC All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | */ 8 | 9 | import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'; 10 | import { MAT_DATE_LOCALE } from '@angular/material/core'; 11 | // Depending on whether rollup is used, moment needs to be imported differently. 12 | // Since Moment.js doesn't have a default export, we normally need to import using the `* as` 13 | // syntax. However, rollup creates a synthetic default module and we thus need to import it using 14 | // the `default as` syntax. 15 | // TODO(mmalerba): See if we can clean this up at some point. 16 | import * as _moment from 'moment'; 17 | // tslint:disable-next-line:no-duplicate-imports 18 | import { default as _rollupMoment, Moment, MomentFormatSpecification, MomentInput } from 'moment'; 19 | import { NgxMatDateAdapter } from '@angular-material-components/datetime-picker'; 20 | 21 | const moment = _rollupMoment || _moment; 22 | 23 | /** Configurable options for {@see MomentDateAdapter}. */ 24 | export interface NgxMatMomentDateAdapterOptions { 25 | 26 | /** 27 | * When enabled, the dates have to match the format exactly. 28 | * See https://momentjs.com/guides/#/parsing/strict-mode/. 29 | */ 30 | strict?: boolean; 31 | 32 | /** 33 | * Turns the use of utc dates on or off. 34 | * Changing this will change how Angular Material components like DatePicker output dates. 35 | * {@default false} 36 | */ 37 | useUtc?: boolean; 38 | } 39 | 40 | /** InjectionToken for moment date adapter to configure options. */ 41 | export const MAT_MOMENT_DATE_ADAPTER_OPTIONS = new InjectionToken( 42 | 'MAT_MOMENT_DATE_ADAPTER_OPTIONS', { 43 | providedIn: 'root', 44 | factory: MAT_MOMENT_DATE_ADAPTER_OPTIONS_FACTORY 45 | }); 46 | 47 | 48 | /** @docs-private */ 49 | export function MAT_MOMENT_DATE_ADAPTER_OPTIONS_FACTORY(): NgxMatMomentDateAdapterOptions { 50 | return { 51 | useUtc: false 52 | }; 53 | } 54 | 55 | 56 | /** Creates an array and fills it with values. */ 57 | function range(length: number, valueFunction: (index: number) => T): T[] { 58 | const valuesArray = Array(length); 59 | for (let i = 0; i < length; i++) { 60 | valuesArray[i] = valueFunction(i); 61 | } 62 | return valuesArray; 63 | } 64 | 65 | 66 | /** Adapts Moment.js Dates for use with Angular Material. */ 67 | @Injectable() 68 | export class NgxMatMomentAdapter extends NgxMatDateAdapter { 69 | 70 | // Note: all of the methods that accept a `Moment` input parameter immediately call `this.clone` 71 | // on it. This is to ensure that we're working with a `Moment` that has the correct locale setting 72 | // while avoiding mutating the original object passed to us. Just calling `.locale(...)` on the 73 | // input would mutate the object. 74 | 75 | private _localeData: { 76 | firstDayOfWeek: number, 77 | longMonths: string[], 78 | shortMonths: string[], 79 | dates: string[], 80 | longDaysOfWeek: string[], 81 | shortDaysOfWeek: string[], 82 | narrowDaysOfWeek: string[] 83 | }; 84 | 85 | constructor(@Optional() @Inject(MAT_DATE_LOCALE) dateLocale: string, 86 | @Optional() @Inject(MAT_MOMENT_DATE_ADAPTER_OPTIONS) 87 | private _options?: NgxMatMomentDateAdapterOptions) { 88 | 89 | super(); 90 | this.setLocale(dateLocale || moment.locale()); 91 | } 92 | 93 | setLocale(locale: string) { 94 | super.setLocale(locale); 95 | 96 | let momentLocaleData = moment.localeData(locale); 97 | this._localeData = { 98 | firstDayOfWeek: momentLocaleData.firstDayOfWeek(), 99 | longMonths: momentLocaleData.months(), 100 | shortMonths: momentLocaleData.monthsShort(), 101 | dates: range(31, (i) => this.createDate(2017, 0, i + 1).format('D')), 102 | longDaysOfWeek: momentLocaleData.weekdays(), 103 | shortDaysOfWeek: momentLocaleData.weekdaysShort(), 104 | narrowDaysOfWeek: momentLocaleData.weekdaysMin(), 105 | }; 106 | } 107 | 108 | getYear(date: Moment): number { 109 | return this.clone(date).year(); 110 | } 111 | 112 | getMonth(date: Moment): number { 113 | return this.clone(date).month(); 114 | } 115 | 116 | getDate(date: Moment): number { 117 | return this.clone(date).date(); 118 | } 119 | 120 | getDayOfWeek(date: Moment): number { 121 | return this.clone(date).day(); 122 | } 123 | 124 | getMonthNames(style: 'long' | 'short' | 'narrow'): string[] { 125 | // Moment.js doesn't support narrow month names, so we just use short if narrow is requested. 126 | return style == 'long' ? this._localeData.longMonths : this._localeData.shortMonths; 127 | } 128 | 129 | getDateNames(): string[] { 130 | return this._localeData.dates; 131 | } 132 | 133 | getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] { 134 | if (style == 'long') { 135 | return this._localeData.longDaysOfWeek; 136 | } 137 | if (style == 'short') { 138 | return this._localeData.shortDaysOfWeek; 139 | } 140 | return this._localeData.narrowDaysOfWeek; 141 | } 142 | 143 | getYearName(date: Moment): string { 144 | return this.clone(date).format('YYYY'); 145 | } 146 | 147 | getFirstDayOfWeek(): number { 148 | return this._localeData.firstDayOfWeek; 149 | } 150 | 151 | getNumDaysInMonth(date: Moment): number { 152 | return this.clone(date).daysInMonth(); 153 | } 154 | 155 | clone(date: Moment): Moment { 156 | return date.clone().locale(this.locale); 157 | } 158 | 159 | createDate(year: number, month: number, date: number): Moment { 160 | // Moment.js will create an invalid date if any of the components are out of bounds, but we 161 | // explicitly check each case so we can throw more descriptive errors. 162 | if (month < 0 || month > 11) { 163 | throw Error(`Invalid month index "${month}". Month index has to be between 0 and 11.`); 164 | } 165 | 166 | if (date < 1) { 167 | throw Error(`Invalid date "${date}". Date has to be greater than 0.`); 168 | } 169 | 170 | const result = this._createMoment({ year, month, date }).locale(this.locale); 171 | 172 | // If the result isn't valid, the date must have been out of bounds for this month. 173 | if (!result.isValid()) { 174 | throw Error(`Invalid date "${date}" for month with index "${month}".`); 175 | } 176 | 177 | return result; 178 | } 179 | 180 | today(): Moment { 181 | return this._createMoment().locale(this.locale); 182 | } 183 | 184 | parse(value: any, parseFormat: string | string[]): Moment | null { 185 | if (value && typeof value == 'string') { 186 | return this._createMoment(value, parseFormat, this.locale); 187 | } 188 | return value ? this._createMoment(value).locale(this.locale) : null; 189 | } 190 | 191 | format(date: Moment, displayFormat: string): string { 192 | date = this.clone(date); 193 | if (!this.isValid(date)) { 194 | throw Error('MomentDateAdapter: Cannot format invalid date.'); 195 | } 196 | return date.format(displayFormat); 197 | } 198 | 199 | addCalendarYears(date: Moment, years: number): Moment { 200 | return this.clone(date).add({ years }); 201 | } 202 | 203 | addCalendarMonths(date: Moment, months: number): Moment { 204 | return this.clone(date).add({ months }); 205 | } 206 | 207 | addCalendarDays(date: Moment, days: number): Moment { 208 | return this.clone(date).add({ days }); 209 | } 210 | 211 | toIso8601(date: Moment): string { 212 | return this.clone(date).format(); 213 | } 214 | 215 | /** 216 | * Returns the given value if given a valid Moment or null. Deserializes valid ISO 8601 strings 217 | * (https://www.ietf.org/rfc/rfc3339.txt) and valid Date objects into valid Moments and empty 218 | * string into null. Returns an invalid date for all other values. 219 | */ 220 | deserialize(value: any): Moment | null { 221 | let date; 222 | if (value instanceof Date) { 223 | date = this._createMoment(value).locale(this.locale); 224 | } else if (this.isDateInstance(value)) { 225 | // Note: assumes that cloning also sets the correct locale. 226 | return this.clone(value); 227 | } 228 | if (typeof value === 'string') { 229 | if (!value) { 230 | return null; 231 | } 232 | date = this._createMoment(value, moment.ISO_8601).locale(this.locale); 233 | } 234 | if (date && this.isValid(date)) { 235 | return this._createMoment(date).locale(this.locale); 236 | } 237 | return super.deserialize(value); 238 | } 239 | 240 | isDateInstance(obj: any): boolean { 241 | return moment.isMoment(obj); 242 | } 243 | 244 | isValid(date: Moment): boolean { 245 | return this.clone(date).isValid(); 246 | } 247 | 248 | invalid(): Moment { 249 | return moment.invalid(); 250 | } 251 | 252 | getHour(date: _moment.Moment): number { 253 | return date.hours(); 254 | } 255 | getMinute(date: _moment.Moment): number { 256 | return date.minutes(); 257 | } 258 | getSecond(date: _moment.Moment): number { 259 | return date.seconds(); 260 | } 261 | setHour(date: _moment.Moment, value: number): void { 262 | date.hours(value); 263 | } 264 | setMinute(date: _moment.Moment, value: number): void { 265 | date.minutes(value) 266 | } 267 | setSecond(date: _moment.Moment, value: number): void { 268 | date.seconds(value); 269 | } 270 | 271 | /** Creates a Moment instance while respecting the current UTC settings. */ 272 | private _createMoment( 273 | date: MomentInput, 274 | format?: MomentFormatSpecification, 275 | locale?: string, 276 | ): Moment { 277 | const { strict, useUtc }: NgxMatMomentDateAdapterOptions = this._options || {}; 278 | 279 | return useUtc 280 | ? moment.utc(date, format, locale, strict) 281 | : moment(date, format, locale, strict); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /projects/moment-adapter/src/lib/moment-formats.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google LLC All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | */ 8 | 9 | import {MatDateFormats} from '@angular/material/core'; 10 | 11 | const DEFAULT_DATE_INPUT = 'l, LTS'; 12 | 13 | export const NGX_MAT_MOMENT_FORMATS: MatDateFormats = { 14 | parse: { 15 | dateInput: DEFAULT_DATE_INPUT, 16 | }, 17 | display: { 18 | dateInput: DEFAULT_DATE_INPUT, 19 | monthYearLabel: 'MMM YYYY', 20 | dateA11yLabel: 'LL', 21 | monthYearA11yLabel: 'MMMM YYYY', 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /projects/moment-adapter/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-mat-moment-adapter 3 | */ 4 | 5 | export * from './lib/moment-adapter'; 6 | export * from './lib/moment-formats'; 7 | export * from './lib/moment-adapter.module'; -------------------------------------------------------------------------------- /projects/moment-adapter/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2018" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "annotateForClosureCompiler": true, 22 | "skipTemplateCodegen": true, 23 | "strictMetadataEmit": true, 24 | "fullTemplateTypeCheck": true, 25 | "strictInjectionParameters": true, 26 | "enableResourceInlining": true 27 | }, 28 | "exclude": [ 29 | "src/test.ts", 30 | "**/*.spec.ts" 31 | ] 32 | } -------------------------------------------------------------------------------- /projects/moment-adapter/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/moment-adapter/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | {{nameApp}} 3 | 6 | 7 | Github 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | home Get Started 21 | 22 | 23 | date_range Datetime picker 24 | 25 | 26 | access_time Time picker 27 | 28 | 29 | colorize Color picker 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | 38 |
39 |
40 |
-------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | .app-content { 2 | padding: 20px; 3 | } 4 | .sidebar-item.active { 5 | background-color: #3f51b5; 6 | color: white; 7 | } 8 | 9 | 10 | .sidenav{ 11 | .mat-list-base{ 12 | padding-top: 0; 13 | } 14 | } -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'] 7 | }) 8 | export class AppComponent implements OnInit { 9 | 10 | public nameApp = 'angular-material-components'; 11 | 12 | 13 | 14 | constructor() { 15 | } 16 | 17 | ngOnInit() { 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientModule } from '@angular/common/http'; 2 | import { NgModule } from '@angular/core'; 3 | import { MatButtonModule } from '@angular/material/button'; 4 | import { MatCardModule } from '@angular/material/card'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatListModule } from '@angular/material/list'; 7 | import { MatSidenavModule } from '@angular/material/sidenav'; 8 | import { MatToolbarModule } from '@angular/material/toolbar'; 9 | import { BrowserModule } from '@angular/platform-browser'; 10 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 11 | import { RouterModule, Routes } from '@angular/router'; 12 | import { AppComponent } from './app.component'; 13 | import { HomeComponent } from './home'; 14 | 15 | const appRoutes: Routes = [ 16 | { path: 'home', component: HomeComponent }, 17 | { 18 | path: 'datetimepicker', 19 | loadChildren: './demo-datetime/demo-datetime.module#DemoDatetimeModule' 20 | }, 21 | { 22 | path: 'timepicker', 23 | loadChildren: './demo-time/demo-time.module#DemoTimeModule' 24 | }, 25 | { 26 | path: 'colorpicker', 27 | loadChildren: './demo-colorpicker/demo-colorpicker.module#DemoColorpickerModule' 28 | }, 29 | { path: '', redirectTo: '/colorpicker', pathMatch: 'full' }, 30 | { path: '**', redirectTo: '/colorpicker', pathMatch: 'full' } 31 | ] 32 | 33 | @NgModule({ 34 | imports: [ 35 | RouterModule.forRoot( 36 | appRoutes, 37 | ) 38 | ], 39 | exports: [ 40 | RouterModule 41 | ] 42 | }) 43 | export class AppRoutingModule { } 44 | 45 | @NgModule({ 46 | declarations: [ 47 | AppComponent, 48 | HomeComponent 49 | ], 50 | imports: [ 51 | BrowserModule, 52 | HttpClientModule, 53 | BrowserAnimationsModule, 54 | AppRoutingModule, 55 | MatButtonModule, 56 | MatSidenavModule, 57 | MatToolbarModule, 58 | MatIconModule, 59 | MatListModule, 60 | MatCardModule 61 | ], 62 | bootstrap: [ 63 | AppComponent 64 | ] 65 | }) 66 | export class AppModule { } -------------------------------------------------------------------------------- /src/app/demo-colorpicker/demo-colorpicker.component.html: -------------------------------------------------------------------------------- 1 | 2 |
HEX: {{colorCtr.value?.hex}}
3 |
RGBA: {{colorCtr.value?.rgba}}
4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | disabled (Default: false) 17 | 18 | 19 | {{option.label}} 20 | 21 |
22 | 23 |
24 | touchUi (Default: false) 25 | 26 | 27 | {{option.label}} 28 | 29 |
30 | 31 |
32 | color (Default: primary) 33 | 34 | Select color 35 | 36 | 37 | {{item}} 38 | 39 | 40 | 41 |
42 |
43 | 44 | 45 |
46 |     
47 |       {{codeColorPicker}}
48 |     
49 |   
50 |
-------------------------------------------------------------------------------- /src/app/demo-colorpicker/demo-colorpicker.component.scss: -------------------------------------------------------------------------------- 1 | @import '../shared/_common'; 2 | 3 | .zone-value { 4 | display: flex; 5 | flex-direction: column; 6 | .row { 7 | display: block; 8 | padding: 5px; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/app/demo-colorpicker/demo-colorpicker.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AbstractControl, FormControl } from '@angular/forms'; 3 | import { ThemePalette } from '@angular/material/core'; 4 | 5 | @Component({ 6 | selector: 'ngx-mat-demo-colorpicker', 7 | templateUrl: './demo-colorpicker.component.html', 8 | styleUrls: ['./demo-colorpicker.component.scss'] 9 | }) 10 | export class DemoColorpickerComponent implements OnInit { 11 | 12 | public disabled = false; 13 | public color: ThemePalette = 'primary'; 14 | public touchUi = false; 15 | 16 | colorCtr: AbstractControl = new FormControl(null); 17 | 18 | public options = [ 19 | { value: true, label: 'True' }, 20 | { value: false, label: 'False' } 21 | ]; 22 | 23 | public listColors = ['primary', 'accent', 'warn']; 24 | 25 | public codeColorPicker = ` 26 | 27 | 28 | 29 | 30 | `; 31 | 32 | constructor() { } 33 | 34 | ngOnInit() { 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/app/demo-colorpicker/demo-colorpicker.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { MatCardModule } from '@angular/material/card'; 5 | import { MatFormFieldModule } from '@angular/material/form-field'; 6 | import { MatInputModule } from '@angular/material/input'; 7 | import { MatRadioModule } from '@angular/material/radio'; 8 | import { MatSelectModule } from '@angular/material/select'; 9 | import { RouterModule, Routes } from '@angular/router'; 10 | import { MAT_COLOR_FORMATS, NgxMatColorPickerModule, NGX_MAT_COLOR_FORMATS } from 'projects/color-picker/src/public-api'; 11 | import { DemoColorpickerComponent } from './demo-colorpicker.component'; 12 | 13 | const routes: Routes = [ 14 | { path: '', component: DemoColorpickerComponent } 15 | ] 16 | 17 | @NgModule({ 18 | imports: [ 19 | CommonModule, 20 | RouterModule.forChild( 21 | routes, 22 | ), 23 | NgxMatColorPickerModule, 24 | MatCardModule, 25 | FormsModule, 26 | ReactiveFormsModule, 27 | MatFormFieldModule, 28 | MatInputModule, 29 | MatRadioModule, 30 | MatSelectModule 31 | ], 32 | providers: [ 33 | { provide: MAT_COLOR_FORMATS, useValue: NGX_MAT_COLOR_FORMATS } 34 | ], 35 | declarations: [ 36 | DemoColorpickerComponent 37 | ] 38 | }) 39 | export class DemoColorpickerModule { } 40 | -------------------------------------------------------------------------------- /src/app/demo-datetime/demo-datetime.component.html: -------------------------------------------------------------------------------- 1 | 2 | Selected date: {{dateControl.value?.toLocaleString()}} 3 | 4 | 5 | 6 | 7 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | disabled (Default: false) 20 | 21 | 22 | {{option.label}} 23 | 24 |
25 | 26 |
27 | showSpinners (Default: true) 28 | 29 | 30 | {{option.label}} 31 | 32 |
33 | 34 | 35 |
36 | showSeconds (Default: false) 37 | 38 | 39 | {{option.label}} 40 | 41 |
42 | 43 |
44 | touchUi (Default: false) 45 | 46 | 47 | {{option.label}} 48 | 49 |
50 | 51 |
52 | enableMeridian (Default: false) 53 | 54 | 55 | {{option.label}} 56 | 57 |
58 | 59 |
60 | steps 61 | 62 | hour (default: 1) 63 | 64 | 65 | {{step}} 66 | 67 | 68 | 69 | 70 | minute (default: 1) 71 | 72 | 73 | {{step}} 74 | 75 | 76 | 77 | 78 | 79 | second (default: 1) 80 | 81 | 82 | {{step}} 83 | 84 | 85 | 86 |
87 | 88 |
89 | color (Default: primary) 90 | 91 | Select color 92 | 93 | 94 | {{item}} 95 | 96 | 97 | 98 |
99 | 100 |
101 | 102 | Min date (Default: null, Demo: now - 1 day) = {{minDate?.toLocaleString()}} 103 | 104 |
105 | 106 |
107 | 108 | Max date (Default: null, Demo: now + 2 days) = {{maxDate?.toLocaleString()}} 109 | 110 |
111 | 112 |
113 | 114 | 115 |
116 |   
117 |     {{codeDatePicker}}
118 |   
119 | 
120 |
-------------------------------------------------------------------------------- /src/app/demo-datetime/demo-datetime.component.scss: -------------------------------------------------------------------------------- 1 | @import "../shared/_common"; 2 | 3 | .table { 4 | border-collapse: collapse; 5 | //width: 100%; 6 | margin-bottom: 30px; 7 | height: 250px; 8 | td, 9 | th { 10 | border: 1px solid #ddd; 11 | padding: 8px; 12 | } 13 | tr:nth-child(even) { 14 | background-color: #f2f2f2; 15 | } 16 | th { 17 | padding-top: 12px; 18 | padding-bottom: 12px; 19 | text-align: left; 20 | color: white; 21 | &:first-of-type { 22 | background-color: #268fa7; 23 | } 24 | &:last-of-type { 25 | background-color: #8a178c; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/demo-datetime/demo-datetime.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild } from '@angular/core'; 2 | import { FormControl } from '@angular/forms'; 3 | import { ThemePalette } from '@angular/material/core'; 4 | 5 | @Component({ 6 | selector: 'app-demo-datetime', 7 | templateUrl: './demo-datetime.component.html', 8 | styleUrls: ['./demo-datetime.component.scss'] 9 | }) 10 | export class DemoDatetimeComponent implements OnInit { 11 | 12 | @ViewChild('picker') picker: any; 13 | 14 | public disabled = false; 15 | public showSpinners = true; 16 | public showSeconds = false; 17 | public touchUi = false; 18 | public enableMeridian = false; 19 | public minDate: Date; 20 | public maxDate: Date; 21 | public stepHour = 1; 22 | public stepMinute = 1; 23 | public stepSecond = 1; 24 | public color: ThemePalette = 'primary'; 25 | 26 | public dateControl = new FormControl(new Date()); 27 | 28 | public options = [ 29 | { value: true, label: 'True' }, 30 | { value: false, label: 'False' } 31 | ]; 32 | 33 | public listColors = ['primary', 'accent', 'warn']; 34 | 35 | public stepHours = [1, 2, 3, 4, 5]; 36 | public stepMinutes = [1, 5, 10, 15, 20, 25]; 37 | public stepSeconds = [1, 5, 10, 15, 20, 25]; 38 | 39 | public codeDatePicker = 40 | ` 41 | 42 | 44 | 45 | 48 | 49 | `; 50 | 51 | constructor() { } 52 | 53 | ngOnInit() { 54 | } 55 | 56 | toggleMinDate(evt: any) { 57 | if (evt.checked) { 58 | this._setMinDate(); 59 | } else { 60 | this.minDate = null; 61 | } 62 | } 63 | 64 | toggleMaxDate(evt: any) { 65 | if (evt.checked) { 66 | this._setMaxDate(); 67 | } else { 68 | this.maxDate = null; 69 | } 70 | } 71 | 72 | closePicker() { 73 | this.picker.cancel(); 74 | } 75 | 76 | private _setMinDate() { 77 | const now = new Date(); 78 | this.minDate = new Date(); 79 | this.minDate.setDate(now.getDate() - 1); 80 | } 81 | 82 | 83 | private _setMaxDate() { 84 | const now = new Date(); 85 | this.maxDate = new Date(); 86 | this.maxDate.setDate(now.getDate() + 1); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/app/demo-datetime/demo-datetime.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatCardModule } from '@angular/material/card'; 6 | import { MatCheckboxModule } from '@angular/material/checkbox'; 7 | import { MatDatepickerModule } from '@angular/material/datepicker'; 8 | import { MatIconModule } from '@angular/material/icon'; 9 | import { MatInputModule } from '@angular/material/input'; 10 | import { MatRadioModule } from '@angular/material/radio'; 11 | import { MatSelectModule } from '@angular/material/select'; 12 | import { RouterModule, Routes } from '@angular/router'; 13 | import { NgxMatDatetimePickerModule, NgxMatNativeDateModule, NgxMatTimepickerModule } from 'projects/datetime-picker/src/public-api'; 14 | import { DemoDatetimeComponent } from './demo-datetime.component'; 15 | 16 | const routes: Routes = [ 17 | { path: '', component: DemoDatetimeComponent } 18 | ] 19 | 20 | @NgModule({ 21 | imports: [ 22 | CommonModule, 23 | RouterModule.forChild( 24 | routes, 25 | ), 26 | MatDatepickerModule, 27 | MatInputModule, 28 | NgxMatDatetimePickerModule, 29 | NgxMatTimepickerModule, 30 | FormsModule, 31 | ReactiveFormsModule, 32 | MatButtonModule, 33 | NgxMatNativeDateModule, 34 | MatRadioModule, 35 | MatSelectModule, 36 | MatCheckboxModule, 37 | MatIconModule, 38 | MatCardModule, 39 | ], 40 | declarations: [DemoDatetimeComponent] 41 | }) 42 | export class DemoDatetimeModule { } 43 | -------------------------------------------------------------------------------- /src/app/demo-time/demo-time.component.html: -------------------------------------------------------------------------------- 1 | Selected date: {{date?.toLocaleString()}} 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 |
12 | disabled (Default: false) 13 | 14 | 15 | {{option.label}} 16 | 17 |
18 | 19 |
20 | showSpinners (Default: true) 21 | 22 | 23 | {{option.label}} 24 | 25 |
26 | 27 | 28 |
29 | showSeconds (Default: false) 30 | 31 | 32 | {{option.label}} 33 | 34 |
35 | 36 |
37 | enableMeridian (Default: false) 38 | 39 | 40 | {{option.label}} 41 | 42 |
43 | 44 |
45 | steps 46 | 47 | hour (default: 1) 48 | 49 | 50 | {{step}} 51 | 52 | 53 | 54 | 55 | minute (default: 1) 56 | 57 | 58 | {{step}} 59 | 60 | 61 | 62 | 63 | 64 | second (default: 1) 65 | 66 | 67 | {{step}} 68 | 69 | 70 | 71 |
72 | 73 |
74 | color (Default: primary) 75 | 76 | Select color 77 | 78 | 79 | {{item}} 80 | 81 | 82 | 83 |
84 |
85 | 86 | 87 |
88 |   
89 |     {{codeTimePicker}}
90 |   
91 | 
92 |
-------------------------------------------------------------------------------- /src/app/demo-time/demo-time.component.scss: -------------------------------------------------------------------------------- 1 | @import "../shared/_common"; -------------------------------------------------------------------------------- /src/app/demo-time/demo-time.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ThemePalette } from '@angular/material/core'; 3 | 4 | @Component({ 5 | selector: 'app-demo-time', 6 | templateUrl: './demo-time.component.html', 7 | styleUrls: ['./demo-time.component.scss'] 8 | }) 9 | export class DemoTimeComponent implements OnInit { 10 | 11 | public disabled = false; 12 | public showSpinners = true; 13 | public showSeconds = false; 14 | public enableMeridian = false; 15 | public stepHour = 1; 16 | public stepMinute = 1; 17 | public stepSecond = 1; 18 | public color: ThemePalette = 'primary'; 19 | 20 | public codeTimePicker = ` 21 | 27 | `; 28 | 29 | 30 | public date: Date; 31 | 32 | public options = [ 33 | { value: true, label: 'True' }, 34 | { value: false, label: 'False' } 35 | ]; 36 | 37 | public listColors = ['primary', 'accent', 'warn']; 38 | 39 | public stepHours = [1, 2, 3, 4, 5]; 40 | public stepMinutes = [1, 5, 10, 15, 20, 25]; 41 | public stepSeconds = [1, 5, 10, 15, 20, 25]; 42 | 43 | constructor() { } 44 | 45 | ngOnInit() { } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/app/demo-time/demo-time.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatCardModule } from '@angular/material/card'; 6 | import { MatCheckboxModule } from '@angular/material/checkbox'; 7 | import { MatDatepickerModule } from '@angular/material/datepicker'; 8 | import { MatIconModule } from '@angular/material/icon'; 9 | import { MatInputModule } from '@angular/material/input'; 10 | import { MatRadioModule } from '@angular/material/radio'; 11 | import { MatSelectModule } from '@angular/material/select'; 12 | import { RouterModule, Routes } from '@angular/router'; 13 | import { NgxMatDatetimePickerModule, NgxMatNativeDateModule, NgxMatTimepickerModule } from 'projects/datetime-picker/src/public-api'; 14 | import { DemoTimeComponent } from './demo-time.component'; 15 | 16 | const routes: Routes = [ 17 | { path: '', component: DemoTimeComponent } 18 | ] 19 | 20 | @NgModule({ 21 | imports: [ 22 | CommonModule, 23 | RouterModule.forChild( 24 | routes, 25 | ), 26 | MatDatepickerModule, 27 | MatInputModule, 28 | NgxMatDatetimePickerModule, 29 | NgxMatTimepickerModule, 30 | FormsModule, 31 | ReactiveFormsModule, 32 | MatButtonModule, 33 | NgxMatNativeDateModule, 34 | MatRadioModule, 35 | MatSelectModule, 36 | MatCheckboxModule, 37 | MatIconModule, 38 | MatCardModule, 39 | ], 40 | declarations: [DemoTimeComponent] 41 | }) 42 | export class DemoTimeModule { } 43 | -------------------------------------------------------------------------------- /src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 |

2 | home works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/home/home.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/src/app/home/home.component.scss -------------------------------------------------------------------------------- /src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'ngx-mat-home', 5 | templateUrl: './home.component.html', 6 | styleUrls: ['./home.component.scss'] 7 | }) 8 | export class HomeComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home.component'; -------------------------------------------------------------------------------- /src/app/shared/_common.scss: -------------------------------------------------------------------------------- 1 | .config-wrapper { 2 | margin-bottom: 15px; 3 | .label { 4 | font-weight: bold; 5 | margin-right: 15px; 6 | } 7 | .mat-radio-button ~ .mat-radio-button { 8 | margin-left: 16px; 9 | } 10 | 11 | &_step { 12 | .mat-form-field { 13 | margin-right: 20px; 14 | width: 100px; 15 | } 16 | } 17 | } 18 | 19 | .zone { 20 | display: flex; 21 | justify-content: center; 22 | margin-bottom: 20px; 23 | 24 | &-value { 25 | font-weight: bold; 26 | } 27 | 28 | &-config { 29 | flex-direction: column; 30 | } 31 | } 32 | 33 | pre { 34 | line-height: 140%; 35 | white-space: pre; 36 | white-space: pre-wrap; 37 | white-space: -moz-pre-wrap; 38 | white-space: -o-pre-wrap; 39 | border: 0.5px dashed rgba(0, 0, 0, 0.12); 40 | padding-left: 10px; 41 | padding-right: 10px; 42 | background-color: #f5f5f5; 43 | } 44 | 45 | code { 46 | line-height: 140%; 47 | white-space: pre; 48 | white-space: pre-wrap; 49 | white-space: -moz-pre-wrap; 50 | white-space: -o-pre-wrap; 51 | } 52 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/GitHub-Mark-Light-32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/src/assets/GitHub-Mark-Light-32px.png -------------------------------------------------------------------------------- /src/assets/btn_donate_LG.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/src/assets/btn_donate_LG.gif -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2qutc/ngx-mat-datetime-picker/589180053342930731050e6213e30a58a66ccc43/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-material-components 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage/demoAngular'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import '~@angular/material/prebuilt-themes/indigo-pink.css'; 3 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "allowSyntheticDefaultImports": true, 13 | "importHelpers": true, 14 | "target": "es5", 15 | "typeRoots": [ 16 | "node_modules/@types" 17 | ], 18 | "lib": [ 19 | "es2018", 20 | "dom" 21 | ], 22 | "paths": { 23 | "@angular-material-components/*": [ 24 | "dist/@angular-material-components/*" 25 | // "projects/*" 26 | ], 27 | "@angular-material-components/color-picker": [ 28 | "dist/@angular-material-components/color-picker" 29 | // "projects/color-picker" 30 | ], 31 | "@angular-material-components/color-picker/*": [ 32 | "dist/@angular-material-components/color-picker/*" 33 | // "projects/color-picker/*" 34 | ], 35 | "@angular-material-components/datetime-picker": [ 36 | "dist/@angular-material-components/datetime-picker" 37 | // "projects/datetime-picker" 38 | ], 39 | "@angular-material-components/datetime-picker/*": [ 40 | "dist/@angular-material-components/datetime-picker/*" 41 | // "projects/datetime-picker/*" 42 | ] 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "array-type": false, 8 | "arrow-parens": false, 9 | "deprecation": { 10 | "severity": "warn" 11 | }, 12 | "import-blacklist": [ 13 | true, 14 | "rxjs/Rx" 15 | ], 16 | "interface-name": false, 17 | "max-classes-per-file": false, 18 | "max-line-length": [ 19 | true, 20 | 140 21 | ], 22 | "member-access": false, 23 | "member-ordering": [ 24 | true, 25 | { 26 | "order": [ 27 | "static-field", 28 | "instance-field", 29 | "static-method", 30 | "instance-method" 31 | ] 32 | } 33 | ], 34 | "no-consecutive-blank-lines": false, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-empty": false, 44 | "no-inferrable-types": [ 45 | true, 46 | "ignore-params" 47 | ], 48 | "no-non-null-assertion": true, 49 | "no-redundant-jsdoc": true, 50 | "no-switch-case-fall-through": true, 51 | "no-use-before-declare": true, 52 | "no-var-requires": false, 53 | "object-literal-key-quotes": [ 54 | true, 55 | "as-needed" 56 | ], 57 | "object-literal-sort-keys": false, 58 | "ordered-imports": false, 59 | "quotemark": [ 60 | true, 61 | "single" 62 | ], 63 | "trailing-comma": false, 64 | "no-output-on-prefix": true, 65 | "use-input-property-decorator": true, 66 | "use-output-property-decorator": true, 67 | "use-host-property-decorator": true, 68 | "no-input-rename": true, 69 | "no-output-rename": true, 70 | "use-life-cycle-interface": true, 71 | "use-pipe-transform-interface": true, 72 | "component-class-suffix": true, 73 | "directive-class-suffix": true 74 | } 75 | } 76 | --------------------------------------------------------------------------------