Skip to content

Commit

Permalink
fix(kit): CalendarRange should not require extra click to start new…
Browse files Browse the repository at this point in the history
… range after same day range
  • Loading branch information
nsbarsukov committed Dec 24, 2024
1 parent 2c14206 commit 7ce58d3
Show file tree
Hide file tree
Showing 21 changed files with 443 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ export class TuiMobileCalendarDropdown {
protected readonly range = this.is('tui-input-date-range');
protected readonly multi = this.data.multi || this.is('tui-input-date[multiple]');
protected readonly single =
this.data.single || this.is('tui-input-date:not([multiple])');
this.data.single || // TODO(v5): use `rangeMode` from DI token `TUI_CALENDAR_SHEET_DEFAULT_OPTIONS`
this.is('tui-input-date:not([multiple])');

constructor() {
this.keyboard.hide();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {TuiMapperPipe} from '@taiga-ui/cdk/pipes/mapper';
import {TUI_IS_E2E, TUI_IS_IOS} from '@taiga-ui/cdk/tokens';
import type {TuiBooleanHandler, TuiMapper} from '@taiga-ui/cdk/types';
import {TuiButton} from '@taiga-ui/core/components/button';
import {TUI_CALENDAR_SHEET_OPTIONS} from '@taiga-ui/core/components/calendar';
import {TuiLink} from '@taiga-ui/core/components/link';
import {TuiMonthPipe} from '@taiga-ui/core/pipes/month';
import {TuiOrderWeekDaysPipe} from '@taiga-ui/core/pipes/order-week-days';
Expand Down Expand Up @@ -153,8 +154,15 @@ export class TuiMobileCalendar implements AfterViewInit {
),
);

/**
* @deprecated use static DI options instead
* ```
* tuiCalendarSheetOptionsProvider({rangeMode: boolean})
* ```
* TODO(v5): delete it
*/
@Input()
public single = true;
public single = !inject(TUI_CALENDAR_SHEET_OPTIONS).rangeMode;

@Input()
public multi = false;
Expand All @@ -180,7 +188,7 @@ export class TuiMobileCalendar implements AfterViewInit {
public readonly valueChange = this.value$.pipe(
skip(1),
distinctUntilChanged((a, b) => a?.toString() === b?.toString()),
takeUntilDestroyed(),
map((x) => (!this.single && x instanceof TuiDay ? new TuiDayRange(x, x) : x)),
);

constructor() {
Expand Down Expand Up @@ -245,10 +253,8 @@ export class TuiMobileCalendar implements AfterViewInit {
this.value = tuiToggleDay(this.value, day);
} else if (this.value instanceof TuiDay) {
this.value = TuiDayRange.sort(this.value, day);
} else if (this.value instanceof TuiDayRange && !this.value.isSingleDay) {
this.value = day;
} else if (this.value instanceof TuiDayRange) {
this.value = TuiDayRange.sort(this.value.from, day);
this.value = day;
} else if (!this.value) {
this.value = day;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ <h2 class="t-month">{{ month | tuiMonth | async }}</h2>
class="t-calendar"
[disabledItemHandler]="disabledItemHandler | tuiMapper: disabledItemHandlerMapper : min : max"
[month]="month"
[single]="single"
[value]="value"
(dayClick)="onDayClick($event)"
/>
Expand Down
15 changes: 15 additions & 0 deletions projects/cdk/date-time/day-range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,19 @@ export class TuiDayRange extends TuiMonthRange {
): string {
return this.getFormattedDayRange(dateFormat, dateSeparator);
}

public toArray(): readonly TuiDay[] {
const {from, to} = this;
const arr = [];

for (
const day = from.toUtcNativeDate();
day <= to.toUtcNativeDate();
day.setDate(day.getDate() + 1)
) {
arr.push(TuiDay.fromLocalNativeDate(day));
}

return arr;
}
}
18 changes: 18 additions & 0 deletions projects/cdk/date-time/test/day-range.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,24 @@ describe('TuiDayRange', () => {
});
});

describe('toArray', () => {
it('returns array with all dates between `from` and `to` (including `from` and `to` day)', () => {
const range = new TuiDayRange(
new TuiDay(2024, 11, 29),
new TuiDay(2025, 0, 3),
);

expect(range.toArray()).toEqual([
new TuiDay(2024, 11, 29),
new TuiDay(2024, 11, 30),
new TuiDay(2024, 11, 31),
new TuiDay(2025, 0, 1),
new TuiDay(2025, 0, 2),
new TuiDay(2025, 0, 3),
]);
});
});

describe('dayLimit', () => {
it('limits from one side if the other is null', () => {
const y2000m0d1 = new TuiDay(2000, 0, 1);
Expand Down
75 changes: 53 additions & 22 deletions projects/core/components/calendar/calendar-sheet.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {tuiNullableSame, tuiPure} from '@taiga-ui/cdk/utils/miscellaneous';
import {TuiCalendarSheetPipe, TuiOrderWeekDaysPipe} from '@taiga-ui/core/pipes';
import {TUI_DAY_TYPE_HANDLER, TUI_SHORT_WEEK_DAYS} from '@taiga-ui/core/tokens';

import {TUI_CALENDAR_SHEET_OPTIONS} from './calendar-sheet.options';

export type TuiMarkerHandler = TuiHandler<TuiDay, [] | [string, string] | [string]>;

@Component({
Expand All @@ -36,10 +38,11 @@ export type TuiMarkerHandler = TuiHandler<TuiDay, [] | [string, string] | [strin
styleUrls: ['./calendar-sheet.style.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class._picking]': 'isSingleDayRange',
'[class._picking]': 'isRangePicking',
},
})
export class TuiCalendarSheet {
private readonly options = inject(TUI_CALENDAR_SHEET_OPTIONS);
private readonly today = TuiDay.currentLocal();

protected readonly unorderedWeekDays$ = inject(TUI_SHORT_WEEK_DAYS);
Expand All @@ -63,12 +66,25 @@ export class TuiCalendarSheet {
@Input()
public showAdjacent = true;

/**
* @deprecated use static DI options instead
* ```
* tuiCalendarSheetOptionsProvider({rangeMode: true})
* ```
* TODO(v5): delete it
*/
@Input()
public single = true;

@Output()
public readonly hoveredItemChange = new EventEmitter<TuiDay | null>();

@Output()
public readonly dayClick = new EventEmitter<TuiDay>();

/**
* @deprecated TODO(v5): delete it. It is used nowhere except unit tests
*/
public itemIsInterval(day: TuiDay): boolean {
const {value, hoveredItem} = this;

Expand Down Expand Up @@ -96,17 +112,25 @@ export class TuiCalendarSheet {
public getItemRange(item: TuiDay): 'active' | 'end' | 'middle' | 'start' | null {
const {value, hoveredItem} = this;

if (value instanceof TuiDay) {
if (!value) {
return null;
}

if (value instanceof TuiDay && !this.computedRangeMode) {
return value.daySame(item) ? 'active' : null;
}

if (!value || !(value instanceof TuiDayRange)) {
return value?.find((day) => day.daySame(item)) ? 'active' : null;
if (value instanceof TuiDayRange && value.isSingleDay) {
return value.from.daySame(item) ? 'active' : null;
}

if (!(value instanceof TuiDay) && !(value instanceof TuiDayRange)) {

Check notice on line 127 in projects/core/components/calendar/calendar-sheet.component.ts

View check run for this annotation

codefactor.io / CodeFactor

projects/core/components/calendar/calendar-sheet.component.ts#L112-L127

Complex Method
return value.find((day) => day.daySame(item)) ? 'active' : null;
}

const range = this.getRange(value, hoveredItem);

if (value.isSingleDay && range.isSingleDay && value.from.daySame(item)) {
if (range.isSingleDay && range.from.daySame(item)) {
return 'active';
}

Expand All @@ -121,8 +145,18 @@ export class TuiCalendarSheet {
return range.from.dayBefore(item) && range.to.dayAfter(item) ? 'middle' : null;
}

protected get isSingleDayRange(): boolean {
return this.value instanceof TuiDayRange && this.value.isSingleDay;
protected get computedRangeMode(): boolean {
return !this.single || this.options.rangeMode;
}

protected get isRangePicking(): boolean {
return this.computedRangeMode
? this.value instanceof TuiDay
: /**
* Only for backward compatibility!
* TODO(v5): replace with `this.options.rangeMode && this.value instanceof TuiDay`
*/
this.value instanceof TuiDayRange && this.value.isSingleDay;
}

protected readonly toMarkers = (
Expand Down Expand Up @@ -157,7 +191,14 @@ export class TuiCalendarSheet {
}

@tuiPure
private getRange(value: TuiDayRange, hoveredItem: TuiDay | null): TuiDayRange {
private getRange(
value: TuiDay | TuiDayRange,
hoveredItem: TuiDay | null,
): TuiDayRange {
if (value instanceof TuiDay) {
return TuiDayRange.sort(value, hoveredItem ?? value);
}

return value.isSingleDay
? TuiDayRange.sort(value.from, hoveredItem ?? value.to)
: value;
Expand All @@ -173,20 +214,10 @@ export class TuiCalendarSheet {
}

private rangeHasDisabledDay(item: TuiDay): boolean {
if (this.value instanceof TuiDayRange) {
const range = this.getRange(this.value, item);

for (
const day = range.from.toUtcNativeDate();
day <= range.to.toUtcNativeDate();
day.setDate(day.getDate() + 1)
) {
const tuiDay = TuiDay.fromLocalNativeDate(day);

if (this.disabledItemHandler(tuiDay)) {
return true;
}
}
if (this.isRangePicking && this.value instanceof TuiDay) {
return this.getRange(this.value, item)
.toArray()
.some((x) => this.disabledItemHandler(x));
}

return false;
Expand Down
24 changes: 24 additions & 0 deletions projects/core/components/calendar/calendar-sheet.options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type {Provider} from '@angular/core';
import {tuiCreateToken, tuiProvideOptions} from '@taiga-ui/cdk/utils/miscellaneous';

export interface TuiCalendarSheetOptions {
readonly rangeMode: boolean;
}

export const TUI_CALENDAR_SHEET_DEFAULT_OPTIONS: TuiCalendarSheetOptions = {
rangeMode: false,
};

export const TUI_CALENDAR_SHEET_OPTIONS = tuiCreateToken(
TUI_CALENDAR_SHEET_DEFAULT_OPTIONS,
);

export function tuiCalendarSheetOptionsProvider(
options: Partial<TuiCalendarSheetOptions>,
): Provider {
return tuiProvideOptions(
TUI_CALENDAR_SHEET_OPTIONS,
options,
TUI_CALENDAR_SHEET_DEFAULT_OPTIONS,
);
}
1 change: 1 addition & 0 deletions projects/core/components/calendar/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './calendar.component';
export * from './calendar-sheet.component';
export * from './calendar-sheet.options';
export * from './calendar-spin.component';
export * from './calendar-year.component';
Loading

0 comments on commit 7ce58d3

Please sign in to comment.