Skip to content

Instantly share code, notes, and snippets.

@matt-daniel-brown
Created November 28, 2018 01:10
Show Gist options
  • Save matt-daniel-brown/0b48839b3324222d967531099b122a14 to your computer and use it in GitHub Desktop.
Save matt-daniel-brown/0b48839b3324222d967531099b122a14 to your computer and use it in GitHub Desktop.
Material Design Date/Time picker Mark 2
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 Full Mode: {{date | date:'yyyy/MM/dd HH:mm a'}}
.modal-dialog
.modal-content
time-date-picker(ng-model="date" display-mode="full")
p Start on Time, With 24 Hour: {{date | date:'yyyy/MM/dd HH:mm a'}}
.modal-dialog
.modal-content
time-date-picker(ng-model="date" default-mode="time" display-twentyfour="true")
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
.full-title {{display.fullTitle()}}
.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(ng-if="_hours24")
.time-inputs
input(type="number" min="0" max="23" ng-model="clock._hours")
a.button.hours.up(ng-click="clock._incHours(1)")
i.fa.fa-caret-up
a.button.hours.down(ng-click="clock._incHours(-1)")
i.fa.fa-caret-down
input(type="number" min="0" max="59" ng-model="clock._minutes")
a.button.minutes.up(ng-click="clock._incMinutes(1)")
i.fa.fa-caret-up
a.button.minutes.down(ng-click="clock._incMinutes(-1)")
i.fa.fa-caret-down
.time-control(ng-if="!_hours24")
.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
span
label minutes
input(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
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
scope._hours24 = attrs.displayTwentyfour? and attrs.displayTwentyfour
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.clock._hours = scope.date.getHours()
scope.save = -> scope._modelValue = scope.date
scope.cancel = -> ngModel.$render()
controller: ['$scope', (scope) ->
scope.date = new Date()
scope.display =
fullTitle: -> $filter('date') scope.date, 'EEEE d MMMM yyyy, h:mm a'
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
_hours: 0
_incHours: (inc) -> @_hours = Math.max 0, Math.min 23, @_hours + inc
_incMinutes: (inc) -> @_minutes = Math.max 0, Math.min 59, @_minutes + inc
_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.$watch 'clock._hours', (val) ->
if val? and val isnt scope.date.getHours() then scope.date.setHours val
scope.setNow = -> scope.date = new Date()
scope._mode = 'date'
scope.modeClass = ->
if scope._displayMode? then scope._mode = scope._displayMode
if scope._displayMode is 'full' then 'full-mode'
else 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'
]]
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
@import "nib"
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: 3.2rem
& > .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
& > .full-title
display: none
background-color: title-background
color: title-colour
line-height: 3.2rem
& > .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: 3.2rem
& > 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
& > .time-inputs
bottom: 50%
margin-bottom: -5.6rem
text-align: center
position: absolute
left: 0
right: 0
& > a
font-size: 4rem
line-height: 1rem
width: 11rem
margin: 0 1.1rem
position: absolute
&.hours
left: 1rem
right: 50%
margin-right: 4rem
&.minutes
margin-left: 1.2rem
right: 1rem
left: 50%
&.up
top: -3rem
&.down
bottom: -4rem
& > input
border: none
border-bottom: 1px solid black
text-align: right
font-size: 8rem
line-height: 4rem
width: 11rem
margin: 0 1rem
& > .buttons
position: absolute
bottom: 0px
left: 8px
right: 8px
height: 5rem
& > span
float: left;
& > label
position: absolute
left: 50%
margin-left: -3rem
font-weight: normal
font-size: 1.3rem
& > input
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
margin-top: -1rem
& > .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
text-decoration: underline
cursor: pointer
&:hover, &:active
color: hover-text-colour !important
background-color: hover-background-colour
&.active
color: selected-text-colour !important
background-color: selected-background-colour
&.full-mode
& > .display
display: none
& > .control
width: 100%
height: 30.5rem
& > .full-title
display: block
& > .slider
float: none
width: 100%
& > .time-control, & > .date-control
width: 50%
& > .switch-control
display: none
& > .buttons
background-color: selected-background-colour
& > button
/* Invert colours for bottom bar */
color: selected-text-colour !important
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment