Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(kit): CalendarRange should not require extra click to start new range after same day range #9847

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
// TODO: Rework to use TuiDropdownOpenDirective so the focus returns to the field on closing
private readonly dropdown = inject(TuiDropdownDirective, {optional: true});
private readonly keyboard = inject(TuiKeyboardService);
private readonly context = injectContext<Record<string, any>>({optional: true});

Check warning on line 50 in projects/addon-mobile/components/mobile-calendar-dropdown/mobile-calendar-dropdown.component.ts

View check run for this annotation

codefactor.io / CodeFactor

projects/addon-mobile/components/mobile-calendar-dropdown/mobile-calendar-dropdown.component.ts#L50

Unexpected any. Specify a different type. (@typescript-eslint/no-explicit-any)
private readonly observer?: Observer<any> = this.context?.$implicit;

Check warning on line 51 in projects/addon-mobile/components/mobile-calendar-dropdown/mobile-calendar-dropdown.component.ts

View check run for this annotation

codefactor.io / CodeFactor

projects/addon-mobile/components/mobile-calendar-dropdown/mobile-calendar-dropdown.component.ts#L51

Unexpected any. Specify a different type. (@typescript-eslint/no-explicit-any)
private readonly data: TuiMobileCalendarData = this.context?.data || {};

private selectedPeriod: TuiDayRange | null = null;
Expand All @@ -62,11 +62,12 @@
};

// TODO: Refactor to proper Date, DateMulti and DateRange components after they are added to kit
protected readonly control: any = inject(TuiControl, {optional: true});

Check warning on line 65 in projects/addon-mobile/components/mobile-calendar-dropdown/mobile-calendar-dropdown.component.ts

View check run for this annotation

codefactor.io / CodeFactor

projects/addon-mobile/components/mobile-calendar-dropdown/mobile-calendar-dropdown.component.ts#L65

Unexpected any. Specify a different type. (@typescript-eslint/no-explicit-any)
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 Expand Up @@ -130,7 +131,7 @@
this.keyboard.show();
}

protected confirm(value: any): void {

Check warning on line 134 in projects/addon-mobile/components/mobile-calendar-dropdown/mobile-calendar-dropdown.component.ts

View check run for this annotation

codefactor.io / CodeFactor

projects/addon-mobile/components/mobile-calendar-dropdown/mobile-calendar-dropdown.component.ts#L134

Unexpected any. Specify a different type. (@typescript-eslint/no-explicit-any)
const normalizedValue = this.range ? this.selectedPeriod : value;

if (this.control) {
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 @@ -16,7 +16,7 @@
) {
super(from, to);

ngDevMode && console.assert(from.daySameOrBefore(to));

Check warning on line 19 in projects/cdk/date-time/day-range.ts

View check run for this annotation

codefactor.io / CodeFactor

projects/cdk/date-time/day-range.ts#L19

Expected an assignment or function call and instead saw an expression. (@typescript-eslint/no-unused-expressions)
}

/**
Expand Down Expand Up @@ -102,4 +102,19 @@
): 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 {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 @@
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 @@
@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 All @@ -93,20 +109,28 @@
this.updateHoveredItem(item || null);
}

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 @@
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 @@
}

@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 @@
}

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
Loading