Skip to content

Commit a92c0e7

Browse files
maxokorokovpkozlowski-opensource
authored andcommitted
feat(datepicker): can easily hide days outside of current month
Closes #937
1 parent 54cd30b commit a92c0e7

File tree

9 files changed

+104
-15
lines changed

9 files changed

+104
-15
lines changed

demo/src/app/components/datepicker/demos/config/datepicker-config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ export class NgbdDatepickerConfig {
1515
config.minDate = {year: 1900, month: 1, day: 1};
1616
config.maxDate = {year: 2099, month: 12, day: 31};
1717

18+
// days that don't belong to current month are not visible
19+
config.outsideDays = 'hidden';
20+
1821
// weekends are disabled
1922
config.markDisabled = (date: NgbDateStruct) => {
2023
const d = new Date(date.year, date.month - 1, date.day);

src/datepicker/datepicker-config.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe('ngb-datepicker-config', () => {
99
expect(config.markDisabled).toBeUndefined();
1010
expect(config.minDate).toBeUndefined();
1111
expect(config.maxDate).toBeUndefined();
12+
expect(config.outsideDays).toBe('visible');
1213
expect(config.showNavigation).toBe(true);
1314
expect(config.showWeekdays).toBe(true);
1415
expect(config.showWeekNumbers).toBe(false);

src/datepicker/datepicker-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export class NgbDatepickerConfig {
1414
markDisabled: (date: NgbDateStruct, current: {year: number, month: number}) => boolean;
1515
minDate: NgbDateStruct;
1616
maxDate: NgbDateStruct;
17+
outsideDays: 'visible' | 'collapsed' | 'hidden' = 'visible';
1718
showNavigation = true;
1819
showWeekdays = true;
1920
showWeekNumbers = false;

src/datepicker/datepicker-input.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,17 @@ describe('NgbInputDatepicker', () => {
201201
expect(dp.maxDate).toEqual({year: 2016, month: 9, day: 13});
202202
});
203203

204+
it('should propagate the "outsideDays" option', () => {
205+
const fixture = createTestCmpt(`<input ngbDatepicker outsideDays="collapsed">`);
206+
const dpInput = fixture.debugElement.query(By.directive(NgbInputDatepicker)).injector.get(NgbInputDatepicker);
207+
208+
dpInput.open();
209+
fixture.detectChanges();
210+
211+
const dp = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbDatepicker);
212+
expect(dp.outsideDays).toEqual('collapsed');
213+
});
214+
204215
it('should propagate the "showNavigation" option', () => {
205216
const fixture = createTestCmpt(`<input ngbDatepicker [showNavigation]="true">`);
206217
const dpInput = fixture.debugElement.query(By.directive(NgbInputDatepicker)).injector.get(NgbInputDatepicker);

src/datepicker/datepicker-input.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ export class NgbInputDatepicker implements ControlValueAccessor {
6767
*/
6868
@Input() maxDate: NgbDateStruct;
6969

70+
/**
71+
* The way to display days that don't belong to current month: `visible` (default),
72+
* `hidden` (not displayed) or `collapsed` (not displayed with empty space collapsed)
73+
*/
74+
@Input() outsideDays: 'visible' | 'collapsed' | 'hidden';
75+
7076
/**
7177
* Whether to display navigation
7278
*/
@@ -184,8 +190,8 @@ export class NgbInputDatepicker implements ControlValueAccessor {
184190
}
185191

186192
private _applyDatepickerInputs(datepickerInstance: NgbDatepicker): void {
187-
['dayTemplate', 'firstDayOfWeek', 'markDisabled', 'minDate', 'maxDate', 'showNavigation', 'showWeekdays',
188-
'showWeekNumbers']
193+
['dayTemplate', 'firstDayOfWeek', 'markDisabled', 'minDate', 'maxDate', 'outsideDays', 'showNavigation',
194+
'showWeekdays', 'showWeekNumbers']
189195
.forEach((optionName: string) => {
190196
if (this[optionName] !== undefined) {
191197
datepickerInstance[optionName] = this[optionName];

src/datepicker/datepicker-month-view.spec.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('ngbDatepickerMonthView', () => {
7373
<template #tpl let-date="date">{{ date.day }}</template>
7474
<tbody ngbDatepickerMonthView [month]="month" [dayTemplate]="tpl"></tbody>
7575
`);
76-
expectDates(fixture.nativeElement, ['22']);
76+
expectDates(fixture.nativeElement, ['22', '23']);
7777
});
7878

7979
it('should send date selection events', () => {
@@ -163,8 +163,49 @@ describe('ngbDatepickerMonthView', () => {
163163
const dates = getDates(fixture.nativeElement);
164164
dates.forEach((date) => expect(window.getComputedStyle(date).getPropertyValue('cursor')).toBe('default'));
165165
});
166+
167+
it('should set default cursor for other months days', () => {
168+
const fixture =
169+
createTestComponent('<tbody ngbDatepickerMonthView [month]="month" [outsideDays]="outsideDays"></tbody>');
170+
171+
const dates = getDates(fixture.nativeElement);
172+
expect(window.getComputedStyle(dates[1]).getPropertyValue('cursor')).toBe('pointer');
173+
174+
fixture.componentInstance.outsideDays = 'collapsed';
175+
fixture.detectChanges();
176+
expect(window.getComputedStyle(dates[1]).getPropertyValue('cursor')).toBe('default');
177+
178+
fixture.componentInstance.outsideDays = 'hidden';
179+
fixture.detectChanges();
180+
expect(window.getComputedStyle(dates[1]).getPropertyValue('cursor')).toBe('default');
181+
});
166182
}
167183

184+
it('should apply proper visibility to other months days', () => {
185+
const fixture =
186+
createTestComponent('<tbody ngbDatepickerMonthView [month]="month" [outsideDays]="outsideDays"></tbody>');
187+
188+
let dates = getDates(fixture.nativeElement);
189+
expect(dates[0]).not.toHaveCssClass('hidden');
190+
expect(dates[0]).not.toHaveCssClass('collapsed');
191+
expect(dates[1]).not.toHaveCssClass('hidden');
192+
expect(dates[1]).not.toHaveCssClass('collapsed');
193+
194+
fixture.componentInstance.outsideDays = 'collapsed';
195+
fixture.detectChanges();
196+
expect(dates[0]).not.toHaveCssClass('hidden');
197+
expect(dates[0]).not.toHaveCssClass('collapsed');
198+
expect(dates[1]).not.toHaveCssClass('hidden');
199+
expect(dates[1]).toHaveCssClass('collapsed');
200+
201+
fixture.componentInstance.outsideDays = 'hidden';
202+
fixture.detectChanges();
203+
expect(dates[0]).not.toHaveCssClass('hidden');
204+
expect(dates[0]).not.toHaveCssClass('collapsed');
205+
expect(dates[1]).toHaveCssClass('hidden');
206+
expect(dates[1]).not.toHaveCssClass('collapsed');
207+
});
208+
168209
});
169210

170211
@Component({selector: 'test-cmp', template: ''})
@@ -173,11 +214,15 @@ class TestComponent {
173214
year: 2016,
174215
number: 7,
175216
weekdays: [1],
176-
weeks: [{number: 2, days: [{date: new NgbDate(2016, 7, 22), disabled: false}]}]
217+
weeks: [{
218+
number: 2,
219+
days: [{date: new NgbDate(2016, 7, 22), disabled: false}, {date: new NgbDate(2016, 8, 23), disabled: false}]
220+
}]
177221
};
178222

179223
showWeekdays = true;
180224
showWeekNumbers = true;
225+
outsideDays = 'visible';
181226

182227
onClick = () => {};
183228
}

src/datepicker/datepicker-month-view.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@ import {DayTemplateContext} from './datepicker-day-template-context';
1717
height: 100%;
1818
cursor: pointer;
1919
}
20-
.day.disabled {
20+
.day.disabled, .day.hidden, .day.collapsed {
2121
cursor: default;
2222
}
23+
:host/deep/.day.collapsed > * {
24+
display: none;
25+
}
26+
:host/deep/.day.hidden > * {
27+
visibility: hidden;
28+
}
2329
`],
2430
template: `
2531
<tr *ngIf="showWeekdays">
@@ -28,21 +34,23 @@ import {DayTemplateContext} from './datepicker-day-template-context';
2834
</tr>
2935
<tr *ngFor="let week of month.weeks">
3036
<td *ngIf="showWeekNumbers" class="weeknumber small text-xs-center">{{ week.number }}</td>
31-
<td *ngFor="let day of week.days" (click)="doSelect(day)" class="day" [class.disabled]="isDisabled(day)">
32-
<template [ngTemplateOutlet]="dayTemplate"
33-
[ngOutletContext]="{date: {year: day.date.year, month: day.date.month, day: day.date.day},
34-
currentMonth: month.number,
35-
disabled: isDisabled(day),
36-
selected: isSelected(day.date)}">
37-
</template>
38-
</td>
37+
<td *ngFor="let day of week.days" (click)="doSelect(day)" class="day" [class.disabled]="isDisabled(day)"
38+
[class.collapsed]="isCollapsed(day)" [class.hidden]="isHidden(day)">
39+
<template [ngTemplateOutlet]="dayTemplate"
40+
[ngOutletContext]="{date: {year: day.date.year, month: day.date.month, day: day.date.day},
41+
currentMonth: month.number,
42+
disabled: isDisabled(day),
43+
selected: isSelected(day.date)}">
44+
</template>
45+
</td>
3946
</tr>
4047
`
4148
})
4249
export class NgbDatepickerMonthView {
4350
@Input() dayTemplate: TemplateRef<DayTemplateContext>;
4451
@Input() disabled: boolean;
4552
@Input() month: MonthViewModel;
53+
@Input() outsideDays: 'visible' | 'hidden' | 'collapsed';
4654
@Input() selectedDate: NgbDate;
4755
@Input() showWeekdays;
4856
@Input() showWeekNumbers;
@@ -52,12 +60,16 @@ export class NgbDatepickerMonthView {
5260
constructor(public i18n: NgbDatepickerI18n) {}
5361

5462
doSelect(day: DayViewModel) {
55-
if (!this.isDisabled(day)) {
63+
if (!this.isDisabled(day) && !this.isCollapsed(day) && !this.isHidden(day)) {
5664
this.select.emit(NgbDate.from(day.date));
5765
}
5866
}
5967

60-
isDisabled(day) { return this.disabled || day.disabled; }
68+
isDisabled(day: DayViewModel) { return this.disabled || day.disabled; }
6169

6270
isSelected(date: NgbDate) { return this.selectedDate && this.selectedDate.equals(date); }
71+
72+
isCollapsed(day: DayViewModel) { return this.outsideDays === 'collapsed' && this.month.number !== day.date.month; }
73+
74+
isHidden(day: DayViewModel) { return this.outsideDays === 'hidden' && this.month.number !== day.date.month; }
6375
}

src/datepicker/datepicker.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ function expectSameValues(datepicker: NgbDatepicker, config: NgbDatepickerConfig
3333
expect(datepicker.markDisabled).toBe(config.markDisabled);
3434
expect(datepicker.minDate).toBe(config.minDate);
3535
expect(datepicker.maxDate).toBe(config.maxDate);
36+
expect(datepicker.outsideDays).toBe(config.outsideDays);
3637
expect(datepicker.showNavigation).toBe(config.showNavigation);
3738
expect(datepicker.showWeekdays).toBe(config.showWeekdays);
3839
expect(datepicker.showWeekNumbers).toBe(config.showWeekNumbers);
@@ -45,6 +46,7 @@ function customizeConfig(config: NgbDatepickerConfig) {
4546
config.markDisabled = (date, current) => false;
4647
config.minDate = {year: 2000, month: 1, day: 1};
4748
config.maxDate = {year: 2030, month: 12, day: 31};
49+
config.outsideDays = 'collapsed';
4850
config.showNavigation = false;
4951
config.showWeekdays = false;
5052
config.showWeekNumbers = true;

src/datepicker/datepicker.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const NGB_DATEPICKER_VALUE_ACCESSOR = {
4444
[showWeekdays]="showWeekdays"
4545
[showWeekNumbers]="showWeekNumbers"
4646
[disabled]="disabled"
47+
[outsideDays]="outsideDays"
4748
(select)="onDateSelect($event)">
4849
</tbody>
4950
</table>
@@ -85,6 +86,12 @@ export class NgbDatepicker implements OnChanges,
8586
*/
8687
@Input() maxDate: NgbDateStruct;
8788

89+
/**
90+
* The way to display days that don't belong to current month: `visible` (default),
91+
* `hidden` (not displayed) or `collapsed` (not displayed with empty space collapsed)
92+
*/
93+
@Input() outsideDays: 'visible' | 'collapsed' | 'hidden';
94+
8895
/**
8996
* Whether to display navigation
9097
*/
@@ -119,6 +126,7 @@ export class NgbDatepicker implements OnChanges,
119126
this.markDisabled = config.markDisabled;
120127
this.minDate = config.minDate;
121128
this.maxDate = config.maxDate;
129+
this.outsideDays = config.outsideDays;
122130
this.showNavigation = config.showNavigation;
123131
this.showWeekdays = config.showWeekdays;
124132
this.showWeekNumbers = config.showWeekNumbers;

0 commit comments

Comments
 (0)