Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 562b76c

Browse files
crisbetoThomasBurleson
authored andcommitted
feat(datepicker): is-open attribute, ability to hide icons, demo layout
* Adds is-open attribute that allows for the datepicker calendar to be triggered from the outside. * Adds the ability to disable the datepicker icons. * Changes the datepicker demo to a 2-column layout, making it more compact. Fixes #8481. Closes #8743
1 parent 361d541 commit 562b76c

File tree

5 files changed

+194
-90
lines changed

5 files changed

+194
-90
lines changed

src/components/datepicker/datePicker.scss

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ md-datepicker {
4747
// If the datepicker is inside of a md-input-container
4848
._md-datepicker-floating-label {
4949
> label:not(.md-no-float):not(._md-container-ignore) {
50-
$offset: $md-datepicker-triangle-button-width + $md-datepicker-button-gap + $icon-button-margin + $baseline-grid;
51-
@include rtl-prop(left, right, $offset);
52-
width: calc(100% - #{$offset + $md-datepicker-triangle-button-width});
50+
$offset: $md-datepicker-triangle-button-width * 2 + $md-datepicker-button-gap + $icon-button-margin + $baseline-grid;
51+
@include rtl(right, $icon-button-margin, auto);
52+
@include rtl(left, auto, $icon-button-margin);
53+
width: calc(100% - #{$offset});
5354
}
5455

5556
> md-datepicker {
@@ -80,7 +81,10 @@ md-datepicker {
8081

8182
display: inline-block;
8283
width: auto;
83-
@include rtl-prop(margin-left, margin-right, $md-datepicker-button-gap);
84+
85+
.md-icon-button + & {
86+
@include rtl-prop(margin-left, margin-right, $md-datepicker-button-gap);
87+
}
8488

8589
&.md-datepicker-focused {
8690
border-bottom-width: 2px;
@@ -209,13 +213,15 @@ md-datepicker[disabled] {
209213
overflow: hidden;
210214

211215
.md-datepicker-input-container {
212-
@include rtl-prop(margin-left, margin-right, -$md-datepicker-button-gap);
213-
214216
// The negative bottom margin prevents the content around the datepicker
215217
// from jumping when it gets opened.
216218
margin-bottom: -$md-datepicker-border-bottom-gap;
217219
}
218220

221+
.md-icon-button + .md-datepicker-input-container {
222+
@include rtl-prop(margin-left, margin-right, -$md-datepicker-button-gap);
223+
}
224+
219225
.md-datepicker-input,
220226
label:not(.md-no-float):not(._md-container-ignore) {
221227
margin-bottom: -$md-datepicker-border-bottom-gap;
Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,80 @@
1-
<div ng-controller="AppCtrl" style='padding: 40px;' ng-cloak>
2-
<md-content>
3-
4-
<h4>Standard date-picker</h4>
5-
<md-datepicker ng-model="myDate" md-placeholder="Enter date"></md-datepicker>
6-
7-
<h4>Disabled date-picker</h4>
8-
<md-datepicker ng-model="myDate" md-placeholder="Enter date" disabled></md-datepicker>
9-
10-
<h4>Date-picker with min date and max date</h4>
11-
<md-datepicker ng-model="myDate" md-placeholder="Enter date"
12-
md-min-date="minDate" md-max-date="maxDate"></md-datepicker>
13-
14-
<h4>Only weekends are selectable</h4>
15-
<md-datepicker ng-model="myDate" md-placeholder="Enter date"
16-
md-date-filter="onlyWeekendsPredicate"></md-datepicker>
17-
18-
<h4>Only weekends within given range are selectable</h4>
19-
<md-datepicker ng-model="myDate" md-placeholder="Enter date"
20-
md-min-date="minDate" md-max-date="maxDate"
21-
md-date-filter="onlyWeekendsPredicate"></md-datepicker>
22-
23-
<h4>Opening the calendar when the input is focused</h4>
24-
<md-datepicker ng-model="myDate" md-placeholder="Enter date"
25-
md-open-on-focus></md-datepicker>
26-
27-
<h4>With ngMessages</h4>
28-
<form name="myForm">
29-
<md-datepicker name="dateField" ng-model="myDate" md-placeholder="Enter date"
30-
required md-min-date="minDate" md-max-date="maxDate"
31-
md-date-filter="onlyWeekendsPredicate"></md-datepicker>
32-
33-
<div class="validation-messages" ng-messages="myForm.dateField.$error">
34-
<div ng-message="valid">The entered value is not a date!</div>
35-
<div ng-message="required">This date is required!</div>
36-
<div ng-message="mindate">Date is too early!</div>
37-
<div ng-message="maxdate">Date is too late!</div>
38-
<div ng-message="filtered">Only weekends are allowed!</div>
1+
<div ng-controller="AppCtrl" ng-cloak>
2+
<md-content layout-padding>
3+
4+
<div layout-gt-xs="row">
5+
<div flex-gt-xs>
6+
<h4>Standard date-picker</h4>
7+
<md-datepicker ng-model="myDate" md-placeholder="Enter date"></md-datepicker>
8+
</div>
9+
10+
<div flex-gt-xs>
11+
<h4>Disabled date-picker</h4>
12+
<md-datepicker ng-model="myDate" md-placeholder="Enter date" disabled></md-datepicker>
13+
</div>
14+
</div>
15+
16+
<div layout-gt-xs="row">
17+
<div flex-gt-xs>
18+
<h4>Date-picker with min date and max date</h4>
19+
<md-datepicker ng-model="myDate" md-placeholder="Enter date"
20+
md-min-date="minDate" md-max-date="maxDate"></md-datepicker>
21+
</div>
22+
23+
<div flex-gt-xs>
24+
<h4>Only weekends are selectable</h4>
25+
<md-datepicker ng-model="myDate" md-placeholder="Enter date"
26+
md-date-filter="onlyWeekendsPredicate"></md-datepicker>
3927
</div>
40-
</form>
28+
</div>
4129

42-
<h4>Inside a md-input-container</h4>
43-
<form name="myOtherForm">
44-
<md-input-container>
45-
<label>Enter date</label>
30+
<div layout-gt-xs="row">
31+
<div flex-gt-xs>
32+
<h4>Only weekends within given range are selectable</h4>
33+
<md-datepicker ng-model="myDate" md-placeholder="Enter date"
34+
md-min-date="minDate" md-max-date="maxDate"
35+
md-date-filter="onlyWeekendsPredicate"></md-datepicker>
36+
</div>
37+
38+
<div flex-gt-xs>
39+
<h4>Opening the calendar when the input is focused</h4>
40+
<md-datepicker ng-model="myDate" md-placeholder="Enter date"
41+
md-open-on-focus></md-datepicker>
42+
</div>
43+
</div>
4644

47-
<md-datepicker ng-model="myDate" name="dateField" md-min-date="minDate"
48-
md-max-date="maxDate"></md-datepicker>
45+
<div layout-gt-xs="row">
46+
<form name="myForm" flex-gt-xs>
47+
<h4>With ngMessages</h4>
48+
<md-datepicker name="dateField" ng-model="myDate" md-placeholder="Enter date"
49+
required md-min-date="minDate" md-max-date="maxDate"
50+
md-date-filter="onlyWeekendsPredicate"></md-datepicker>
4951

50-
<div class="validation-messages" ng-messages="myOtherForm.dateField.$error">
52+
<div class="validation-messages" ng-messages="myForm.dateField.$error">
5153
<div ng-message="valid">The entered value is not a date!</div>
5254
<div ng-message="required">This date is required!</div>
5355
<div ng-message="mindate">Date is too early!</div>
5456
<div ng-message="maxdate">Date is too late!</div>
57+
<div ng-message="filtered">Only weekends are allowed!</div>
5558
</div>
56-
</md-input-container>
57-
</form>
59+
</form>
60+
61+
<form name="myOtherForm" flex-gt-xs>
62+
<h4>Inside a md-input-container</h4>
63+
64+
<md-input-container>
65+
<label>Enter date</label>
66+
<md-datepicker ng-model="myDate" name="dateField" md-min-date="minDate"
67+
md-max-date="maxDate"></md-datepicker>
68+
69+
<div class="validation-messages" ng-messages="myOtherForm.dateField.$error">
70+
<div ng-message="valid">The entered value is not a date!</div>
71+
<div ng-message="required">This date is required!</div>
72+
<div ng-message="mindate">Date is too early!</div>
73+
<div ng-message="maxdate">Date is too late!</div>
74+
</div>
75+
</md-input-container>
76+
</form>
77+
</div>
78+
5879
</md-content>
5980
</div>

src/components/datepicker/demoBasicUsage/script.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ angular.module('datepickerBasicUsage',
1111
$scope.myDate.getFullYear(),
1212
$scope.myDate.getMonth() + 2,
1313
$scope.myDate.getDate());
14-
14+
1515
$scope.onlyWeekendsPredicate = function(date) {
1616
var day = date.getDay();
1717
return day === 0 || day === 6;
18-
}
18+
};
1919
});

src/components/datepicker/js/datepickerDirective.js

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
* @param {(function(Date): boolean)=} md-date-filter Function expecting a date and returning a boolean whether it can be selected or not.
2828
* @param {String=} md-placeholder The date input placeholder value.
2929
* @param {String=} md-open-on-focus When present, the calendar will be opened when the input is focused.
30+
* @param {Boolean=} md-is-open Expression that can be used to open the datepicker's calendar on-demand.
31+
* @param {String=} md-hide-icons Determines which datepicker icons should be hidden. Note that this may cause the
32+
* datepicker to not align properly with other components. **Use at your own risk.** Possible values are:
33+
* * `"all"` - Hides all icons.
34+
* * `"calendar"` - Only hides the calendar icon.
35+
* * `"triangle"` - Only hides the triangle icon.
3036
* @param {boolean=} ng-disabled Whether the datepicker is disabled.
3137
* @param {boolean=} ng-required Whether a value is required for the datepicker.
3238
*
@@ -49,47 +55,58 @@
4955
*/
5056
function datePickerDirective($$mdSvgRegistry) {
5157
return {
52-
template:
53-
// Buttons are not in the tab order because users can open the calendar via keyboard
54-
// interaction on the text input, and multiple tab stops for one component (picker)
55-
// may be confusing.
58+
template: function(tElement, tAttrs) {
59+
// Buttons are not in the tab order because users can open the calendar via keyboard
60+
// interaction on the text input, and multiple tab stops for one component (picker)
61+
// may be confusing.
62+
var hiddenIcons = tAttrs.mdHideIcons;
63+
64+
var calendarButton = (hiddenIcons === 'all' || hiddenIcons === 'calendar') ? '' :
5665
'<md-button class="md-datepicker-button md-icon-button" type="button" ' +
5766
'tabindex="-1" aria-hidden="true" ' +
5867
'ng-click="ctrl.openCalendarPane($event)">' +
5968
'<md-icon class="md-datepicker-calendar-icon" aria-label="md-calendar" ' +
6069
'md-svg-src="' + $$mdSvgRegistry.mdCalendar + '"></md-icon>' +
61-
'</md-button>' +
62-
'<div class="md-datepicker-input-container" ' +
63-
'ng-class="{\'md-datepicker-focused\': ctrl.isFocused}">' +
64-
'<input class="md-datepicker-input" aria-haspopup="true" ' +
65-
'ng-focus="ctrl.setFocused(true)" ng-blur="ctrl.setFocused(false)">' +
66-
'<md-button type="button" md-no-ink ' +
67-
'class="md-datepicker-triangle-button md-icon-button" ' +
68-
'ng-click="ctrl.openCalendarPane($event)" ' +
69-
'aria-label="{{::ctrl.dateLocale.msgOpenCalendar}}">' +
70-
'<div class="md-datepicker-expand-triangle"></div>' +
71-
'</md-button>' +
70+
'</md-button>';
71+
72+
var triangleButton = (hiddenIcons === 'all' || hiddenIcons === 'triangle') ? '' :
73+
'<md-button type="button" md-no-ink ' +
74+
'class="md-datepicker-triangle-button md-icon-button" ' +
75+
'ng-click="ctrl.openCalendarPane($event)" ' +
76+
'aria-label="{{::ctrl.dateLocale.msgOpenCalendar}}">' +
77+
'<div class="md-datepicker-expand-triangle"></div>' +
78+
'</md-button>';
79+
80+
return '' +
81+
calendarButton +
82+
'<div class="md-datepicker-input-container" ' +
83+
'ng-class="{\'md-datepicker-focused\': ctrl.isFocused}">' +
84+
'<input class="md-datepicker-input" aria-haspopup="true" ' +
85+
'ng-focus="ctrl.setFocused(true)" ng-blur="ctrl.setFocused(false)">' +
86+
triangleButton +
87+
'</div>' +
88+
89+
// This pane will be detached from here and re-attached to the document body.
90+
'<div class="md-datepicker-calendar-pane md-whiteframe-z1">' +
91+
'<div class="md-datepicker-input-mask">' +
92+
'<div class="md-datepicker-input-mask-opaque"></div>' +
7293
'</div>' +
73-
74-
// This pane will be detached from here and re-attached to the document body.
75-
'<div class="md-datepicker-calendar-pane md-whiteframe-z1">' +
76-
'<div class="md-datepicker-input-mask">' +
77-
'<div class="md-datepicker-input-mask-opaque"></div>' +
78-
'</div>' +
79-
'<div class="md-datepicker-calendar">' +
80-
'<md-calendar role="dialog" aria-label="{{::ctrl.dateLocale.msgCalendar}}" ' +
81-
'md-min-date="ctrl.minDate" md-max-date="ctrl.maxDate"' +
82-
'md-date-filter="ctrl.dateFilter"' +
83-
'ng-model="ctrl.date" ng-if="ctrl.isCalendarOpen">' +
84-
'</md-calendar>' +
85-
'</div>' +
86-
'</div>',
94+
'<div class="md-datepicker-calendar">' +
95+
'<md-calendar role="dialog" aria-label="{{::ctrl.dateLocale.msgCalendar}}" ' +
96+
'md-min-date="ctrl.minDate" md-max-date="ctrl.maxDate"' +
97+
'md-date-filter="ctrl.dateFilter"' +
98+
'ng-model="ctrl.date" ng-if="ctrl.isCalendarOpen">' +
99+
'</md-calendar>' +
100+
'</div>' +
101+
'</div>';
102+
},
87103
require: ['ngModel', 'mdDatepicker', '?^mdInputContainer'],
88104
scope: {
89105
minDate: '=mdMinDate',
90106
maxDate: '=mdMaxDate',
91107
placeholder: '@mdPlaceholder',
92-
dateFilter: '=mdDateFilter'
108+
dateFilter: '=mdDateFilter',
109+
isOpen: '=?mdIsOpen'
93110
},
94111
controller: DatePickerCtrl,
95112
controllerAs: 'ctrl',
@@ -287,9 +304,22 @@
287304
this.attachInteractionListeners();
288305

289306
var self = this;
307+
290308
$scope.$on('$destroy', function() {
291309
self.detachCalendarPane();
292310
});
311+
312+
if ($attrs.mdIsOpen) {
313+
$scope.$watch('ctrl.isOpen', function(shouldBeOpen) {
314+
if (shouldBeOpen) {
315+
self.openCalendarPane({
316+
target: self.inputElement
317+
});
318+
} else {
319+
self.closeCalendarPane();
320+
}
321+
});
322+
}
293323
}
294324

295325
/**
@@ -396,7 +426,10 @@
396426
DatePickerCtrl.prototype.setDisabled = function(isDisabled) {
397427
this.isDisabled = isDisabled;
398428
this.inputElement.disabled = isDisabled;
399-
this.calendarButton.disabled = isDisabled;
429+
430+
if (this.calendarButton) {
431+
this.calendarButton.disabled = isDisabled;
432+
}
400433
};
401434

402435
/**
@@ -594,7 +627,7 @@
594627
*/
595628
DatePickerCtrl.prototype.openCalendarPane = function(event) {
596629
if (!this.isCalendarOpen && !this.isDisabled) {
597-
this.isCalendarOpen = true;
630+
this.isCalendarOpen = this.isOpen = true;
598631
this.calendarPaneOpenedFrom = event.target;
599632

600633
// Because the calendar pane is attached directly to the body, it is possible that the
@@ -643,7 +676,7 @@
643676

644677
function detach() {
645678
self.detachCalendarPane();
646-
self.isCalendarOpen = false;
679+
self.isCalendarOpen = self.isOpen = false;
647680
self.ngModelCtrl.$setTouched();
648681

649682
self.documentElement.off('click touchstart', self.bodyClickHandler);

0 commit comments

Comments
 (0)