complete datepicker positioning and text field input

This commit is contained in:
t1m0n 2015-11-14 20:24:36 +03:00
parent cce9a64017
commit 1dac51b7cf
7 changed files with 242 additions and 61 deletions

View File

@ -23,9 +23,27 @@
width: 224px; width: 224px;
position: absolute; position: absolute;
left: -100000px; left: -100000px;
opacity: 0; } opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease, left 0s 0.3s; }
.datepicker.-from-top- {
transform: translateY(-8px); }
.datepicker.-from-right- {
transform: translateX(8px); }
.datepicker.-from-bottom- {
transform: translateY(8px); }
.datepicker.-from-left- {
transform: translateX(-8px); }
.datepicker.active { .datepicker.active {
opacity: 1; } opacity: 1;
transform: translate(0);
transition: opacity 0.3s ease, transform 0.3s ease, left 0s 0s; }
.datepicker-inline .datepicker {
position: static;
left: auto;
right: auto;
opacity: 1;
transform: none; }
.datepicker--content { .datepicker--content {
padding: 2px; } padding: 2px; }

122
dist/js/datepicker.js vendored
View File

@ -9,7 +9,7 @@ var Datepicker;
'<div class="datepicker--content"></div>' + '<div class="datepicker--content"></div>' +
'</div>', '</div>',
defaults = { defaults = {
//TODO сделать работу с инпутом //TODO сделать кастомизируемые заголовки
inline: false, inline: false,
language: 'ru', language: 'ru',
startDate: new Date(), startDate: new Date(),
@ -17,7 +17,9 @@ var Datepicker;
weekends: [6, 0], weekends: [6, 0],
dateFormat: '', dateFormat: '',
toggleSelected: true, toggleSelected: true,
position: 'bottom left', position: 'bottom left',
offset: 8,
view: 'days', view: 'days',
minView: 'days', minView: 'days',
@ -41,6 +43,7 @@ var Datepicker;
clearButton: false, clearButton: false,
showEvent: 'focus', showEvent: 'focus',
autoClose: false,
// navigation // navigation
prevHtml: '&laquo;', prevHtml: '&laquo;',
@ -98,6 +101,8 @@ var Datepicker;
if (this.elIsInput) { if (this.elIsInput) {
if (!this.opts.inline) { if (!this.opts.inline) {
// Set extra classes for proper transitions
this._setPositionClasses(this.opts.position);
this._bindEvents() this._bindEvents()
} }
} }
@ -112,9 +117,9 @@ var Datepicker;
_bindEvents : function () { _bindEvents : function () {
this.$el.on(this.opts.showEvent, this._onShowEvent.bind(this)); this.$el.on(this.opts.showEvent, this._onShowEvent.bind(this));
this.$el.on('blur', this._onBlur.bind(this));
this.$datepicker.on('mousedown', this._onMouseDownDatepicker.bind(this)); this.$datepicker.on('mousedown', this._onMouseDownDatepicker.bind(this));
this.$datepicker.on('mouseup', this._onMouseUpDatepicker.bind(this)); this.$datepicker.on('mouseup', this._onMouseUpDatepicker.bind(this));
$body.on('mousedown.datepicker', this._onMouseDownBody.bind(this));
}, },
isWeekend: function (day) { isWeekend: function (day) {
@ -148,24 +153,24 @@ var Datepicker;
}, },
_buildBaseHtml: function () { _buildBaseHtml: function () {
var $appendTarget = this.$el; var $appendTarget,
$inline = $('<div class="datepicker-inline">');
if(this.el.nodeName == 'INPUT') { if(this.el.nodeName == 'INPUT') {
if (!this.opts.inline) { if (!this.opts.inline) {
$appendTarget = $datepickersContainer; $appendTarget = $datepickersContainer;
} else { } else {
$appendTarget = $inline.insertAfter(this.$el)
} }
} else {
$appendTarget = $inline.insertAfter(this.$el)
} }
this.$datepicker = $(baseTemplate).appendTo($appendTarget); this.$datepicker = $(baseTemplate).appendTo($appendTarget);
this.$content = $('.datepicker--content', this.$datepicker); this.$content = $('.datepicker--content', this.$datepicker);
this.$nav = $('.datepicker--nav', this.$datepicker); this.$nav = $('.datepicker--nav', this.$datepicker);
}, },
_defineDOM: function () {
},
_triggerOnChange: function () { _triggerOnChange: function () {
if (!this.selectedDates.length) { if (!this.selectedDates.length) {
return this.opts.onChange('', '', this); return this.opts.onChange('', '', this);
@ -173,16 +178,16 @@ var Datepicker;
var selectedDates = this.selectedDates, var selectedDates = this.selectedDates,
parsedSelected = Datepicker.getParsedDate(selectedDates[0]), parsedSelected = Datepicker.getParsedDate(selectedDates[0]),
formattedDates = this.formatDate(this.loc.dateFormat, selectedDates[0]), formattedDates,
_this = this, _this = this,
dates = new Date(parsedSelected.year, parsedSelected.month, parsedSelected.date); dates = new Date(parsedSelected.year, parsedSelected.month, parsedSelected.date);
if (this.opts.multipleDates) {
formattedDates = selectedDates.map(function (date) { formattedDates = selectedDates.map(function (date) {
return _this.formatDate(_this.opts.dateFormat, date) return _this.formatDate(_this.loc.dateFormat, date)
}).join(this.opts.multipleDatesSeparator); }).join(this.opts.multipleDatesSeparator);
// Create new dates array, to separate it from original selectedDates // Create new dates array, to separate it from original selectedDates
if (this.opts.multipleDates) {
dates = selectedDates.map(function(date) { dates = selectedDates.map(function(date) {
var parsedDate = Datepicker.getParsedDate(date); var parsedDate = Datepicker.getParsedDate(date);
return new Date(parsedDate.year, parsedDate.month, parsedDate.date) return new Date(parsedDate.year, parsedDate.month, parsedDate.date)
@ -286,7 +291,13 @@ var Datepicker;
this.selectedDates = [date]; this.selectedDates = [date];
} }
this.views[this.currentView]._render() this._setInputValue();
if (this.opts.autoClose) {
this.hide();
} else {
this.views[this.currentView]._render()
}
}, },
removeDate: function (date) { removeDate: function (date) {
@ -297,6 +308,7 @@ var Datepicker;
if (Datepicker.isSame(curDate, date)) { if (Datepicker.isSame(curDate, date)) {
selected.splice(i, 1); selected.splice(i, 1);
_this.views[_this.currentView]._render(); _this.views[_this.currentView]._render();
_this._setInputValue();
return true return true
} }
}) })
@ -321,6 +333,18 @@ var Datepicker;
}) })
}, },
_setInputValue: function () {
var _this = this,
format = this.loc.dateFormat,
value = this.selectedDates.map(function (date) {
return _this.formatDate(format, date)
});
value.join(this.opts.multipleDatesSeparator);
this.$el.val(value)
},
/** /**
* Check if date is between minDate and maxDate * Check if date is between minDate and maxDate
* @param date {object} - date object * @param date {object} - date object
@ -343,51 +367,93 @@ var Datepicker;
return type ? types[type] : types.day return type ? types[type] : types.day
}, },
_getDimensions: function () { _getDimensions: function ($el) {
var offset = this.$el.offset(); var offset = $el.offset();
return { return {
width: this.$el.outerWidth(), width: $el.outerWidth(),
height: this.$el.outerHeight(), height: $el.outerHeight(),
left: offset.left, left: offset.left,
top: offset.top top: offset.top
} }
}, },
_setPositionClasses: function (pos) {
pos = pos.split(' ');
var main = pos[0],
sec = pos[1];
this.$datepicker.addClass('-' + main + '-' + sec + '- -from-' + main + '-');
},
setPosition: function (position) { setPosition: function (position) {
var dims = this._getDimensions(), var dims = this._getDimensions(this.$el),
selfDims = this._getDimensions(this.$datepicker),
pos = position.split(' '), pos = position.split(' '),
top, left, top, left,
offset = 6, offset = this.opts.offset,
main = pos[0], main = pos[0],
secondary = pos[1]; secondary = pos[1];
switch (main) { switch (main) {
case 'top':
top = dims.top - selfDims.height - offset;
break;
case 'right':
left = dims.left + dims.width + offset;
break;
case 'bottom': case 'bottom':
top = dims.top + dims.height + offset; top = dims.top + dims.height + offset;
break; break;
case 'left':
left = dims.left - selfDims.width - offset;
break;
} }
switch(secondary) { switch(secondary) {
case 'top':
top = dims.top;
break;
case 'right':
left = dims.left + dims.width - selfDims.width;
break;
case 'bottom':
top = dims.top + dims.height - selfDims.height;
break;
case 'left': case 'left':
left = dims.left left = dims.left;
break;
case 'center':
if (/left|right/.test(main)) {
top = dims.top + dims.height/2 - selfDims.height/2;
} else {
left = dims.left + dims.width/2 - selfDims.width/2;
}
} }
this.$datepicker.css({ this.$datepicker
left: left, .css({
top: top left: left,
}) top: top
})
}, },
show: function () { show: function () {
this.$datepicker.addClass('active');
this.setPosition(this.opts.position); this.setPosition(this.opts.position);
this.$datepicker.addClass('active');
this.visible = true; this.visible = true;
}, },
hide: function () { hide: function () {
this.$datepicker.removeClass('active'); this.$datepicker
.removeClass('active')
.css({
left: '-100000px'
});
this.inFocus = false;
this.visible = false; this.visible = false;
this.$el.blur();
}, },
_onShowEvent: function () { _onShowEvent: function () {
@ -396,8 +462,8 @@ var Datepicker;
} }
}, },
_onMouseDownBody: function (e) { _onBlur: function () {
if (!this.inFocus) { if (!this.inFocus && this.visible) {
this.hide(); this.hide();
} }
}, },

View File

@ -2,6 +2,7 @@ var gulp = require('gulp'),
watch = require('gulp-watch'), watch = require('gulp-watch'),
rename = require('gulp-rename'), rename = require('gulp-rename'),
sass = require('gulp-sass'), sass = require('gulp-sass'),
uglify = require('gulp-uglify'),
livereload = require('gulp-livereload'), livereload = require('gulp-livereload'),
concat = require('gulp-concat'); concat = require('gulp-concat');

View File

@ -10,15 +10,13 @@
<body> <body>
<div class="contents"> <div class="contents">
<article> <article>
<input class="calendar" type="text"/> <div class="calendar"></div>
</article> </article>
</div> </div>
<script type="text/javascript" src="dist/js/datepicker.js"></script> <script type="text/javascript" src="dist/js/datepicker.js"></script>
<script type="text/javascript" src="dist/js/i18n/datepicker.en.js"></script> <script type="text/javascript" src="dist/js/i18n/datepicker.en.js"></script>
<script type="text/javascript"> <script type="text/javascript">
$('.calendar').datepicker({ $('.calendar').datepicker({
todayButton: true,
clearButton: true,
onChange: function (dateString, date, inst) { onChange: function (dateString, date, inst) {
console.log(dateString); console.log(dateString);
} }

View File

@ -9,7 +9,7 @@ var Datepicker;
'<div class="datepicker--content"></div>' + '<div class="datepicker--content"></div>' +
'</div>', '</div>',
defaults = { defaults = {
//TODO сделать работу с инпутом //TODO сделать кастомизируемые заголовки
inline: false, inline: false,
language: 'ru', language: 'ru',
startDate: new Date(), startDate: new Date(),
@ -17,7 +17,9 @@ var Datepicker;
weekends: [6, 0], weekends: [6, 0],
dateFormat: '', dateFormat: '',
toggleSelected: true, toggleSelected: true,
position: 'bottom left', position: 'bottom left',
offset: 8,
view: 'days', view: 'days',
minView: 'days', minView: 'days',
@ -41,6 +43,7 @@ var Datepicker;
clearButton: false, clearButton: false,
showEvent: 'focus', showEvent: 'focus',
autoClose: false,
// navigation // navigation
prevHtml: '&laquo;', prevHtml: '&laquo;',
@ -98,6 +101,8 @@ var Datepicker;
if (this.elIsInput) { if (this.elIsInput) {
if (!this.opts.inline) { if (!this.opts.inline) {
// Set extra classes for proper transitions
this._setPositionClasses(this.opts.position);
this._bindEvents() this._bindEvents()
} }
} }
@ -112,9 +117,9 @@ var Datepicker;
_bindEvents : function () { _bindEvents : function () {
this.$el.on(this.opts.showEvent, this._onShowEvent.bind(this)); this.$el.on(this.opts.showEvent, this._onShowEvent.bind(this));
this.$el.on('blur', this._onBlur.bind(this));
this.$datepicker.on('mousedown', this._onMouseDownDatepicker.bind(this)); this.$datepicker.on('mousedown', this._onMouseDownDatepicker.bind(this));
this.$datepicker.on('mouseup', this._onMouseUpDatepicker.bind(this)); this.$datepicker.on('mouseup', this._onMouseUpDatepicker.bind(this));
$body.on('mousedown.datepicker', this._onMouseDownBody.bind(this));
}, },
isWeekend: function (day) { isWeekend: function (day) {
@ -148,24 +153,24 @@ var Datepicker;
}, },
_buildBaseHtml: function () { _buildBaseHtml: function () {
var $appendTarget = this.$el; var $appendTarget,
$inline = $('<div class="datepicker-inline">');
if(this.el.nodeName == 'INPUT') { if(this.el.nodeName == 'INPUT') {
if (!this.opts.inline) { if (!this.opts.inline) {
$appendTarget = $datepickersContainer; $appendTarget = $datepickersContainer;
} else { } else {
$appendTarget = $inline.insertAfter(this.$el)
} }
} else {
$appendTarget = $inline.insertAfter(this.$el)
} }
this.$datepicker = $(baseTemplate).appendTo($appendTarget); this.$datepicker = $(baseTemplate).appendTo($appendTarget);
this.$content = $('.datepicker--content', this.$datepicker); this.$content = $('.datepicker--content', this.$datepicker);
this.$nav = $('.datepicker--nav', this.$datepicker); this.$nav = $('.datepicker--nav', this.$datepicker);
}, },
_defineDOM: function () {
},
_triggerOnChange: function () { _triggerOnChange: function () {
if (!this.selectedDates.length) { if (!this.selectedDates.length) {
return this.opts.onChange('', '', this); return this.opts.onChange('', '', this);
@ -173,16 +178,16 @@ var Datepicker;
var selectedDates = this.selectedDates, var selectedDates = this.selectedDates,
parsedSelected = Datepicker.getParsedDate(selectedDates[0]), parsedSelected = Datepicker.getParsedDate(selectedDates[0]),
formattedDates = this.formatDate(this.loc.dateFormat, selectedDates[0]), formattedDates,
_this = this, _this = this,
dates = new Date(parsedSelected.year, parsedSelected.month, parsedSelected.date); dates = new Date(parsedSelected.year, parsedSelected.month, parsedSelected.date);
if (this.opts.multipleDates) {
formattedDates = selectedDates.map(function (date) { formattedDates = selectedDates.map(function (date) {
return _this.formatDate(_this.opts.dateFormat, date) return _this.formatDate(_this.loc.dateFormat, date)
}).join(this.opts.multipleDatesSeparator); }).join(this.opts.multipleDatesSeparator);
// Create new dates array, to separate it from original selectedDates // Create new dates array, to separate it from original selectedDates
if (this.opts.multipleDates) {
dates = selectedDates.map(function(date) { dates = selectedDates.map(function(date) {
var parsedDate = Datepicker.getParsedDate(date); var parsedDate = Datepicker.getParsedDate(date);
return new Date(parsedDate.year, parsedDate.month, parsedDate.date) return new Date(parsedDate.year, parsedDate.month, parsedDate.date)
@ -286,7 +291,13 @@ var Datepicker;
this.selectedDates = [date]; this.selectedDates = [date];
} }
this.views[this.currentView]._render() this._setInputValue();
if (this.opts.autoClose) {
this.hide();
} else {
this.views[this.currentView]._render()
}
}, },
removeDate: function (date) { removeDate: function (date) {
@ -297,6 +308,7 @@ var Datepicker;
if (Datepicker.isSame(curDate, date)) { if (Datepicker.isSame(curDate, date)) {
selected.splice(i, 1); selected.splice(i, 1);
_this.views[_this.currentView]._render(); _this.views[_this.currentView]._render();
_this._setInputValue();
return true return true
} }
}) })
@ -321,6 +333,18 @@ var Datepicker;
}) })
}, },
_setInputValue: function () {
var _this = this,
format = this.loc.dateFormat,
value = this.selectedDates.map(function (date) {
return _this.formatDate(format, date)
});
value.join(this.opts.multipleDatesSeparator);
this.$el.val(value)
},
/** /**
* Check if date is between minDate and maxDate * Check if date is between minDate and maxDate
* @param date {object} - date object * @param date {object} - date object
@ -343,51 +367,93 @@ var Datepicker;
return type ? types[type] : types.day return type ? types[type] : types.day
}, },
_getDimensions: function () { _getDimensions: function ($el) {
var offset = this.$el.offset(); var offset = $el.offset();
return { return {
width: this.$el.outerWidth(), width: $el.outerWidth(),
height: this.$el.outerHeight(), height: $el.outerHeight(),
left: offset.left, left: offset.left,
top: offset.top top: offset.top
} }
}, },
_setPositionClasses: function (pos) {
pos = pos.split(' ');
var main = pos[0],
sec = pos[1];
this.$datepicker.addClass('-' + main + '-' + sec + '- -from-' + main + '-');
},
setPosition: function (position) { setPosition: function (position) {
var dims = this._getDimensions(), var dims = this._getDimensions(this.$el),
selfDims = this._getDimensions(this.$datepicker),
pos = position.split(' '), pos = position.split(' '),
top, left, top, left,
offset = 6, offset = this.opts.offset,
main = pos[0], main = pos[0],
secondary = pos[1]; secondary = pos[1];
switch (main) { switch (main) {
case 'top':
top = dims.top - selfDims.height - offset;
break;
case 'right':
left = dims.left + dims.width + offset;
break;
case 'bottom': case 'bottom':
top = dims.top + dims.height + offset; top = dims.top + dims.height + offset;
break; break;
case 'left':
left = dims.left - selfDims.width - offset;
break;
} }
switch(secondary) { switch(secondary) {
case 'top':
top = dims.top;
break;
case 'right':
left = dims.left + dims.width - selfDims.width;
break;
case 'bottom':
top = dims.top + dims.height - selfDims.height;
break;
case 'left': case 'left':
left = dims.left left = dims.left;
break;
case 'center':
if (/left|right/.test(main)) {
top = dims.top + dims.height/2 - selfDims.height/2;
} else {
left = dims.left + dims.width/2 - selfDims.width/2;
}
} }
this.$datepicker.css({ this.$datepicker
left: left, .css({
top: top left: left,
}) top: top
})
}, },
show: function () { show: function () {
this.$datepicker.addClass('active');
this.setPosition(this.opts.position); this.setPosition(this.opts.position);
this.$datepicker.addClass('active');
this.visible = true; this.visible = true;
}, },
hide: function () { hide: function () {
this.$datepicker.removeClass('active'); this.$datepicker
.removeClass('active')
.css({
left: '-100000px'
});
this.inFocus = false;
this.visible = false; this.visible = false;
this.$el.blur();
}, },
_onShowEvent: function () { _onShowEvent: function () {
@ -396,8 +462,8 @@ var Datepicker;
} }
}, },
_onMouseDownBody: function (e) { _onBlur: function () {
if (!this.inFocus) { if (!this.inFocus && this.visible) {
this.hide(); this.hide();
} }
}, },

View File

@ -18,9 +18,36 @@
position: absolute; position: absolute;
left: -100000px; left: -100000px;
opacity: 0; opacity: 0;
transition: opacity $transitionSpeed $transitionEase, transform $transitionSpeed $transitionEase, left 0s $transitionSpeed;
&.-from-top- {
transform: translateY(-$transitionOffset);
}
&.-from-right- {
transform: translateX($transitionOffset);
}
&.-from-bottom- {
transform: translateY($transitionOffset);
}
&.-from-left- {
transform: translateX(-$transitionOffset);
}
&.active { &.active {
opacity: 1; opacity: 1;
transform: translate(0);
transition: opacity $transitionSpeed $transitionEase, transform $transitionSpeed $transitionEase, left 0s 0s;
}
}
.datepicker-inline {
.datepicker {
position: static;
left: auto;
right: auto;
opacity: 1;
transform: none;
} }
} }

View File

@ -19,6 +19,11 @@ $colorCellWeekend: '';
$bgButton: #f2f2f2; $bgButton: #f2f2f2;
$bgButtonHover: darken($bgButton, 3); $bgButtonHover: darken($bgButton, 3);
// Transitions
$transitionSpeed: .3s;
$transitionEase: ease;
$transitionOffset: 8px;
// Objects // Objects
%otherMonth { %otherMonth {
color: $colorAnotherMonth; color: $colorAnotherMonth;