├── .gitignore ├── .idea ├── inspectionProfiles │ └── Project_Default.xml ├── ionic3-datepicker.iml ├── jsLinters │ └── tslint.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── .npmignore ├── LICENSE ├── README.md ├── demo ├── .editorconfig ├── .gitignore ├── config.xml ├── ionic.config.json ├── package.json ├── server.js ├── src │ ├── app │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── app.scss │ │ └── main.ts │ ├── assets │ │ └── icon │ │ │ └── favicon.ico │ ├── declarations.d.ts │ ├── index.html │ ├── manifest.json │ ├── pages │ │ └── home │ │ │ ├── home.html │ │ │ ├── home.scss │ │ │ └── home.ts │ ├── service-worker.js │ └── theme │ │ └── variables.scss ├── tsconfig.json └── tslint.json ├── index.ts ├── package.json ├── scripts └── copy-package.js ├── src ├── components │ ├── datepicker.component.html │ ├── datepicker.component.scss │ ├── datepicker.component.ts │ ├── datepicker.directive.ts │ ├── datepicker.interface.ts │ ├── datepicker.modal.ts │ ├── datepicker.module.ts │ ├── datepicker.service.ts │ └── nls.ts └── index.ts ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | aot/ 4 | debug.log 5 | npm-debug.log -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/ionic3-datepicker.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/jsLinters/tslint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | years 293 | month 294 | months 295 | config 296 | 297 | 298 | 299 | 301 | 302 | 307 | 310 | 311 | 312 | 325 | 326 | 327 | 328 | 329 | true 330 | DEFINITION_ORDER 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | JSP Inspections 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 38 | 39 | 40 | 41 | 45 | 52 | 53 |
54 | 56 | {{limitTo(dayOfWeek,3)}} 57 | 58 |
59 |
60 |
62 | 71 | {{getDate(i, j) | date:'d'}} 72 | 73 |
74 |
75 | 76 | 86 | -------------------------------------------------------------------------------- /src/components/datepicker.component.scss: -------------------------------------------------------------------------------- 1 | $headerHeight:35%; 2 | $bodyheight:1; 3 | $footerheight:60px; 4 | ionic2-datepicker{ 5 | .datepicker-wrapper{ 6 | height: 100%; 7 | background-color:white; 8 | display: flex; 9 | flex-direction: column; 10 | justify-content: space-between; 11 | .datepicker-header{ 12 | color:white; 13 | background-color: #009688; 14 | display:flex; 15 | flex-flow: column; 16 | height:$headerHeight; 17 | .date-header{ 18 | display:flex; 19 | flex-flow: column; 20 | text-align: center; 21 | .datepicker-day-of-month { 22 | font-size: 60px; 23 | font-weight: 700 24 | } 25 | .datepicker-year, .datepicker-month { 26 | font-size: 14px; 27 | margin-top: 10px; 28 | margin-bottom: 10px; 29 | } 30 | } 31 | .weekday-header{ 32 | padding: 8px 10px; 33 | background-color: #008d7f; 34 | .weekday-title{ 35 | font-weight: bold; 36 | text-align: center; 37 | } 38 | } 39 | } 40 | } 41 | 42 | .weekdays-row{ 43 | text-align:center; 44 | } 45 | 46 | .datepicker-calendar{ 47 | height:calc(100% - (#{$headerHeight} + #{$footerheight})); 48 | .datepicker-controls{ 49 | align-items: center; 50 | justify-content: space-between; 51 | } 52 | .calendar-wrapper{ 53 | height:calc(100% - #{$footerheight} - 40px); 54 | display: flex; 55 | flex-direction: column; 56 | justify-content: space-around; 57 | .datepicker-date-col:hover { 58 | background-color: rgba(56, 126, 245, .5); 59 | border-radius: 20px; 60 | } 61 | 62 | .datepicker-selected { 63 | background-color: rgba(182, 217, 214, 1); 64 | border-radius:20px; 65 | } 66 | 67 | .datepicker-mark { 68 | background-color: rgba(202, 157, 14, 1); 69 | border-radius:20px; 70 | } 71 | 72 | .datepicker-current { 73 | color: rgba(60, 170, 159, 1); 74 | border-radius:20px; 75 | } 76 | 77 | .datepicker-disabled { 78 | color: rgba(170, 170, 170, 1) 79 | } 80 | 81 | .datepicker-disabled:hover { 82 | background-color: transparent; 83 | cursor: default 84 | } 85 | 86 | .calendar-cell { 87 | flex-flow:row wrap; 88 | text-align: center; 89 | 90 | } 91 | } 92 | } 93 | 94 | .datepicker-footer{ 95 | display:flex; 96 | justify-content: space-between; 97 | height:$footerheight; 98 | button{ 99 | width:100%; 100 | } 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /src/components/datepicker.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, ViewEncapsulation} from '@angular/core'; 2 | import {NavParams, ViewController} from 'ionic-angular'; 3 | 4 | import {DateService} from './datepicker.service'; 5 | import {FormControl} from '@angular/forms'; 6 | 7 | @Component({ 8 | template: ` 9 |
10 |
12 |
13 |
{{getSelectedWeekday()}}
14 |
15 |
16 |
17 |
18 | {{limitTo(getSelectedMonth(), 3)}} 19 |
20 |
21 |
22 |
23 | {{selectedDate | date: 'd'}} 24 |
25 |
26 |
27 |
28 | {{selectedDate | date: 'yyyy'}} 29 |
30 |
31 |
32 |
33 |
35 |
36 | 46 | 50 | 54 | 63 |
64 |
65 | 67 | {{limitTo(dayOfWeek, 3)}} 68 | 69 |
70 |
71 |
73 | 82 | {{getDate(i, j) | date:'d'}} 83 | 84 |
85 |
86 |
87 | 101 |
102 | `, 103 | styles: [` 104 | ionic2-datepicker .datepicker-wrapper { 105 | height: 100%; 106 | background-color: white; 107 | display: flex; 108 | flex-direction: column; 109 | justify-content: space-between; 110 | } 111 | 112 | ionic2-datepicker .datepicker-wrapper .datepicker-header { 113 | color: white; 114 | background-color: #009688; 115 | display: flex; 116 | flex-flow: column; 117 | height: 35%; 118 | } 119 | 120 | ionic2-datepicker .datepicker-wrapper .datepicker-header .date-header { 121 | display: flex; 122 | flex-flow: column; 123 | text-align: center; 124 | } 125 | 126 | ionic2-datepicker .datepicker-wrapper .datepicker-header .date-header .datepicker-day-of-month { 127 | font-size: 60px; 128 | font-weight: 700; 129 | } 130 | 131 | ionic2-datepicker .datepicker-wrapper .datepicker-header .date-header .datepicker-year, ionic2-datepicker .datepicker-wrapper .datepicker-header .date-header .datepicker-month { 132 | font-size: 14px; 133 | margin-top: 10px; 134 | margin-bottom: 10px; 135 | } 136 | 137 | ionic2-datepicker .datepicker-wrapper .datepicker-header .weekday-header { 138 | padding: 8px 10px; 139 | background-color: #008d7f; 140 | } 141 | 142 | ionic2-datepicker .datepicker-wrapper .datepicker-header .weekday-header .weekday-title { 143 | font-weight: bold; 144 | text-align: center; 145 | } 146 | 147 | ionic2-datepicker .weekdays-row { 148 | text-align: center; 149 | } 150 | 151 | ionic2-datepicker .datepicker-calendar { 152 | height: calc(100% - (35% + 60px)); 153 | } 154 | 155 | ionic2-datepicker .datepicker-calendar .datepicker-controls { 156 | align-items: center; 157 | justify-content: space-between; 158 | } 159 | 160 | ionic2-datepicker .datepicker-calendar .calendar-wrapper { 161 | height: calc(100% - 60px - 40px); 162 | display: flex; 163 | flex-direction: column; 164 | justify-content: space-around; 165 | } 166 | 167 | ionic2-datepicker .datepicker-calendar .calendar-wrapper .datepicker-mark { 168 | background-color: #5b6c6b; 169 | border-radius: 20px; 170 | } 171 | 172 | ionic2-datepicker .datepicker-calendar .calendar-wrapper .datepicker-selected { 173 | background-color: #b6d9d6; 174 | border-radius: 20px; 175 | } 176 | 177 | ionic2-datepicker .datepicker-calendar .calendar-wrapper .datepicker-current { 178 | color: #3caa9f; 179 | border-radius: 20px; 180 | } 181 | 182 | ionic2-datepicker .datepicker-calendar .calendar-wrapper .datepicker-disabled { 183 | color: #aaaaaa; 184 | } 185 | 186 | ionic2-datepicker .datepicker-calendar .calendar-wrapper .calendar-cell { 187 | flex-flow: row wrap; 188 | text-align: center; 189 | } 190 | 191 | ionic2-datepicker .datepicker-footer { 192 | display: flex; 193 | justify-content: space-between; 194 | height: 60px; 195 | } 196 | 197 | ionic2-datepicker .datepicker-footer button { 198 | width: 100%; 199 | } 200 | 201 | `], 202 | selector: 'ionic2-datepicker', 203 | encapsulation: ViewEncapsulation.None, 204 | }) 205 | 206 | export class DatePickerComponent { 207 | public config: { 208 | okText: string, 209 | cancelText: string, 210 | min: Date, 211 | max: Date, 212 | ionChanged: EventEmitter, 213 | ionSelected: EventEmitter, 214 | ionCanceled: EventEmitter, 215 | headerClasses: string[], 216 | bodyClasses: string[], 217 | date: Date, 218 | disabledDates: Date[], 219 | markDates: Date[], 220 | showMaxAndMin: boolean; 221 | }; 222 | public selectedDate: Date = new Date(); 223 | public dateList: Date[]; 224 | public cols: number[]; 225 | public rows: number[]; 226 | public weekdays: string[]; 227 | public months: string[]; 228 | public years: string[]; 229 | public active: boolean = false; 230 | private tempDate: Date; 231 | private today: Date = new Date(); 232 | 233 | 234 | public yearsMaxMin: number[]; 235 | public selectedMonth; 236 | public selectedYear; 237 | public monthChanged: FormControl; 238 | public yearChanged: FormControl; 239 | public nextDisabled: boolean = false; 240 | public previousDisabled: boolean = false; 241 | private maxYear: number; 242 | private minYear: number; 243 | 244 | constructor(public viewCtrl: ViewController, 245 | public navParams: NavParams, 246 | public DatepickerService: DateService) { 247 | this.config = this.navParams.data; 248 | this.selectedDate = this.navParams.data.date; 249 | this.initialize(); 250 | 251 | } 252 | 253 | 254 | /** 255 | * @function initialize - Initializes date variables 256 | */ 257 | public initialize(): void { 258 | this.tempDate = this.selectedDate; 259 | this.createDateList(this.selectedDate); 260 | this.weekdays = this.DatepickerService.getDaysOfWeek(); 261 | this.months = this.DatepickerService.getMonths(); 262 | this.years = this.DatepickerService.getYears(); 263 | this.initSelectBoxes(); 264 | this.initSelectBoxListener(); 265 | } 266 | 267 | /** 268 | * initializes the selectbox and considers max and min date 269 | */ 270 | private initSelectBoxes() { 271 | let maxDate = this.config.max; 272 | let minDate = this.config.min; 273 | this.maxYear = Number.parseInt(this.years[this.years.length - 1]); 274 | this.minYear = Number.parseInt(this.years[0]); 275 | if (maxDate) { 276 | this.maxYear = maxDate.getFullYear(); 277 | } 278 | if (minDate) { 279 | this.minYear = minDate.getFullYear(); 280 | } 281 | this.yearsMaxMin = []; 282 | for (let _minYear = this.minYear; _minYear <= this.maxYear; _minYear++) { 283 | this.yearsMaxMin.push(_minYear); 284 | } 285 | this.selectedMonth = this.getSelectedMonth(); 286 | this.selectedYear = this.getSelectedYear(); 287 | } 288 | 289 | /** 290 | * initializes the listener to change the dategrid if that changes 291 | */ 292 | private initSelectBoxListener() { 293 | this.monthChanged = new FormControl(); 294 | this.monthChanged.valueChanges 295 | .subscribe(selectedMonth => { 296 | let monthAsNumber = this.months.indexOf(selectedMonth); 297 | let testDate: Date = new Date(this.tempDate.getTime()); 298 | testDate.setMonth(monthAsNumber); 299 | this.tempDate = testDate; 300 | this.createDateList(this.tempDate); 301 | this.checkDisableButtons(monthAsNumber, testDate.getFullYear()); 302 | }); 303 | this.yearChanged = new FormControl(); 304 | this.yearChanged.valueChanges 305 | .subscribe(selectedYear => { 306 | let testDate: Date = new Date(this.tempDate.getTime()); 307 | testDate.setFullYear(selectedYear); 308 | this.tempDate = testDate; 309 | this.createDateList(this.tempDate); 310 | this.checkDisableButtons(testDate.getMonth(), selectedYear); 311 | }); 312 | } 313 | 314 | /** 315 | * checks if the forward/previos buttons should be disabled or not 316 | * @param selectedMonth 317 | * @param selectedYear 318 | */ 319 | checkDisableButtons(selectedMonth, selectedYear) { 320 | this.nextDisabled = selectedMonth == 11 && this.maxYear === selectedYear; 321 | this.previousDisabled = selectedMonth == 0 && this.minYear === selectedYear; 322 | } 323 | 324 | /** 325 | * @function createDateList - creates the list of dates 326 | * @param selectedDate - creates the list based on the currently selected date 327 | */ 328 | public createDateList(selectedDate: Date): void { 329 | this.dateList = this.DatepickerService.createDateList(selectedDate); 330 | this.cols = new Array(7); 331 | this.rows = new Array(Math.ceil(this.dateList.length / this.cols.length)); 332 | } 333 | 334 | /** 335 | * @function getDate - gets the actual date of date from the list of dates 336 | * @param row - the row of the date in a month. For instance 14 date would be 3rd or 2nd row 337 | * @param col - the column of the date in a month. For instance 1 would be on the column of the weekday. 338 | */ 339 | public getDate(row: number, col: number): Date { 340 | /** 341 | * @description The locale en-US is noted for the sake of starting with monday if its in usa 342 | */ 343 | return this.dateList[(row * 7 + col) + ((this.DatepickerService.doesStartFromMonday()) ? 1 : 0)]; 344 | } 345 | 346 | /** 347 | * @function getDate - gets the actual number of date from the list of dates 348 | * @param row - the row of the date in a month. For instance 14 date would be 3rd or 2nd row 349 | * @param col - the column of the date in a month. For instance 1 would be on the column of the weekday. 350 | */ 351 | public getDateAsDay(row: number, col: number): number { 352 | let date = this.getDate(row, col); 353 | if (date) return date.getDate(); 354 | } 355 | 356 | 357 | /** 358 | * @function isDisabled - Checks whether the date should be disabled or not 359 | * @param date - the date to test against 360 | */ 361 | public isDisabled(date: Date): boolean { 362 | if (!date) return true; 363 | if (this.config.min) { 364 | this.config.min.setHours(0, 0, 0, 0); 365 | if (date < this.config.min) return true; 366 | } 367 | if (this.config.max) { 368 | this.config.max.setHours(0, 0, 0, 0); 369 | if (date > this.config.max) return true; 370 | } 371 | if (this.config.disabledDates) { 372 | return this.config.disabledDates.some(disabledDate => 373 | this.areEqualDates(new Date(disabledDate), date)); 374 | } 375 | return false; 376 | } 377 | 378 | public isMark(date: Date): boolean { 379 | if (!date) return false; 380 | if (this.config.markDates) { 381 | return this.config.markDates.some(markDate => 382 | this.areEqualDates(new Date(markDate), date)); 383 | } 384 | return false 385 | } 386 | 387 | public isActualDate(date: Date): boolean { 388 | if (!date) return false; 389 | return this.areEqualDates(date, this.today); 390 | } 391 | 392 | public isActualMonth(month: number): boolean { 393 | return month === this.today.getMonth(); 394 | } 395 | 396 | public isActualYear(year: number): boolean { 397 | return year === this.today.getFullYear(); 398 | } 399 | 400 | public isSelectedDate(date: Date): boolean { 401 | if (!date) return false; 402 | return this.areEqualDates(date, this.selectedDate); 403 | } 404 | 405 | public isSelectedMonth(month: number): boolean { 406 | return month === this.tempDate.getMonth(); 407 | } 408 | 409 | public isSelectedYear(year: number): boolean { 410 | return year === this.tempDate.getFullYear(); 411 | } 412 | 413 | 414 | public selectDate(date: Date): void { 415 | if (this.isDisabled(date)) return; 416 | this.selectedDate = date; 417 | this.selectedDate.setHours(0, 0, 0, 0); 418 | this.tempDate = this.selectedDate; 419 | this.config.ionSelected.emit(this.tempDate); 420 | } 421 | 422 | 423 | public getSelectedWeekday(): string { 424 | return this.weekdays[this.selectedDate.getDay()]; 425 | } 426 | 427 | public getSelectedMonth(date?: Date): string { 428 | if (!date) { 429 | return this.months[this.selectedDate.getMonth()]; 430 | } else { 431 | return this.months[date.getMonth()]; 432 | } 433 | } 434 | 435 | public getTempMonth() { 436 | return this.months[this.tempDate.getMonth()]; 437 | } 438 | 439 | public getTempYear() { 440 | return (this.tempDate || this.selectedDate).getFullYear(); 441 | } 442 | 443 | public getSelectedDate() { 444 | return (this.selectedDate || new Date()).getDate(); 445 | } 446 | 447 | public getSelectedYear() { 448 | return (this.selectedDate || new Date()).getFullYear(); 449 | } 450 | 451 | 452 | public onCancel(e: Event) { 453 | if (this.config.date) 454 | this.selectedDate = this.config.date || new Date(); 455 | this.config.ionCanceled.emit(); 456 | this.viewCtrl.dismiss(); 457 | }; 458 | 459 | public onDone(e: Event) { 460 | this.config.date = this.selectedDate; 461 | this.config.ionChanged.emit(this.config.date); 462 | this.viewCtrl.dismiss(); 463 | }; 464 | 465 | public selectMonthOrYear() { 466 | this.createDateList(this.tempDate); 467 | if (this.isDisabled(this.tempDate)) return; 468 | this.selectedDate = this.tempDate; 469 | } 470 | 471 | private areEqualDates(dateA: Date, dateB: Date) { 472 | return dateA.getDate() === dateB.getDate() && 473 | dateA.getMonth() === dateB.getMonth() && 474 | dateA.getFullYear() === dateB.getFullYear(); 475 | } 476 | 477 | public limitTo(arr: Array | string, limit: number): Array | string { 478 | if (this.DatepickerService.locale === 'custom') return arr; 479 | if (this.DatepickerService.locale === 'de') limit = 2; 480 | if (Array.isArray(arr)) 481 | return arr.splice(0, limit); 482 | if (this.DatepickerService.locale === 'zh-CN' || this.DatepickerService.locale === 'zh-TW') 483 | arr = arr.replace('星期', '') 484 | return (arr).slice(0, limit); 485 | } 486 | 487 | public getMonthRows(): {}[] { 488 | return []; 489 | } 490 | 491 | 492 | public nextMonth() { 493 | //if (this.max.getMonth() < this.tempDate.getMonth() + 1 && this.min.getFullYear() === this.tempDate.getFullYear()) return; 494 | let testDate: Date = new Date(this.tempDate.getTime()); 495 | testDate.setDate(1); 496 | 497 | if (testDate.getMonth() === 11) { 498 | testDate.setFullYear(testDate.getFullYear() + 1); 499 | testDate.setMonth(0); 500 | } 501 | else { 502 | testDate.setMonth(testDate.getMonth() + 1); 503 | } 504 | if ((!this.config.max || this.config.max >= testDate) || this.config.showMaxAndMin) { 505 | this.setDateAfterSelection(testDate); 506 | } 507 | } 508 | 509 | public prevMonth() { 510 | let testDate: Date = new Date(this.tempDate.getTime()); 511 | testDate.setDate(0); 512 | // testDate.setDate(this.tempDate.getDate()); 513 | if ((!this.config.min || 514 | (this.config.min <= testDate)) || this.config.showMaxAndMin) { 515 | this.setDateAfterSelection(testDate); 516 | } 517 | } 518 | 519 | /** 520 | * calls to create the days (list/grid) if applicable 521 | * @param {Date} testDate 522 | */ 523 | private setDateAfterSelection(testDate: Date) { 524 | this.tempDate = testDate; 525 | this.createDateList(this.tempDate); 526 | this.selectedMonth = this.getSelectedMonth(this.tempDate); 527 | this.selectedYear = this.tempDate.getFullYear(); 528 | this.checkDisableButtons(this.tempDate.getMonth(), this.selectedYear); 529 | } 530 | } -------------------------------------------------------------------------------- /src/components/datepicker.directive.ts: -------------------------------------------------------------------------------- 1 | import {ModalOptions} from 'ionic-angular'; 2 | import {DatePickerController, DatePickerDisplayer} from './datepicker.modal'; 3 | import {Directive, EventEmitter, HostListener, Input, Output} from '@angular/core'; 4 | 5 | import {DatePickerData} from './datepicker.interface'; 6 | import {DateService} from './datepicker.service'; 7 | 8 | @Directive({ 9 | selector: 'ion-datepicker,[ion-datepicker]', 10 | }) 11 | export class DatePickerDirective { 12 | @Output('ionChanged') public changed: EventEmitter = new EventEmitter(); 13 | @Output('ionCanceled') public canceled: EventEmitter = new EventEmitter(); 14 | 15 | @Input() public max: Date; 16 | @Input() public min: Date; 17 | 18 | @Input() 19 | public set locale(val: string) { 20 | if (val) 21 | this.dateService.locale = val; 22 | }; 23 | 24 | @Input() 25 | public set localeStrings(val: { weekdays: string[], months: string[] }) { 26 | if (val) { 27 | this.dateService.locale = 'custom'; 28 | this.locale = 'custom'; 29 | this.dateService.setCustomNls(val); 30 | } 31 | }; 32 | 33 | @Input() public okText: string; 34 | @Input() public cancelText: string; 35 | @Input() public bodyClasses: Array; 36 | @Input() public headerClasses: Array; 37 | @Input() public modalOptions: ModalOptions; 38 | @Input() public value: Date = new Date(); 39 | @Input() public disabledDates: Date[] = []; 40 | @Input() public markDates: Date[] = []; 41 | @Input() public showMaxAndMin: boolean = false; 42 | public dateSelected: EventEmitter = new EventEmitter(); 43 | public modal: DatePickerDisplayer; 44 | private _fn: any; 45 | 46 | constructor(public datepickerCtrl: DatePickerController, 47 | public dateService: DateService) { 48 | this.changed.subscribe((d: Date) => { 49 | this.value = d; 50 | }); 51 | } 52 | 53 | @HostListener('tap', ['$event']) 54 | _click(ev: UIEvent) { 55 | this.open(); 56 | } 57 | 58 | public open() { 59 | const data = { 60 | min: this.min, 61 | max: this.max, 62 | bodyClasses: this.bodyClasses, 63 | headerClasses: this.headerClasses, 64 | ionChanged: this.changed, 65 | ionCanceled: this.canceled, 66 | ionSelected: this.dateSelected, 67 | date: this.value, 68 | okText: this.okText, 69 | cancelText: this.cancelText, 70 | disabledDates: this.disabledDates, 71 | markDates: this.markDates, 72 | showMaxAndMin: this.showMaxAndMin 73 | }; 74 | this.modal = this.datepickerCtrl.create(data, this.modalOptions); 75 | this.modal.present(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/components/datepicker.interface.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "@angular/core"; 2 | import { languages } from './nls'; 3 | 4 | export interface DatePickerData { 5 | okText?: string, 6 | cancelText?: string, 7 | showMaxAndMin: boolean, 8 | min?: Date, 9 | max?: Date, 10 | ionChanged: EventEmitter, 11 | ionSelected: EventEmitter, 12 | ionCanceled: EventEmitter, 13 | headerClasses?: string[], 14 | bodyClasses?: string[], 15 | date?: Date 16 | locale?: languages; 17 | disabledDates: Date[]; 18 | markDates: Date[]; 19 | } -------------------------------------------------------------------------------- /src/components/datepicker.modal.ts: -------------------------------------------------------------------------------- 1 | import { App, ModalCmp, ModalOptions, NavOptions, ViewController } from 'ionic-angular'; 2 | import { ModalMDSlideIn, ModalMDSlideOut, ModalSlideIn, ModalSlideOut } from 'ionic-angular/components/modal/modal-transitions'; 3 | 4 | import { Config } from 'ionic-angular/config/config'; 5 | import { DatePickerComponent } from './datepicker.component'; 6 | import { Injectable } from '@angular/core'; 7 | import { PORTAL_MODAL } from 'ionic-angular/components/app/app-constants'; 8 | import { isPresent } from 'ionic-angular/util/util'; 9 | 10 | /** 11 | * @private 12 | */ 13 | export class DatePickerDisplayer extends ViewController { 14 | private _app: App; 15 | private _enterAnimation: string; 16 | private _leaveAnimation: string; 17 | 18 | constructor(app: App, component: any, data: any, opts: ModalOptions = {}, config: Config) { 19 | 20 | data = data || {}; 21 | data.component = component; 22 | opts.showBackdrop = isPresent(opts.showBackdrop) ? !!opts.showBackdrop : true; 23 | opts.enableBackdropDismiss = isPresent(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true; 24 | data.opts = opts; 25 | 26 | super(ModalCmp, data, null); 27 | this._app = app; 28 | this._enterAnimation = opts.enterAnimation; 29 | this._leaveAnimation = opts.leaveAnimation; 30 | 31 | this.isOverlay = true; 32 | 33 | config.setTransition('modal-slide-in', ModalSlideIn); 34 | config.setTransition('modal-slide-out', ModalSlideOut); 35 | config.setTransition('modal-md-slide-in', ModalMDSlideIn); 36 | config.setTransition('modal-md-slide-out', ModalMDSlideOut); 37 | } 38 | 39 | /** 40 | * @private 41 | */ 42 | getTransitionName(direction: string): string { 43 | let key: string; 44 | if (direction === 'back') { 45 | if (this._leaveAnimation) { 46 | return this._leaveAnimation; 47 | } 48 | key = 'modalLeave'; 49 | } else { 50 | if (this._enterAnimation) { 51 | return this._enterAnimation; 52 | } 53 | key = 'modalEnter'; 54 | } 55 | return this._nav && this._nav.config.get(key); 56 | } 57 | 58 | /** 59 | * Present the action sheet instance. 60 | * 61 | * @param {NavOptions} [opts={}] Nav options to go with this transition. 62 | * @returns {Promise} Returns a promise which is resolved when the transition has completed. 63 | */ 64 | present(navOptions: NavOptions = {}) { 65 | return this._app.present(this, navOptions, PORTAL_MODAL); 66 | } 67 | } 68 | @Injectable() 69 | export class DatePickerController { 70 | 71 | constructor(private _app: App, public config: Config) { } 72 | /** 73 | * Create a modal to display. See below for options. 74 | * 75 | * @param {object} component The Modal view 76 | * @param {object} data Any data to pass to the Modal view 77 | * @param {object} opts Modal options 78 | */ 79 | create(data: any = {}, opts: ModalOptions = {}) { 80 | data.component = DatePickerComponent; 81 | opts.showBackdrop = opts.showBackdrop !== undefined ? !!opts.showBackdrop : true; 82 | opts.enableBackdropDismiss = opts.enableBackdropDismiss !== undefined ? !!opts.enableBackdropDismiss : true; 83 | data.opts = opts; 84 | return new DatePickerDisplayer(this._app, data.component, data, opts, this.config); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/components/datepicker.module.ts: -------------------------------------------------------------------------------- 1 | import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core'; 2 | 3 | import {CommonModule} from '@angular/common'; 4 | import {DatePickerComponent} from './datepicker.component'; 5 | import {DatePickerController} from './datepicker.modal'; 6 | import {DatePickerDirective} from './datepicker.directive'; 7 | import {DateService} from './datepicker.service'; 8 | import {FormsModule, ReactiveFormsModule} from '@angular/forms'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CommonModule, FormsModule, ReactiveFormsModule 13 | ], 14 | exports: [ 15 | DatePickerComponent, 16 | DatePickerDirective], 17 | entryComponents: [DatePickerComponent], 18 | declarations: [DatePickerComponent, DatePickerDirective], 19 | providers: [ 20 | DatePickerController, 21 | DateService], 22 | schemas: [ 23 | CUSTOM_ELEMENTS_SCHEMA 24 | ] 25 | }) 26 | export class DatePickerModule { 27 | } 28 | -------------------------------------------------------------------------------- /src/components/datepicker.service.ts: -------------------------------------------------------------------------------- 1 | import {languages, nls} from './nls'; 2 | 3 | import {Injectable} from '@angular/core'; 4 | 5 | @Injectable() 6 | export class DateService { 7 | private static _local: languages = undefined; 8 | 9 | public get locale(): languages { 10 | return DateService._local || 'en-UK'; 11 | } 12 | 13 | public set locale(val: languages) { 14 | if (!nls.checkExists(val)) { 15 | throw 'Locale not recognized as a valid value. Only en-US/he-IL/ru-RU/pt-BR/de avaliable'; 16 | } 17 | DateService._local = val; 18 | } 19 | 20 | constructor() { 21 | } 22 | 23 | // private locale = ((window).navigator['userLanguage'] || window.navigator.language).toLowerCase(); 24 | 25 | public setCustomNls(val: { weekdays: string[], months: string[] }) { 26 | nls._nls.custom = val; 27 | } 28 | 29 | public getDaysOfWeek() { 30 | return nls.getWeekdays(this.locale); 31 | } 32 | 33 | public getMonths() { 34 | return nls.getMonths(this.locale); 35 | } 36 | 37 | public doesStartFromMonday(): boolean { 38 | return nls.getNls(this.locale).monday === true; 39 | } 40 | 41 | public getYears() { 42 | let years: Array = []; 43 | for (let i = 1900; i < 2101; i++) years.push(i); 44 | return years; 45 | } 46 | 47 | public createDateList(currentDate: Date) { 48 | let firstDayOfWeek = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1).getDay(); 49 | let firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1).getDate(); 50 | let lastDay = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getDate(); 51 | let dateList: Date[] = []; 52 | 53 | // Empty placeholders so dates align with weekday columns 54 | for (let j = 0; j < firstDayOfWeek; j++) { 55 | dateList.push(undefined); 56 | } 57 | 58 | // Actual dates 59 | for (let i = firstDay; i <= lastDay; i++) { 60 | dateList.push(new Date(currentDate.getFullYear(), currentDate.getMonth(), i)); 61 | } 62 | 63 | return dateList; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/components/nls.ts: -------------------------------------------------------------------------------- 1 | 2 | export module nls { 3 | export const _nls = 4 | { 5 | 'custom': { 6 | 7 | }, 8 | 'en-US': { 9 | monday: true, 10 | weekdays: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], 11 | months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] 12 | }, 13 | 'en-UK': { 14 | weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 15 | months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] 16 | }, 17 | 'pt-BR': { 18 | weekdays: ['Domingo', 'Segunda-Feira', 'Terça-Feira', 'Quarta-Feira', 'Quinta-Feira', 'Sexta-Feira', 'Sábado'], 19 | months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'] 20 | }, 21 | 'he-IL': { 22 | weekdays: ['ראשון', 'שני', 'שלישי', 'רביעי', 'חמישי', 'שישי', 'שבת'], 23 | months: ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'] 24 | }, 25 | 'ru-RU': { 26 | weekdays: ['Воскресение', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'], 27 | months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'] 28 | }, 29 | 'de': { 30 | weekdays: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], 31 | months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 32 | 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'] 33 | }, 34 | 'fi': { 35 | weekdays: ['Sunnuntai', 'Maanantai', 'Tiistai', 'Keskiviikko', 'Torstai', 'Perjantai', 'Lauantai'], 36 | months: ['Tammikuu', 'Helmikuu', 'Maaliskuu', 'Huhtikuu', 'Toukokuu', 'Kesäkuu', 37 | 'Heinäkuu', 'Elokuu', 'Syyskuu', 'Lokakuu', 'Marraskuu', 'Joulukuu'] 38 | }, 39 | 'zh-CN': { 40 | weekdays: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], 41 | months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'] 42 | }, 43 | 'zh-TW': { 44 | weekdays: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], 45 | months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'] 46 | } 47 | }; 48 | 49 | export function getWeekdays(locale: languages): string[] { 50 | return this.getNls(locale).weekdays; 51 | } 52 | export function getMonths(locale: languages): string[] { 53 | return this.getNls(locale).months; 54 | } 55 | export function getNls(locale: languages): { weekdays: string[], months: string[], monday: boolean } { 56 | return this._nls[locale] || this._nls['en-US']; 57 | } 58 | 59 | export function checkExists(locale: string): boolean { 60 | let keys: Array = Object.keys(this._nls); 61 | return keys.some(key => key === locale); 62 | } 63 | } 64 | export type languages = string | 'en-US' | 'en-UK' | 'pt-BR' | 'he-IL' | 'ru-RU' | 'de' | 'fi' | 'zh-TW' | 'zh-CN' | 'custom'; 65 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { DatePickerModule } from './components/datepicker.module'; 2 | export { DatePickerDirective } from './components/datepicker.directive'; 3 | export { DatePickerComponent } from './components/datepicker.component'; 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "target": "es5", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "removeComments": true, 9 | "lib": [ 10 | "es2015", 11 | "dom" 12 | ], 13 | "sourceMap": true, 14 | "outDir": "dist", 15 | "declarationDir": "dist", 16 | "declaration": true 17 | }, 18 | "compileOnSave": true, 19 | "include": [ 20 | "src/**/*" 21 | ], 22 | "exclude": [ 23 | "demo", 24 | "index.d.ts", 25 | "node_modules", 26 | "dist" 27 | ], 28 | "angularCompilerOptions": { 29 | "genDir": "dist/", 30 | "strictMetadataEmit": true, 31 | "skipTemplateCodegen": true 32 | } 33 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-duplicate-variable": true, 4 | "no-unused-variable": [ 5 | true 6 | ] 7 | }, 8 | "rulesDirectory": [ 9 | "node_modules/tslint-eslint-rules/dist/rules" 10 | ] 11 | } 12 | --------------------------------------------------------------------------------