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 |