From 6c9a43d97da4066a6009f319f4b132d8b1b66b35 Mon Sep 17 00:00:00 2001 From: KekeH Date: Tue, 1 Dec 2015 20:52:00 +0200 Subject: [PATCH] Initial version. --- .gitignore | 4 + README.md | 27 ++- package.json | 22 +++ src/app/css/mydatepicker.css | 301 +++++++++++++++++++++++++++++ src/app/mydatepicker.ts | 254 ++++++++++++++++++++++++ src/app/sampleapp.ts | 28 +++ src/app/template/mydatepicker.html | 42 ++++ src/index.html | 44 +++++ src/tsconfig.json | 11 ++ 9 files changed, 731 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 package.json create mode 100644 src/app/css/mydatepicker.css create mode 100644 src/app/mydatepicker.ts create mode 100644 src/app/sampleapp.ts create mode 100644 src/app/template/mydatepicker.html create mode 100644 src/index.html create mode 100644 src/tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0c3e157b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +node_modules/ +src/app/*.js +src/app/*.js.map \ No newline at end of file diff --git a/README.md b/README.md index c911ec96..98e57ce4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ -# mydatepicker -AngularJS 2 date picker +# mydatepicker v. 0.0.1 + +**AngularJS 2 date picker - AngularJS reusable UI component** + +## Description +Simple AngularJS 2 date picker. Uses version "2.0.0-alpha.46" of AngularJS 2. + +##Getting Started +1. Fork and clone this repo +2. npm install +3. Run the TypeScript compiler and watch for changes "npm run tsc" +4. Open second terminal and launch the app in the browser "npm start" + +## Demo +Online demo is [here](http://kekeh.github.io/mydatepicker) + +## Compatibility (tested with) +* Firefox (latest) +* Chromium (latest) + +## License +* License: MIT + +## Author +* Author: kekeh \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000..dc16c669 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "MyDatePicker", + "version": "0.0.1", + "description": "", + "main": "index.html", + "scripts": { + "tsc": "tsc -p src -w", + "start": "live-server --open=src" + }, + "keywords": [], + "author": "kekeh", + "license": "MIT", + "dependencies": { + "angular2": "2.0.0-alpha.46", + "systemjs": "0.19.2", + "bootstrap": "3.3.6" + }, + "devDependencies": { + "live-server": "^0.8.2", + "typescript": "^1.6.2" + } +} diff --git a/src/app/css/mydatepicker.css b/src/app/css/mydatepicker.css new file mode 100644 index 00000000..45670423 --- /dev/null +++ b/src/app/css/mydatepicker.css @@ -0,0 +1,301 @@ +.mydatepicker { + min-width: 100px; + border-radius: 2px; + line-height: 1.1; +} + +.mydatepicker * { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + font-family: Arial, Helvetica, sans-serif; + padding: 0; + margin: 0; +} + +.mydatepicker .selector { + margin-top: 3px; + margin-left: -1px; + position: absolute; + max-width: 262px; + padding: 3px; + border-radius: 2px; + background-color: #DDD; + z-index: 100; +} + +.mydatepicker .selectiongroup { + position: relative; + display: table; + border: none; + background-color: #FFF; +} + +.mydatepicker .selection { + background-color: #FFF; + display: table-cell; + position: absolute; + width: 100%; + text-align: left; + font-size: 16px; + font-weight: bold; + padding: 0 64px 0 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: center; +} + +.mydatepicker .selbtngroup { + position: relative; + vertical-align: middle; + white-space: nowrap; + width: 1%; + display: table-cell; + text-align: right; + font-size: 0; +} + +.mydatepicker .btnpicker, +.mydatepicker .btnclear { + height: 100%; + width: 30px; + border: none; + border-left: 1px solid #999; + padding: 0px; + cursor: pointer; + outline: 0; + font: inherit; + -moz-user-select: none; +} + +.mydatepicker .btnpicker, +.mydatepicker .btnclear, +.mydatepicker .headertodaybtn, +.mydatepicker .footerbtn { + background: #FAFAFA; + background-image: -webkit-linear-gradient(#F0F0F0 30%, #AEC2E1 100%); + background-image: -moz-linear-gradient(#F0F0F0 30%, #AEC2E1 100%); + background-image: -o-linear-gradient(#F0F0F0 30%, #AEC2E1 100%); + background-image: -ms-linear-gradient(#F0F0F0 30%, #AEC2E1 100%); + background-image: linear-gradient(#F0F0F0 30%, #AEC2E1 100%); +} + +.mydatepicker .header { + width: 100%; + height: 36px; + margin-bottom: 1px; + background-color: #FAFAFA; +} + +.mydatepicker .header td { + vertical-align: middle; + border: none; +} + +.mydatepicker .header td:nth-child(1) { + font-size: 16px; + padding-left: 4px; +} + +.mydatepicker .header td:nth-child(2) { + text-align: center; +} + +.mydatepicker .header td:nth-child(3) { + font-size: 16px; + padding-right: 4px; +} + +.mydatepicker .caltable { + table-layout: fixed; + width: 100%; + background-color: #FFF; + font-size: 14px; +} + +.mydatepicker .caltable tbody{ + border: 1px solid #AAA; +} + +.mydatepicker .caltable, +.mydatepicker .caltable th, +.mydatepicker .caltable td { + border-collapse: collapse; + color: #003366; + line-height: 1.1; +} + +.mydatepicker .caltable th, +.mydatepicker .caltable td { + padding: 5px; + text-align: center; +} + +.mydatepicker .caltable th { + background-color: #DDD; + font-size: 12px; + vertical-align: middle; +} + +.mydatepicker .caltable td { + cursor: pointer; + font-weight: bold; +} + +.mydatepicker .prevmonth { + color: #CCC; +} + +.dpdatepicker .dpcurrmonth { + color: #000; + font-weight: bold; +} + +.mydatepicker .nextmonth { + color: #CCC; +} + +.mydatepicker .sunday { + color: #C30000; +} + +.mydatepicker .currmonth { + background-color: #FAFAFA; +} + +.mydatepicker .currday { + background-color: #64E986; + text-decoration: underline; +} + +.mydatepicker .selectedday { + background-color: #3BB9FF; +} + +.mydatepicker .selectmenu { + height: 24px; + width: 60px +} + +.mydatepicker .headerbtn { + background-color: #FAFAFA; + cursor: pointer; + display: table-cell; +} + +.mydatepicker, +.mydatepicker .selector, +.mydatepicker .header, +.mydatepicker .footerarea, +.mydatepicker .table, +.mydatepicker .table th, +.mydatepicker .table td, +.mydatepicker .headertodaybtn, +.mydatepicker .footerbtn { + border: 1px solid #AAA; +} + +.mydatepicker .btnpicker, +.mydatepicker .btnclear, +.mydatepicker .headerbtn, +.mydatepicker .headermonthtxt, +.mydatepicker .headeryeartxt, +.mydatepicker .headertodaybtn, +.mydatepicker .selection, +.mydatepicker .footerbtn { + color: #000; +} + +.mydatepicker .headertodaybtn, +.mydatepicker .footerbtn { + padding: 4px 6px; + border-radius: 2px; + cursor: pointer; + font-size: 12px; +} + +.mydatepicker .btndisable { + cursor: default; + opacity: 0.5; +} + +.mydatepicker button::-moz-focus-inner { + border: 0; +} + +.mydatepicker .headermonthtxt, +.mydatepicker .headeryeartxt { + width: 40px; + text-align: center; + display: table-cell; + vertical-align: middle; +} + +.mydatepicker .btnclear:focus, +.mydatepicker .btnpicker:focus, +.mydatepicker .btnclear:hover, +.mydatepicker .btnpicker:hover { + background: #ADD8E6; +} + +.mydatepicker .icon-calendar, +.mydatepicker .icon-cross { + font-size: 16px; +} + +.mydatepicker .icon-left, +.mydatepicker .icon-right { + font-size: 14px; +} + +.mydatepicker .icon-left:hover, +.mydatepicker .icon-right:hover { + color: #63B2CC; +} + + + +.mydatepicker table { + display: table; +} + +.mydatepicker table td { + padding: 0; +} + +@font-face { + font-family: 'mydatepicker'; + src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SAssAAAC8AAAAYGNtYXDMUczTAAABHAAAAGxnYXNwAAAAEAAAAYgAAAAIZ2x5ZmFQ1q4AAAGQAAABbGhlYWQGZuTFAAAC/AAAADZoaGVhB4IDyQAAAzQAAAAkaG10eBYAAnAAAANYAAAAIGxvY2EBdAE0AAADeAAAABJtYXhwABUAPgAAA4wAAAAgbmFtZQ5R9RkAAAOsAAABnnBvc3QAAwAAAAAFTAAAACAAAwOaAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADmBwPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAUAAAABAAEAADAAAAAQAg5gDmAuYF5gf//f//AAAAAAAg5gDmAuYF5gf//f//AAH/4xoEGgMaARoAAAMAAQAAAAAAAAAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAAMAEAAAAPAA4AABAAJAA4AEwAYAB0AIgAnACwAMQA2ADsAABMRMxEjFyE1IRUDITUhFQERMxEjJRUzNSMTFTM1IzMVMzUjMxUzNSMBFTM1IzMVMzUjMxUzNSMTFTM1I0Bzc0ADAP0AQAOA/IADDXNz/ZOAgCCAgMCAgMCAgP6AgIDAgIDAgIAggIADAP1AAsBzc3P9c3NzAwD9QALAgMDA/sCAgICAgID/AICAgICAgAJAwMAAAAAAAgBwADADkANQAAQACQAANwEnARcDATcBB+kCp3n9WXl5Aqd5/Vl5MAKnef1ZeQKn/Vl5Aqd5AAABAOAAAAMgA4AAAwAAAQMBJQMgA/3DASADgPyAAcPfAAEA4AAAAyADgAADAAA3EwEF4AMCPf7gAAOA/j3fAAAAAQAAAAEAAF0/BsNfDzz1AAsEAAAAAADRxFAkAAAAANHEUCQAAAAAA8ADgAAAAAgAAgAAAAAAAAABAAADwP/AAAAEAAAAAAADwAABAAAAAAAAAAAAAAAAAAAACAQAAAAAAAAAAAAAAAIAAAAEAABABAAAcAQAAOAEAADgAAAAAAAKABQAHgB6AJYApgC2AAAAAQAAAAgAPAAMAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAkAAAABAAAAAAACAAcAcgABAAAAAAADAAkAPAABAAAAAAAEAAkAhwABAAAAAAAFAAsAGwABAAAAAAAGAAkAVwABAAAAAAAKABoAogADAAEECQABABIACQADAAEECQACAA4AeQADAAEECQADABIARQADAAEECQAEABIAkAADAAEECQAFABYAJgADAAEECQAGABIAYAADAAEECQAKADQAvHZzZHBpY2tlcgB2AHMAZABwAGkAYwBrAGUAclZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMHZzZHBpY2tlcgB2AHMAZABwAGkAYwBrAGUAcnZzZHBpY2tlcgB2AHMAZABwAGkAYwBrAGUAclJlZ3VsYXIAUgBlAGcAdQBsAGEAcnZzZHBpY2tlcgB2AHMAZABwAGkAYwBrAGUAckZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) format('truetype'); + font-weight: normal; + font-style: normal; +} + +.mydatepicker .icon { + font-family: 'mydatepicker'; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.mydatepicker .icon-calendar:before { + content: "\e600"; +} + +.mydatepicker .icon-cross:before { + content: "\e602"; +} + +.mydatepicker .icon-left:before { + content: "\e605"; +} + +.mydatepicker .icon-right:before { + content: "\e607"; +} \ No newline at end of file diff --git a/src/app/mydatepicker.ts b/src/app/mydatepicker.ts new file mode 100644 index 00000000..5cea10a3 --- /dev/null +++ b/src/app/mydatepicker.ts @@ -0,0 +1,254 @@ +import {Component, View, Input, Output, EventEmitter, NgIf, NgFor, NgClass, NgStyle} from 'angular2/angular2'; + +@Component({ + selector: 'my-date-picker' +}) +@View({ + templateUrl: 'app/template/mydatepicker.html', + styleUrls: ['app/css/mydatepicker.css'], + directives: [NgIf, NgFor, NgClass, NgStyle] +}) + +export class MyDatePicker { + @Input() options:any; + @Output() dateChanged = new EventEmitter(); + + private showSelector:boolean = false; + private visibleMonth:Month = {monthTxt: '', monthNbr: 0, year: 0}; + private selectedDate:Date = {year:0, month:0, day:0}; + private weekDays:Array = []; + private dates:Array = []; + private selectionDayTxt:string = ''; + private dayIdx:number = 0; + + private PREV_MONTH:number = 1; + private CURR_MONTH:number = 2; + private NEXT_MONTH:number = 3; + private dayLabels = {su: 'Sun', mo: 'Mon', tu: 'Tue', we: 'Wed', th: 'Thu', fr: 'Fri', sa: 'Sat'}; + private monthLabels = {1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun', 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec'}; + + private dateFormat:string = 'yyyy-mm-dd' + private firstDayOfWeek:string = 'mo'; + private sunHighlight:boolean = true; + private height:string = '34px'; + private width:string = '100%'; + + private today = new Date(); + + constructor() {} + + onInit() { + this.dateFormat = this.options.dateFormat !== undefined ? this.options.dateFormat : this.dateFormat; + this.firstDayOfWeek = this.options.firstDayOfWeek !== undefined ? this.options.firstDayOfWeek : this.firstDayOfWeek; + this.sunHighlight = this.options.sunHighlight !== undefined ? this.options.sunHighlight : this.sunHighlight; + this.height = this.options.height !== undefined ? this.options.height : this.height; + this.width = this.options.width !== undefined ? this.options.width : this.width; + + var days = ['su', 'mo', 'tu', 'we', 'th', 'fr', 'sa']; + this.dayIdx = days.indexOf(this.firstDayOfWeek); + if (this.dayIdx !== -1) { + var idx = this.dayIdx; + for (var i = 0; i < days.length; i++) { + this.weekDays.push(this.dayLabels[days[idx]]); + idx = days[idx] === 'sa' ? 0 : idx + 1; + } + } + } + + removeBtnClicked() { + this.selectionDayTxt = ''; + this.selectedDate = {}; + this.dateChanged.next({date: {}, formatted: this.selectionDayTxt}); + } + + openBtnClicked() { + this.showSelector = !this.showSelector; + if (this.showSelector) { + var y = this.today.getFullYear(); + var m = this.today.getMonth() + 1; + + // Set current month + this.visibleMonth = {monthTxt: this.monthLabels[m], monthNbr: m, year: y}; + + // Create current month + this.createMonth(m, y); + } + } + + prevMonth() { + var m = this.visibleMonth.monthNbr; + var y = this.visibleMonth.year; + if (m === 1) { + m = 12; + y--; + } + else { + m--; + } + this.visibleMonth = {monthTxt: this.monthText(m), monthNbr: m, year: y}; + this.createMonth(m, y); + } + + nextMonth() { + var m = this.visibleMonth.monthNbr; + var y = this.visibleMonth.year; + if (m === 12) { + m = 1; + y++; + } + else { + m++; + } + this.visibleMonth = {monthTxt: this.monthText(m), monthNbr: m, year: y}; + this.createMonth(m, y); + } + + prevYear() { + this.visibleMonth.year--; + this.createMonth(this.visibleMonth.monthNbr, this.visibleMonth.year); + } + + nextYear() { + this.visibleMonth.year++; + this.createMonth(this.visibleMonth.monthNbr, this.visibleMonth.year); + } + + todayClicked() { + // Today selected + var m = this.today.getMonth() + 1; + this.visibleMonth = {monthTxt: this.monthText(m), monthNbr: m, year: this.today.getFullYear()}; + this.createMonth(this.visibleMonth.monthNbr, this.visibleMonth.year); + } + + cellClicked(cell) { + // Cell clicked in the selector + if (cell.cmo === this.PREV_MONTH) { + // Previous month of day + this.prevMonth(); + } + else if (cell.cmo === this.CURR_MONTH) { + // Current month of day + this.selectedDate = {day: cell.day, month: cell.month, year: cell.year}; + this.selectionDayTxt = this.formatDate(cell); + this.showSelector = false; + this.dateChanged.next({date: this.selectedDate, formatted: this.selectionDayTxt}); + } + else if (cell.cmo === this.NEXT_MONTH) { + // Next month of day + this.nextMonth(); + } + } + + preZero(val) { + // Prepend zero if smaller than 10 + return val < 10 ? '0' + val : val; + } + + formatDate(val) { + return this.dateFormat.replace('yyyy', val.year) + .replace('mm', this.preZero(val.month)) + .replace('dd', this.preZero(val.day)); + } + + monthText(m) { + // Returns mont as a text + return this.monthLabels[m]; + } + + monthStartIdx(y, m) { + // Month start index + var d = new Date(); + d.setDate(1); + d.setMonth(m - 1); + d.setFullYear(y); + var idx = d.getDay() + this.sundayIdx(); + return idx >= 7 ? idx - 7 : idx; + } + + daysInMonth(m, y) { + // Return number of days of current month + return new Date(y, m, 0).getDate(); + } + + daysInPrevMonth(m, y) { + // Return number of days of the previous month + if (m === 1) { + m = 12; + y--; + } + else { + m--; + } + return this.daysInMonth(m, y); + } + + isCurrDay(d, m, y, cmo) { + // Check is a given date the current date + return d === this.today.getDate() && m === this.today.getMonth() + 1 && y === this.today.getFullYear() && cmo === 2; + } + + sundayIdx() { + // Index of Sunday day + return this.dayIdx > 0 ? 7 - this.dayIdx : 0; + } + + createMonth(m, y) { + this.dates.length = 0; + var monthStart = this.monthStartIdx(y, m); + var dInThisM = this.daysInMonth(m, y); + var dInPrevM = this.daysInPrevMonth(m, y); + var sunIdx = this.sundayIdx(); + + var dayNbr = 1; + var cmo = this.PREV_MONTH; + for (var i = 1; i < 7; i++) { + var week = []; + if (i === 1) { + // First week + var pm = dInPrevM - monthStart + 1; + // Previous month + for (var j = pm; j <= dInPrevM; j++) { + week.push({ + day: j, month: m, year: y, cmo: cmo, currDay: this.isCurrDay(j, m, y, cmo), sun: week.length === sunIdx + }); + } + cmo = this.CURR_MONTH; + // Current month + var daysLeft = 7 - week.length; + for (var j = 0; j < daysLeft; j++) { + week.push({ + day: dayNbr, month: m, year: y, cmo: cmo, currDay: this.isCurrDay(dayNbr, m, y, cmo), sun: week.length === sunIdx + }); + dayNbr++; + } + } + else { + // Rest of the weeks + for (var j = 1; j < 8; j++) { + if (dayNbr > dInThisM) { + // Next month + dayNbr = 1; + cmo = this.NEXT_MONTH; + } + week.push({ + day: dayNbr, month: m, year: y, cmo: cmo, currDay: this.isCurrDay(dayNbr, m, y, cmo), sun: week.length === sunIdx + }); + dayNbr++; + } + } + this.dates.push(week); + } + } +} + +interface Date { + year: number; + month: number; + day: number; +} + +interface Month { + monthTxt: string; + monthNbr: number; + year: number; +} \ No newline at end of file diff --git a/src/app/sampleapp.ts b/src/app/sampleapp.ts new file mode 100644 index 00000000..d3eec2c2 --- /dev/null +++ b/src/app/sampleapp.ts @@ -0,0 +1,28 @@ +import {bootstrap, Component} from 'angular2/angular2'; +import {MyDatePicker} from './mydatepicker'; + +@Component({ + selector: 'sample-date-picker', + template: ``, + directives: [MyDatePicker] +}) + +class SampleDatePicker { + private myDatePickerOptions = { + dateFormat: 'dd.mm.yyyy', + firstDayOfWeek: 'mo', + sunHighlight: true, + height: '34px', + width: '260px' + }; + constructor() {} + + onInit() { + console.log('onInit(): SampleDatePicker') + } + + onDateChanged(event) { + console.log('onDateChanged(): ', event.date, ' - formatted: ', event.formatted); + } +} +bootstrap(SampleDatePicker); \ No newline at end of file diff --git a/src/app/template/mydatepicker.html b/src/app/template/mydatepicker.html new file mode 100644 index 00000000..d83babec --- /dev/null +++ b/src/app/template/mydatepicker.html @@ -0,0 +1,42 @@ +
+
+ {{selectionDayTxt}} + + + + +
+
+ + + + + + +
+
+
+
{{visibleMonth.monthTxt}}
+
+
+
+ + +
+
+
{{visibleMonth.year}}
+
+
+
+ + + + + + + +
{{d}}
+ {{d.day}} +
+
+
diff --git a/src/index.html b/src/index.html new file mode 100644 index 00000000..264985a9 --- /dev/null +++ b/src/index.html @@ -0,0 +1,44 @@ + + + Angular 2 Date picker + + + + + + + + + + +

AngularJS 2 date picker

+ + loading... + + diff --git a/src/tsconfig.json b/src/tsconfig.json new file mode 100644 index 00000000..a8e3d762 --- /dev/null +++ b/src/tsconfig.json @@ -0,0 +1,11 @@ + { + "compilerOptions": { + "target": "ES5", + "module": "commonjs", + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "removeComments": false, + "noImplicitAny": false + } + }