An implementation of http://www.google.com/design/spec/components/pickers.html in AngularJS and some bootstrap just for helper classes (just the button classes).
Work in progress.
| div(ng-app="testMod" ng-controller="testCtrl") | |
| p Basic Mode: {{date | date:'yyyy/MM/dd HH:mm a'}} | |
| .modal-dialog | |
| .modal-content | |
| time-date-picker(ng-model="date") | |
| p Start on Time: {{date | date:'yyyy/MM/dd HH:mm a'}} | |
| .modal-dialog | |
| .modal-content | |
| time-date-picker(ng-model="date" default-mode="time") | |
| p Time Only: {{date | date:'yyyy/MM/dd HH:mm a'}} | |
| .modal-dialog | |
| .modal-content | |
| time-date-picker(ng-model="date" display-mode="time") | |
| p Date Only: {{date | date:'yyyy/MM/dd HH:mm a'}} | |
| .modal-dialog | |
| .modal-content | |
| time-date-picker(ng-model="date" display-mode="date") | |
| script(type="text/ng-template" id="time-date.tpl") | |
| .time-date(ng-class="modeClass()") | |
| .display(ng-click="modeSwitch()") | |
| .title {{display.title()}} | |
| .content | |
| .super-title {{display.super()}} | |
| .main-title(ng-bind-html="display.main()") | |
| .sub-title {{display.sub()}} | |
| .control | |
| .slider | |
| .date-control | |
| .title | |
| span.month-part {{date | date:'MMMM'}} | |
| select(ng-model="calendar._month" ng-change="calendar.monthChange()" ng-options="calendar._months.indexOf(month) as month for month in calendar._months") | |
| input.year-part(ng-model="calendar._year" ng-change="calendar.monthChange()" type="number") | |
| .headers | |
| each day in ['S','M','T','W','T','F','S'] | |
| .day-cell= day | |
| .days | |
| each day in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31] | |
| if day === 1 | |
| .day-cell(ng-style="{'margin-left': calendar.offsetMargin()}" ng-class= "calendar.class("+day+")" ng-show="calendar.isVisible("+day+")" ng-click="calendar.select("+day+")")= day | |
| else | |
| .day-cell(ng-class= "calendar.class("+day+")" ng-show="calendar.isVisible("+day+")" ng-click="calendar.select("+day+")")= day | |
| .button.switch-control(ng-click="modeSwitch()") | |
| i.fa.fa-clock-o | |
| i.fa.fa-calendar | |
| .time-control | |
| .clock | |
| .clock-face | |
| .center | |
| each num in [1,2,3,4,5,6,7,8,9,10,11,12] | |
| .hand(ng-click= "clock.setHour(" + num + ")" ng-class= "{'selected': clock._hour() == " + num + "}")= num | |
| .buttons | |
| button.btn.btn-link.pull-left(ng-click="clock.setAM(true)" ng-class="{'active': clock.isAM()}" type="button") AM | |
| input(value="30" type="number" min="0" max="59" ng-model="clock._minutes") | |
| button.btn.btn-link.pull-right(ng-click="clock.setAM(false)" ng-class="{'active': !clock.isAM()}" type="button") PM | |
| .buttons | |
| button.btn.btn-link(ng-click="setNow()" type="button") Now | |
| button.btn.btn-link(ng-click="cancel()" type="button") Cancel | |
| button.btn.btn-link(ng-click="save()" type="button") OK |
An implementation of http://www.google.com/design/spec/components/pickers.html in AngularJS and some bootstrap just for helper classes (just the button classes).
Work in progress.
| angular.module('testMod', []) | |
| .controller 'testCtrl', ($scope) -> | |
| $scope.date = new Date() | |
| .directive 'timeDatePicker', ['$filter', '$sce', ($filter, $sce) -> | |
| restrict: 'AE' | |
| replace: true | |
| scope: | |
| _modelValue: '=ngModel' | |
| require: 'ngModel' | |
| templateUrl: 'time-date.tpl' | |
| link: (scope, element, attrs, ngModel) -> | |
| scope._mode = attrs.defaultMode ? 'date' | |
| scope._displayMode = attrs.displayMode | |
| ngModel.$render = -> | |
| scope.date = if ngModel.$modelValue? then new Date ngModel.$modelValue else new Date() | |
| scope.calendar._year = scope.date.getFullYear() | |
| scope.calendar._month = scope.date.getMonth() | |
| scope.clock._minutes = scope.date.getMinutes() | |
| scope.save = -> scope._modelValue = scope.date | |
| scope.cancel = -> ngModel.$render() | |
| controller: ['$scope', (scope) -> | |
| scope.date = new Date() | |
| scope.display = | |
| title: -> | |
| if scope._mode is 'date' then $filter('date') scope.date, 'EEEE h:mm a' | |
| else $filter('date') scope.date, 'MMMM d yyyy' | |
| super: -> | |
| if scope._mode is 'date' then $filter('date') scope.date, 'MMM' | |
| else '' | |
| main: -> $sce.trustAsHtml( | |
| if scope._mode is 'date' then $filter('date') scope.date, 'd' | |
| else "#{$filter('date') scope.date, 'h:mm'}<small>#{$filter('date') scope.date, 'a'}</small>" | |
| ) | |
| sub: -> | |
| if scope._mode is 'date' then $filter('date') scope.date, 'yyyy' | |
| else $filter('date') scope.date, 'HH:mm' | |
| scope.calendar = | |
| _month: 0 | |
| _year: 0 | |
| _months: ["January","February","March","April","May","June","July","August","September","October","November","December"] | |
| offsetMargin: -> "#{new Date(@_year, @_month).getDay() * 3.6}rem" | |
| isVisible: (d) -> new Date(@_year, @_month, d).getMonth() is @_month | |
| class: (d) -> | |
| if new Date(@_year, @_month, d).getTime() is new Date(scope.date.getTime()).setHours(0,0,0,0) then "selected" | |
| else if new Date(@_year, @_month, d).getTime() is new Date().setHours(0,0,0,0) then "today" | |
| else "" | |
| select: (d) -> scope.date.setFullYear @_year, @_month, d | |
| monthChange: -> | |
| if not @_year? or isNaN @_year then @_year = new Date().getFullYear() | |
| scope.date.setFullYear @_year, @_month | |
| if scope.date.getMonth() isnt @_month then scope.date.setDate 0 | |
| scope.clock = | |
| _minutes: 0 | |
| _hour: -> | |
| _h = scope.date.getHours() | |
| _h = _h % 12 | |
| return if _h is 0 then 12 else _h | |
| setHour: (h) -> | |
| if h is 12 and @isAM() then h = 0 | |
| h += if not @isAM() then 12 else 0 | |
| if h is 24 then h = 12 | |
| scope.date.setHours h | |
| setAM: (b) -> if b and not @isAM() then scope.date.setHours(scope.date.getHours() - 12) else if not b and @isAM() then scope.date.setHours(scope.date.getHours() + 12) | |
| isAM: -> scope.date.getHours() < 12 | |
| scope.$watch 'clock._minutes', (val) -> | |
| if val? and val isnt scope.date.getMinutes() then scope.date.setMinutes val | |
| scope.setNow = -> scope.date = new Date() | |
| scope._mode = 'date' | |
| scope.modeClass = -> | |
| if scope._displayMode? then scope._mode = scope._displayMode | |
| if scope._displayMode is 'time' then 'time-only' | |
| else if scope._displayMode is 'date' then 'date-only' | |
| else if scope._mode is 'date' then 'date-mode' | |
| else 'time-mode' | |
| scope.modeSwitch = -> scope._mode = scope._displayMode ? if scope._mode is 'date' then 'time' else 'date' | |
| ]] |
| title-background = rgb(0, 121, 107) | |
| title-colour = rgb(225,225,225) | |
| content-background = rgb(0, 150, 136) | |
| content-main-colour = rgb(225,225,225) | |
| content-secondary-colour = rgb(138,207,200) | |
| background-colour = rgb(255, 255, 255) | |
| text-colour = rgb(32,32,32) | |
| unselectable-text-colour = rgb(115,115,115) | |
| today-text-colour = rgb(0, 150, 136) | |
| selected-text-colour = background-colour | |
| selected-background-colour = rgb(0, 150, 136) | |
| hover-text-colour = darken(selected-text-colour, 10%) | |
| hover-background-colour = lighten(selected-background-colour, 10%) | |
| button-text-colour = rgb(0, 150, 136) | |
| clock-background-colour = rgb(236,239,241) | |
| content-height = 27.3rem | |
| clock-radius = 75 | |
| .time-date | |
| font-size: 16px | |
| overflow: hidden | |
| border: none | |
| max-width: 600px | |
| & > .display | |
| position: relative | |
| width: 47% | |
| height: 100% | |
| cursor: pointer | |
| & > .title | |
| background-color: title-background | |
| color: title-colour | |
| line-height: 2em | |
| & > .content | |
| position: absolute | |
| left: 0px | |
| right: 0px | |
| bottom: 0px | |
| top: 3.2rem | |
| padding: 3rem | |
| font-size: 2rem | |
| background-color: content-background | |
| color: content-main-colour | |
| & > .super-title | |
| text-transform: uppercase | |
| & > .main-title | |
| font-size: 8rem | |
| & > small | |
| font-size: 1.5rem | |
| margin-left: 0.5rem | |
| & > .sub-title | |
| color: content-secondary-colour | |
| & > .control | |
| width: 53% | |
| overflow-x: hidden | |
| position: relative | |
| & > .slider | |
| position: absolute !important | |
| width: 200% | |
| transition: left 0.6s ease-in-out | |
| & > .date-control, & > .time-control, & > .switch-control | |
| float: left | |
| height: content-height | |
| & > .date-control, & > .time-control | |
| width: 45% | |
| & > .switch-control | |
| width: 10% | |
| cursor: pointer | |
| & > i.fa | |
| margin-top: 12.65rem | |
| &:first-child | |
| margin-right 1rem | |
| &:last-child | |
| margin-left 1rem | |
| & > .date-control | |
| & > .title | |
| font-weight: 500 | |
| line-height: 2em | |
| & > span | |
| cursor: pointer | |
| position: relative | |
| &:hover | |
| text-decoration: underline | |
| & > select | |
| position: absolute | |
| top: 0px | |
| bottom: 0px | |
| left: 0px | |
| right: 0px | |
| opacity: 0 | |
| & > input | |
| border: none | |
| width: 5.6rem | |
| & > .headers | |
| margin-top: -1rem | |
| font-weight: 500 | |
| font-size: 1.3rem | |
| padding-left: 2rem | |
| color: unselectable-text-colour | |
| & > .day-cell | |
| cursor: default | |
| & .day-cell | |
| float: left | |
| width: 3.6rem | |
| height: 3.6rem | |
| padding-top: 1rem | |
| cursor: pointer | |
| border-radius: 4rem | |
| & > .days | |
| font-size: 1.3rem | |
| padding-left: 2rem | |
| width: (3.6 * 7.7)rem | |
| & .day-cell | |
| &.today | |
| color: today-text-colour | |
| &.selected | |
| color: selected-text-colour | |
| background-color: selected-background-colour | |
| &:hover, &:active | |
| color: hover-text-colour | |
| background-color: hover-background-colour | |
| & > .time-control | |
| position: relative | |
| & > .buttons | |
| position: absolute | |
| bottom: 0px | |
| left: 8px | |
| right: 8px | |
| & > input | |
| float: left; | |
| bottom: 50% | |
| margin-bottom: -1.4rem | |
| margin-left: -3rem | |
| border: none | |
| border-bottom: 1px solid black | |
| text-align: center | |
| position: absolute | |
| left: 50% | |
| & > button | |
| border-radius: 100% | |
| width: 5rem | |
| height: 5rem | |
| & > .clock | |
| position: relative | |
| width: 100% | |
| padding-left: 50% | |
| padding-top: 3rem | |
| & > .clock-face | |
| position: relative | |
| margin-left: -(clock-radius)px | |
| margin-top: 1.5rem | |
| &:before | |
| content: " " | |
| background-color: clock-background-colour | |
| position: absolute | |
| top: -20px | |
| left: -20px | |
| height: ((2 * clock-radius) + 38)px | |
| width: ((2 * clock-radius) + 38)px | |
| border-radius: (((2 * clock-radius) + 38) / 2)px | |
| & > .center | |
| position: absolute | |
| width: 4px | |
| height: 4px | |
| background-color: text-colour | |
| border-radius: 2px | |
| z-index: 3 | |
| left: (clock-radius - 1)px | |
| top: (clock-radius - 2)px | |
| & > .hand | |
| position: absolute | |
| height: 4rem | |
| width: 4rem | |
| margin-left: -2rem | |
| margin-top: -2rem | |
| padding: 1rem | |
| text-align: center | |
| border-radius: 8rem | |
| cursor: pointer | |
| color: today-text-colour | |
| z-index: 2 | |
| &:hover, &:active, &:hover:before, &:active:before | |
| color: hover-text-colour | |
| background-color: hover-background-colour | |
| &.selected, &.selected:before | |
| color: selected-text-colour | |
| background-color: selected-background-colour | |
| for num in 1 2 3 4 5 6 7 8 9 10 11 12 | |
| &:nth-child({num+1}) | |
| left: (clock-radius + clock-radius * sin(PI * 2 * (num/12)))px | |
| top: (clock-radius - clock-radius * cos(PI * 2 * (num/12)))px | |
| &:before | |
| content: " " | |
| position: absolute | |
| left: 2rem | |
| top: 2rem | |
| height: (clock-radius)px | |
| width: 2px | |
| z-index: -1 | |
| transform-origin: center top | |
| transform: rotate((num / 12 * 360)deg) | |
| &.date-mode > .control > .slider | |
| left: 0% | |
| &.time-mode | |
| & > .display > .content | |
| padding-top: 5.4rem | |
| & > .control > .slider | |
| left: -100% | |
| &.date-only > .control > .slider | |
| & > .switch-control, & > .time-control | |
| display: none | |
| & > .date-control | |
| width: 50% | |
| padding-left: 1.4rem | |
| &.time-only | |
| & > .display > .content | |
| top: 0px | |
| padding-top: 8.2rem | |
| & > .control > .slider | |
| & > .switch-control, & > .date-control | |
| display: none | |
| & > .time-control | |
| width: 50% | |
| & > .display, & > .control | |
| height: content-height | |
| float: left | |
| text-align: center | |
| & > .buttons | |
| clear: both | |
| padding: 1rem | |
| text-align: right | |
| & button, & .button | |
| color: button-text-colour !important | |
| font-weight: bold | |
| &:hover, &:active | |
| color: hover-text-colour !important | |
| background-color: hover-background-colour | |
| &.active | |
| color: selected-text-colour !important | |
| background-color: selected-background-colour |