Skip to content

Commit

Permalink
feat: add firstDayOfWeek to datepickers (#3220)
Browse files Browse the repository at this point in the history
  • Loading branch information
sashaqred authored Nov 8, 2023
1 parent 96968a6 commit 5923384
Show file tree
Hide file tree
Showing 16 changed files with 188 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { convertToBoolProperty, NbBooleanInput } from '../../../helpers';
[weekNumberSymbol]="weekNumberSymbol">
</nb-calendar-week-numbers>
<div class="days-container">
<nb-calendar-days-names [size]="size"></nb-calendar-days-names>
<nb-calendar-days-names [size]="size" [firstDayOfWeek]="firstDayOfWeek"></nb-calendar-days-names>
<nb-calendar-picker
[data]="weeks"
[visibleDate]="visibleDate"
Expand Down Expand Up @@ -121,6 +121,12 @@ export class NbCalendarDayPickerComponent<D, T> implements OnChanges {
* */
@Input() weekNumberSymbol: string;

/**
* Sets first day of the week, it can be 1 if week starts from monday and 0 if from sunday and so on.
* `undefined` means that default locale setting will be used.
* */
@Input() firstDayOfWeek: number | undefined;

/**
* Fires newly selected date.
* */
Expand All @@ -141,9 +147,9 @@ export class NbCalendarDayPickerComponent<D, T> implements OnChanges {
constructor(private monthModel: NbCalendarMonthModelService<D>) {
}

ngOnChanges({ visibleDate, boundingMonths }: SimpleChanges) {
if (visibleDate || boundingMonths) {
this.weeks = this.monthModel.createDaysGrid(this.visibleDate, this.boundingMonths);
ngOnChanges({ visibleDate, boundingMonths, firstDayOfWeek }: SimpleChanges) {
if (visibleDate || boundingMonths || firstDayOfWeek) {
this.weeks = this.monthModel.createDaysGrid(this.visibleDate, this.boundingMonths, this.firstDayOfWeek);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

import { ChangeDetectionStrategy, Component, OnInit, Input, HostBinding } from '@angular/core';
import { ChangeDetectionStrategy, Component, OnInit, Input, HostBinding, SimpleChanges, OnChanges } from '@angular/core';

import { NbCalendarDay, NbCalendarSize, NbCalendarSizeValues } from '../../model';
import { NbDateService } from '../../services/date.service';
Expand All @@ -18,7 +18,7 @@ import { NbDateService } from '../../services/date.service';
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NbCalendarDaysNamesComponent<D> implements OnInit {
export class NbCalendarDaysNamesComponent<D> implements OnInit, OnChanges {

days: NbCalendarDay[];

Expand All @@ -30,6 +30,12 @@ export class NbCalendarDaysNamesComponent<D> implements OnInit {
return this.size === NbCalendarSize.LARGE;
}

/**
* Sets first day of the week, it can be 1 if week starts from monday and 0 if from sunday and so on.
* `undefined` means that default locale setting will be used.
* */
@Input() firstDayOfWeek: number | undefined;

constructor(private dateService: NbDateService<D>) {
}

Expand All @@ -38,13 +44,21 @@ export class NbCalendarDaysNamesComponent<D> implements OnInit {
this.days = this.shiftStartOfWeek(days);
}

ngOnChanges({firstDayOfWeek}: SimpleChanges) {
if (firstDayOfWeek) {
const days: NbCalendarDay[] = this.createDaysNames();
this.days = this.shiftStartOfWeek(days);
}
}

private createDaysNames(): NbCalendarDay[] {
return this.dateService.getDayOfWeekNames()
.map(this.markIfHoliday);
}

private shiftStartOfWeek(days: NbCalendarDay[]): NbCalendarDay[] {
for (let i = 0; i < this.dateService.getFirstDayOfWeek(); i++) {
const firstDayOfWeek = this.firstDayOfWeek ?? this.dateService.getFirstDayOfWeek();
for (let i = 0; i < firstDayOfWeek; i++) {
days.push(days.shift());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ export class NbCalendarMonthModelService<D> {
constructor(protected dateService: NbDateService<D>) {
}

createDaysGrid(activeMonth: D, boundingMonth: boolean = true): D[][] {
const weeks = this.createDates(activeMonth);
createDaysGrid(activeMonth: D, boundingMonth: boolean = true, firstDayOfWeek?: number): D[][] {
const weeks = this.createDates(activeMonth, firstDayOfWeek);
return this.withBoundingMonths(weeks, activeMonth, boundingMonth);
}

private createDates(activeMonth: D): D[][] {
private createDates(activeMonth: D, firstDayOfWeek?: number): D[][] {
const days = this.createDateRangeForMonth(activeMonth);
const startOfWeekDayDiff = this.getStartOfWeekDayDiff(activeMonth);
const startOfWeekDayDiff = this.getStartOfWeekDayDiff(activeMonth, firstDayOfWeek);
return batch(days, this.dateService.DAYS_IN_WEEK, startOfWeekDayDiff);
}

Expand Down Expand Up @@ -70,13 +70,14 @@ export class NbCalendarMonthModelService<D> {
.map(date => boundingMonth ? date : null);
}

private getStartOfWeekDayDiff(date: D): number {
private getStartOfWeekDayDiff(date: D, firstDayOfWeek?: number): number {
const startOfMonth = this.dateService.getMonthStart(date);
return this.getWeekStartDiff(startOfMonth);
return this.getWeekStartDiff(startOfMonth, firstDayOfWeek);
}

private getWeekStartDiff(date: D): number {
return (7 - this.dateService.getFirstDayOfWeek() + this.dateService.getDayOfWeek(date)) % 7;
private getWeekStartDiff(date: D, firstDayOfWeek?: number): number {
const weekOfset = firstDayOfWeek ?? this.dateService.getFirstDayOfWeek();
return (7 - weekOfset + this.dateService.getDayOfWeek(date)) % 7;
}

private isShouldAddPrevBoundingMonth(weeks: D[][]): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
[size]="size"
[date]="date"
[showWeekNumber]="showWeekNumber"
[firstDayOfWeek]="firstDayOfWeek"
(dateChange)="dateChange.emit($any($event))"
[weekNumberSymbol]="weekNumberSymbol">
</nb-calendar-day-picker>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ export class NbBaseCalendarComponent<D, T> implements OnInit {
* */
@Input() weekNumberSymbol: string;

/**
* Sets first day of the week, it can be 1 if week starts from monday and 0 if from sunday and so on.
* `undefined` means that default locale setting will be used.
* */
@Input() firstDayOfWeek: number | undefined;

/**
* Emits date when selected.
* */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export interface NbCalendarRange<D> {
[size]="size"
[showWeekNumber]="showWeekNumber"
[weekNumberSymbol]="weekNumberSymbol"
[firstDayOfWeek]="firstDayOfWeek"
></nb-base-calendar>
`,
})
Expand Down Expand Up @@ -284,6 +285,12 @@ export class NbCalendarRangeComponent<D> {
* */
@Input() weekNumberSymbol: string = '#';

/**
* Sets first day of the week, it can be 1 if week starts from monday and 0 if from sunday and so on.
* `undefined` means that default locale setting will be used.
* */
@Input() firstDayOfWeek: number | undefined;

/**
* Emits range when start selected and emits again when end selected.
* */
Expand Down
7 changes: 7 additions & 0 deletions src/framework/theme/components/calendar/calendar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ import { convertToBoolProperty, NbBooleanInput } from '../helpers';
[showNavigation]="showNavigation"
[showWeekNumber]="showWeekNumber"
[weekNumberSymbol]="weekNumberSymbol"
[firstDayOfWeek]="firstDayOfWeek"
(dateChange)="dateChange.emit($event)"
></nb-base-calendar>
`,
Expand Down Expand Up @@ -297,6 +298,12 @@ export class NbCalendarComponent<D> {
* */
@Input() weekNumberSymbol: string = '#';

/**
* Sets first day of the week, it can be 1 if week starts from monday and 0 if from sunday and so on.
* `undefined` means that default locale setting will be used.
* */
@Input() firstDayOfWeek: number | undefined;

/**
* Emits date when selected.
* */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { NbTimePickerComponent } from '../timepicker/timepicker.component';
[showNavigation]="showNavigation"
[showWeekNumber]="showWeekNumber"
[weekNumberSymbol]="weekNumberSymbol"
[firstDayOfWeek]="firstDayOfWeek"
(dateChange)="onDateValueChange($event)"
>
</nb-base-calendar>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ export abstract class NbBasePicker<D, T, P> extends NbDatepicker<T, D> {
* */
abstract showWeekNumber: boolean;

/**
* Sets first day of the week, it can be 1 if week starts from monday and 0 if from sunday and so on.
* `undefined` means that default locale setting will be used.
* */
abstract firstDayOfWeek: number | undefined;

readonly formatChanged$: Subject<void> = new Subject();

/**
Expand Down Expand Up @@ -351,6 +357,7 @@ export abstract class NbBasePicker<D, T, P> extends NbDatepicker<T, D> {
this.picker.visibleDate = this.visibleDate;
this.picker.showWeekNumber = this.showWeekNumber;
this.picker.weekNumberSymbol = this.weekNumberSymbol;
this.picker.firstDayOfWeek = this.firstDayOfWeek;
}

protected checkFormat() {
Expand Down Expand Up @@ -465,6 +472,8 @@ export class NbBasePickerComponent<D, T, P> extends NbBasePicker<D, T, P> implem
protected _showWeekNumber: boolean = false;
static ngAcceptInputType_showWeekNumber: NbBooleanInput;

@Input() firstDayOfWeek: number | undefined;

/**
* Determines picker overlay offset (in pixels).
* */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
[step]="step"
[twelveHoursFormat]="twelveHoursFormat"
[showAmPmLabel]="false"
[firstDayOfWeek]="firstDayOfWeek"
#dateTimePicker
></nb-date-timepicker>
</section>
Expand Down Expand Up @@ -47,5 +48,21 @@
{{ formatToggleTimer ? 'Stop' : 'Start' }} auto format toggle
</button>
</section>

<section>
<nb-select [(selected)]="firstDayOfWeek">
<nb-option>LOCALE_ID</nb-option>
<nb-option [value]="0">Sunday</nb-option>
<nb-option [value]="1">Moday</nb-option>
<nb-option [value]="2">Tuesday</nb-option>
<nb-option [value]="3">Wednesday</nb-option>
<nb-option [value]="4">Thursday</nb-option>
<nb-option [value]="5">Friday</nb-option>
<nb-option [value]="6">Saturday</nb-option>
</nb-select>
<button nbButton (click)="toggleFirstDayOfWeekSwitching()">
{{ firstDayOfWeekToggleTimer ? 'Stop' : 'Start' }} auto firstDayOfWeek toggle
</button>
</section>
</nb-card-body>
</nb-card>
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Component } from '@angular/core';
margin-bottom: 2rem;
}
button + button {
section > * + * {
margin-left: 1rem;
}
`,
Expand Down Expand Up @@ -113,4 +113,26 @@ export class DateTimepickerDynamicInputsShowcaseComponent {
toggleFormat() {
this.format = this.format === 'dd/MM/yyyy HH:mm' ? 'HH:mm dd/MM/yyyy' : 'dd/MM/yyyy HH:mm';
}

firstDayOfWeek: number | undefined = undefined;
firstDayOfWeekToggleTimer = null;

toggleFirstDayOfWeekSwitching() {
if (this.firstDayOfWeekToggleTimer == null) {
this.firstDayOfWeekToggleTimer = setInterval(() => {
this.toggleFirstDayOfWeek();
}, 1000);
} else {
clearInterval(this.firstDayOfWeekToggleTimer);
this.firstDayOfWeekToggleTimer = null;
}
}

toggleFirstDayOfWeek() {
this.firstDayOfWeek ??= 0;
this.firstDayOfWeek++;
if (this.firstDayOfWeek > 6) {
this.firstDayOfWeek = 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<nb-card-body>
<section>
<input nbInput placeholder="Pick Date" [ngModel]="now" [nbDatepicker]="datePicker" />
<nb-datepicker [format]="format" #datePicker></nb-datepicker>
<nb-datepicker [format]="format" [firstDayOfWeek]="firstDayOfWeek" #datePicker></nb-datepicker>
</section>

<section>
Expand All @@ -11,5 +11,21 @@
{{ formatToggleTimer ? 'Stop' : 'Start' }} auto format toggle
</button>
</section>

<section>
<nb-select [(selected)]="firstDayOfWeek">
<nb-option>LOCALE_ID</nb-option>
<nb-option [value]="0">Sunday</nb-option>
<nb-option [value]="1">Moday</nb-option>
<nb-option [value]="2">Tuesday</nb-option>
<nb-option [value]="3">Wednesday</nb-option>
<nb-option [value]="4">Thursday</nb-option>
<nb-option [value]="5">Friday</nb-option>
<nb-option [value]="6">Saturday</nb-option>
</nb-select>
<button nbButton (click)="toggleFirstDayOfWeekSwitching()">
{{ firstDayOfWeekToggleTimer ? 'Stop' : 'Start' }} auto firstDayOfWeek toggle
</button>
</section>
</nb-card-body>
</nb-card>
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Component } from '@angular/core';
margin-bottom: 2rem;
}
button + button {
section > * + * {
margin-left: 1rem;
}
`,
Expand All @@ -26,6 +26,8 @@ export class DatepickerDynamicInputsShowcaseComponent {

format = 'dd/MM/yyyy HH:mm';
formatToggleTimer = null;
firstDayOfWeek: number | undefined = undefined;
firstDayOfWeekToggleTimer = null;

toggleFormatSwitching() {
if (this.formatToggleTimer == null) {
Expand All @@ -41,4 +43,23 @@ export class DatepickerDynamicInputsShowcaseComponent {
toggleFormat() {
this.format = this.format === 'dd/MM/yyyy HH:mm' ? 'HH:mm dd/MM/yyyy' : 'dd/MM/yyyy HH:mm';
}

toggleFirstDayOfWeekSwitching() {
if (this.firstDayOfWeekToggleTimer == null) {
this.firstDayOfWeekToggleTimer = setInterval(() => {
this.toggleFirstDayOfWeek();
}, 1000);
} else {
clearInterval(this.firstDayOfWeekToggleTimer);
this.firstDayOfWeekToggleTimer = null;
}
}

toggleFirstDayOfWeek() {
this.firstDayOfWeek ??= 0;
this.firstDayOfWeek++;
if (this.firstDayOfWeek > 6) {
this.firstDayOfWeek = 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NbButtonModule, NbCardModule, NbDatepickerModule, NbInputModule, NbTimepickerModule } from '@nebular/theme';
import { NbButtonModule, NbCardModule, NbDatepickerModule, NbInputModule, NbSelectModule, NbTimepickerModule } from '@nebular/theme';
import { DatepickerWithFormatRoutingModule } from './datepicker-with-format-routing.module';
import { DateTimepickerDynamicInputsShowcaseComponent } from './date-timepicker-dynamic-inputs-showcase.component';
import { NbDateFnsDateModule } from '@nebular/date-fns';
Expand All @@ -29,6 +29,7 @@ import { RangepickerDynamicInputsShowcaseComponent } from './rangepicker-dynamic
NbCardModule,
NbButtonModule,
NbDateFnsDateModule.forRoot({}),
NbSelectModule,
],
})
export class DatepickerWithFormatModule {}
Loading

0 comments on commit 5923384

Please sign in to comment.