@taiga-ui/addon-mobile
Version:
Extension package for Taiga UI that adds support for mobile specific behaviors such as custom data pickers, dropdowns, etc.
489 lines (482 loc) • 35.2 kB
JavaScript
import { VIRTUAL_SCROLL_STRATEGY, CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { DOCUMENT, AsyncPipe, NgForOf, NgIf } from '@angular/common';
import * as i0 from '@angular/core';
import { InjectionToken, Optional, inject, DestroyRef, NgZone, EventEmitter, Component, ChangeDetectionStrategy, ViewChild, Input, Output } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { TuiMobileCalendarSheet } from '@taiga-ui/addon-mobile/components/mobile-calendar-sheet';
import { TuiRipple, TuiTouchable } from '@taiga-ui/addon-mobile/directives';
import { TUI_FALSE_HANDLER } from '@taiga-ui/cdk/constants';
import { MONTHS_IN_YEAR, TuiDay, TuiMonth, TUI_FIRST_DAY, TUI_LAST_DAY, TuiDayRange } from '@taiga-ui/cdk/date-time';
import { tuiWatch, tuiZonefreeScheduler, tuiZonefree, tuiTypedFromEvent } from '@taiga-ui/cdk/observables';
import { TuiMapperPipe } from '@taiga-ui/cdk/pipes/mapper';
import { TUI_IS_IOS, TUI_IS_E2E } from '@taiga-ui/cdk/tokens';
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';
import { TUI_ANIMATIONS_SPEED, TUI_COMMON_ICONS, TUI_CLOSE_WORD, TUI_SHORT_WEEK_DAYS } from '@taiga-ui/core/tokens';
import { tuiGetDuration } from '@taiga-ui/core/utils/miscellaneous';
import { TUI_CALENDAR_DATE_STREAM, TUI_CANCEL_WORD, TUI_DONE_WORD, TUI_CHOOSE_DAY_OR_RANGE_TEXTS } from '@taiga-ui/kit/tokens';
import { tuiToggleDay } from '@taiga-ui/kit/utils';
import { Subject, distinctUntilChanged, takeUntil, EMPTY, BehaviorSubject, skip, map, timer, identity, delay, take, windowToggle, mergeMap, filter, switchMap, race, debounceTime } from 'rxjs';
import { TuiScrollService } from '@taiga-ui/cdk/services';
import { __decorate } from 'tslib';
import { tuiPure } from '@taiga-ui/cdk/utils/miscellaneous';
function getCycle(options) {
return Array.from({ length: options.yearCycle }, (_, i) => Array.from({ length: MONTHS_IN_YEAR }, (_, month) => options.label +
weekCount({ year: i, month, startingYear: options.startingYear }) *
options.week));
}
function weekCount(options) {
const firstOfMonth = new Date(options.year + options.startingYear, options.month, 1);
const lastOfMonth = new Date(options.year + options.startingYear, options.month + 1, 0);
const days = lastOfMonth.getDate() + (firstOfMonth.getDay() || 7) - 1;
return Math.ceil(days / 7);
}
const SCROLL_DEBOUNCE_TIME = 80;
const YEARS_IN_ROW = 5;
const STARTING_YEAR = 1900;
const RANGE = 196;
const BUFFER = 500;
const ANDROID_LABEL = 64;
const ANDROID_WEEK = 48;
const IOS_LABEL = 50;
const IOS_WEEK = 50;
const YEARLY_CYCLE = 28;
const ANDROID_CYCLE = getCycle({
label: ANDROID_LABEL,
week: ANDROID_WEEK,
yearCycle: YEARLY_CYCLE,
startingYear: STARTING_YEAR,
});
const IOS_CYCLE = getCycle({
label: IOS_LABEL,
week: IOS_WEEK,
yearCycle: YEARLY_CYCLE,
startingYear: STARTING_YEAR,
});
const ANDROID_CYCLE_HEIGHT = reduceCycle(ANDROID_CYCLE);
const IOS_CYCLE_HEIGHT = reduceCycle(IOS_CYCLE);
function reduceCycle(cycle, lastYear = 28, lastMonth = 12) {
return cycle.reduce((total, year, yearIndex) => yearIndex <= lastYear
? total +
year.reduce((sum, month, monthIndex) => yearIndex < lastYear ||
(yearIndex === lastYear && monthIndex < lastMonth)
? sum + month
: sum, 0)
: total, 0);
}
/**
* This scroll strategy is hard wired with styles for iOS and Android.
* It is dependent on month height on those platforms and is designed to
* work for {@link TuiMobileCalendar} with years 1906 to 2102
*/
class TuiMobileCalendarStrategy {
constructor(isIOS, scrollService) {
this.isIOS = isIOS;
this.scrollService = scrollService;
this.index$ = new Subject();
this.viewport = null;
this.destroy$ = new Subject();
}
get scrolledIndexChange() {
return this.index$.pipe(distinctUntilChanged());
}
attach(viewport) {
const cycle = this.isIOS ? IOS_CYCLE_HEIGHT : ANDROID_CYCLE_HEIGHT;
this.viewport = viewport;
this.viewport.setTotalContentSize(cycle * 7);
this.updateRenderedRange(this.viewport);
}
detach() {
this.index$.complete();
this.viewport = null;
this.destroy$.next();
this.destroy$.complete();
}
onContentScrolled() {
if (this.viewport) {
this.updateRenderedRange(this.viewport);
}
}
/** These do not matter for this case */
onDataLengthChanged() { }
onContentRendered() { }
onRenderedOffsetChanged() { }
scrollToIndex(index, behavior) {
if (!this.viewport) {
return;
}
const scrollTop = this.getOffsetForIndex(index);
if (behavior !== 'smooth') {
this.viewport.scrollToOffset(scrollTop, behavior);
return;
}
this.scrollService
.scroll$(this.viewport.elementRef.nativeElement, scrollTop)
.pipe(takeUntil(this.destroy$))
.subscribe();
}
getOffsetForIndex(index) {
const month = index % MONTHS_IN_YEAR;
const year = (index - month) / MONTHS_IN_YEAR;
return this.computeHeight(year, month);
}
getIndexForOffset(offset) {
const cycle = this.isIOS ? IOS_CYCLE : ANDROID_CYCLE;
const cycleHeight = this.isIOS ? IOS_CYCLE_HEIGHT : ANDROID_CYCLE_HEIGHT;
const remainder = offset % cycleHeight;
const years = ((offset - remainder) / cycleHeight) * YEARLY_CYCLE;
let accumulator = 0;
for (let year = 0; year < cycle.length; year++) {
for (let month = 0; month < (cycle[year]?.length ?? 0); month++) {
accumulator += cycle[year]?.[month] ?? 0;
if (accumulator - (cycle[year]?.[month] ?? 0) / 2 > remainder) {
return Math.max((years + year) * MONTHS_IN_YEAR + month, 0);
}
}
}
return RANGE;
}
computeHeight(year, month) {
const cycle = this.isIOS ? IOS_CYCLE : ANDROID_CYCLE;
const remainder = year % YEARLY_CYCLE;
const remainderHeight = reduceCycle(cycle, remainder, month);
const fullCycles = (year - remainder) / YEARLY_CYCLE;
const fullCyclesHeight = this.isIOS
? fullCycles * IOS_CYCLE_HEIGHT
: fullCycles * ANDROID_CYCLE_HEIGHT;
return fullCyclesHeight + remainderHeight;
}
updateRenderedRange(viewport) {
const offset = viewport.measureScrollOffset();
const { start, end } = viewport.getRenderedRange();
const viewportSize = viewport.getViewportSize();
const dataLength = viewport.getDataLength();
const newRange = { start, end };
const firstVisibleIndex = this.getIndexForOffset(offset);
const startBuffer = offset - this.getOffsetForIndex(start);
if (startBuffer < BUFFER && start !== 0) {
newRange.start = Math.max(0, this.getIndexForOffset(offset - BUFFER * 2));
newRange.end = Math.min(dataLength, this.getIndexForOffset(offset + viewportSize + BUFFER));
}
else {
const endBuffer = this.getOffsetForIndex(end) - offset - viewportSize;
if (endBuffer < BUFFER && end !== dataLength) {
newRange.start = Math.max(0, this.getIndexForOffset(offset - BUFFER));
newRange.end = Math.min(dataLength, this.getIndexForOffset(offset + viewportSize + BUFFER * 2));
}
}
viewport.setRenderedRange(newRange);
viewport.setRenderedContentOffset(this.getOffsetForIndex(newRange.start));
this.index$.next(firstVisibleIndex);
}
}
__decorate([
tuiPure
], TuiMobileCalendarStrategy.prototype, "scrolledIndexChange", null);
/**
* Stream for updating value
*/
const TUI_VALUE_STREAM = new InjectionToken(ngDevMode ? 'TUI_VALUE_STREAM' : '');
const TUI_MOBILE_CALENDAR_PROVIDERS = [
TuiScrollService,
{
provide: VIRTUAL_SCROLL_STRATEGY,
deps: [TUI_IS_IOS, TuiScrollService],
useClass: TuiMobileCalendarStrategy,
},
{
provide: TUI_VALUE_STREAM,
deps: [[new Optional(), TUI_CALENDAR_DATE_STREAM]],
useFactory: (value$) => (value$ || EMPTY).pipe(tuiWatch(), takeUntilDestroyed()),
},
];
class TuiMobileCalendar {
constructor() {
this.value$ = new BehaviorSubject(null);
this.today = TuiDay.currentLocal();
this.activeYear = 0;
this.activeMonth = 0;
this.destroyRef = inject(DestroyRef);
this.doc = inject(DOCUMENT);
this.speed = inject(TUI_ANIMATIONS_SPEED);
this.ngZone = inject(NgZone);
this.initialized = false;
this.isIOS = inject(TUI_IS_IOS);
this.isE2E = inject(TUI_IS_E2E);
this.icons = inject(TUI_COMMON_ICONS);
this.closeWord$ = inject(TUI_CLOSE_WORD);
this.cancelWord$ = inject(TUI_CANCEL_WORD);
this.doneWord$ = inject(TUI_DONE_WORD);
this.unorderedWeekDays$ = inject(TUI_SHORT_WEEK_DAYS);
this.chooseDayOrRangeTexts$ = inject(TUI_CHOOSE_DAY_OR_RANGE_TEXTS, {
optional: true,
});
this.years = Array.from({ length: RANGE }, (_, i) => i + STARTING_YEAR);
this.months = Array.from({ length: RANGE * 12 }, (_, i) => new TuiMonth(Math.floor(i / MONTHS_IN_YEAR) + STARTING_YEAR, i % MONTHS_IN_YEAR));
/**
* @deprecated use static DI options instead
* ```
* tuiCalendarSheetOptionsProvider({rangeMode: boolean})
* ```
* TODO(v5): delete it
*/
this.single = !inject(TUI_CALENDAR_SHEET_OPTIONS).rangeMode;
this.multi = false;
this.min = TUI_FIRST_DAY;
this.max = TUI_LAST_DAY;
this.disabledItemHandler = TUI_FALSE_HANDLER;
this.cancel = new EventEmitter();
this.confirm = new EventEmitter();
this.valueChange = this.value$.pipe(skip(1), distinctUntilChanged((a, b) => a?.toString() === b?.toString()), map((x) => (!this.single && x instanceof TuiDay ? new TuiDayRange(x, x) : x)));
this.disabledItemHandlerMapper = (disabledItemHandler, min, max) => (item) => item.dayBefore(min) ||
(max !== null && item.dayAfter(max)) ||
disabledItemHandler(item);
inject(TUI_VALUE_STREAM)
.pipe(takeUntilDestroyed())
.subscribe((value) => {
this.value = value;
});
}
set value(value) {
if (value !== undefined) {
this.value$.next(value);
}
}
get value() {
return this.value$.value;
}
ngAfterViewInit() {
this.activeYear = this.initialYear;
this.activeMonth = this.initialMonth;
// Virtual scroll has not yet rendered items even in ngAfterViewInit
this.waitScrolledChange();
}
setYear(year) {
if (this.activeYear === year) {
return;
}
this.activeMonth += this.getMonthOffset(year);
this.activeYear = year;
this.scrollToActiveYear('smooth');
timer(0, tuiZonefreeScheduler(this.ngZone))
.pipe(tuiZonefree(this.ngZone), takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.scrollToActiveMonth());
}
get yearWidth() {
return this.doc.documentElement.clientWidth / YEARS_IN_ROW;
}
onClose() {
this.cancel.emit();
}
onConfirm() {
if (this.value) {
this.confirm.emit(this.value);
}
else {
this.cancel.emit();
}
}
onDayClick(day) {
if (this.single) {
this.value = day;
}
else if (this.isMultiValue(this.value)) {
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 = day;
}
else if (!this.value) {
this.value = day;
}
}
getState(index) {
if (this.isYearActive(index)) {
return 'active';
}
if (this.isYearActive(index - 1) || this.isYearActive(index + 1)) {
return 'adjacent';
}
return null;
}
onMonthChange(month) {
// Skipping initial callback where index === 0
if (!month || this.activeMonth === month) {
return;
}
this.activeMonth = month;
const activeYear = this.monthToYear(month);
if (activeYear === this.activeYear) {
return;
}
this.activeYear = activeYear;
this.scrollToActiveYear();
}
get initialYear() {
if (!this.value) {
return this.today.year;
}
if (this.value instanceof TuiDay) {
return this.value.year;
}
if (!(this.value instanceof TuiDayRange)) {
return this.value?.[0]?.year ?? this.today.year;
}
return this.value.to.year;
}
get initialMonth() {
if (!this.value) {
return this.today.month + (this.today.year - STARTING_YEAR) * MONTHS_IN_YEAR;
}
if (this.value instanceof TuiDay) {
return this.value.month + (this.value.year - STARTING_YEAR) * MONTHS_IN_YEAR;
}
if (!(this.value instanceof TuiDayRange)) {
return ((this.value?.[0]?.month ?? this.today.month) +
((this.value?.[0]?.year ?? this.today.year) - STARTING_YEAR) *
MONTHS_IN_YEAR);
}
return (this.value.to.month + (this.value.to.year - STARTING_YEAR) * MONTHS_IN_YEAR);
}
isMultiValue(day) {
return !(day instanceof TuiDay) && !(day instanceof TuiDayRange) && this.multi;
}
getYearsViewportSize() {
return this.yearsScrollRef?.getViewportSize() || 0;
}
updateViewportDimension() {
this.yearsScrollRef?.checkViewportSize();
this.monthsScrollRef?.checkViewportSize();
}
lateInit() {
return this.getYearsViewportSize() > 0 ? identity : delay(200);
}
waitScrolledChange() {
this.updateViewportDimension();
this.monthsScrollRef?.scrolledIndexChange
.pipe(delay(tuiGetDuration(this.speed)), this.lateInit(), take(1), takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.initialized = true;
this.updateViewportDimension();
this.initYearScroll();
this.initMonthScroll();
this.scrollToActiveYear();
this.scrollToActiveMonth();
});
}
initYearScroll() {
const { yearsScrollRef } = this;
if (!yearsScrollRef) {
return;
}
const touchstart$ = tuiTypedFromEvent(yearsScrollRef.elementRef.nativeElement, 'touchstart', { passive: true });
const touchend$ = tuiTypedFromEvent(yearsScrollRef.elementRef.nativeElement, 'touchend', { passive: true });
const click$ = tuiTypedFromEvent(yearsScrollRef.elementRef.nativeElement, 'click');
// Refresh activeYear
yearsScrollRef
.elementScrolled()
.pipe(
// Ignore smooth scroll resulting from click on the exact year
windowToggle(touchstart$, () => click$), mergeMap((x) => x),
// Delay is required to run months scroll in the next frame to prevent flicker
delay(0), map(() => Math.round(yearsScrollRef.measureScrollOffset() / this.yearWidth) +
Math.floor(YEARS_IN_ROW / 2) +
STARTING_YEAR), filter((activeYear) => activeYear !== this.activeYear), takeUntilDestroyed(this.destroyRef))
.subscribe((activeYear) => {
this.activeMonth += this.getMonthOffset(activeYear);
this.activeYear = activeYear;
this.scrollToActiveMonth();
});
// Smooth scroll to activeYear after scrolling is done
touchstart$
.pipe(switchMap(() => touchend$), switchMap(() => race(yearsScrollRef.elementScrolled(), timer(SCROLL_DEBOUNCE_TIME, tuiZonefreeScheduler(this.ngZone))).pipe(debounceTime(SCROLL_DEBOUNCE_TIME * 2, tuiZonefreeScheduler(this.ngZone)), take(1), takeUntil(touchstart$))), takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.scrollToActiveYear('smooth'));
}
initMonthScroll() {
const { monthsScrollRef } = this;
if (!monthsScrollRef) {
return;
}
const touchstart$ = tuiTypedFromEvent(monthsScrollRef.elementRef.nativeElement, 'touchstart', { passive: true });
const touchend$ = tuiTypedFromEvent(monthsScrollRef.elementRef.nativeElement, 'touchend', { passive: true });
// Smooth scroll to the closest month after scrolling is done
touchstart$
.pipe(switchMap(() => touchend$), switchMap(() => race(monthsScrollRef.elementScrolled(), timer(SCROLL_DEBOUNCE_TIME, tuiZonefreeScheduler(this.ngZone))).pipe(debounceTime(SCROLL_DEBOUNCE_TIME * 2, tuiZonefreeScheduler(this.ngZone)), take(1), takeUntil(touchstart$))), takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.scrollToActiveMonth('smooth'));
}
scrollToActiveYear(behavior = 'auto') {
this.yearsScrollRef?.scrollToIndex(Math.max(this.activeYear - STARTING_YEAR - 2, 0), this.isE2E ? 'auto' : behavior);
}
scrollToActiveMonth(behavior = 'auto') {
this.monthsScrollRef?.scrollToIndex(this.activeMonth, this.isE2E ? 'auto' : behavior);
}
isYearActive(index) {
return index === this.activeYear;
}
monthToYear(month) {
return Math.ceil((month + 1) / MONTHS_IN_YEAR) + STARTING_YEAR - 1;
}
getMonthOffset(year) {
return (year - this.activeYear) * MONTHS_IN_YEAR;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TuiMobileCalendar, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: TuiMobileCalendar, isStandalone: true, selector: "tui-mobile-calendar", inputs: { single: "single", multi: "multi", min: "min", max: "max", disabledItemHandler: "disabledItemHandler", value: "value" }, outputs: { cancel: "cancel", confirm: "confirm", valueChange: "valueChange" }, host: { listeners: { "mousedown.prevent": "0" }, properties: { "class._ios": "isIOS", "class._initialized": "initialized" } }, providers: TUI_MOBILE_CALENDAR_PROVIDERS, viewQueries: [{ propertyName: "yearsScrollRef", first: true, predicate: ["yearsScrollRef"], descendants: true }, { propertyName: "monthsScrollRef", first: true, predicate: ["monthsScrollRef"], descendants: true }], ngImport: i0, template: "<header\n *ngIf=\"chooseDayOrRangeTexts$ | async as texts\"\n class=\"t-header\"\n>\n <button\n appearance=\"\"\n automation-id=\"tui-mobile-calendar__cancel\"\n tuiIconButton\n tuiRipple\n type=\"button\"\n class=\"t-close\"\n [iconStart]=\"icons.close\"\n [style.border-radius.%]=\"100\"\n (click)=\"onClose()\"\n >\n {{ closeWord$ | async }}\n </button>\n <button\n tuiLink\n tuiTouchable=\"opacity\"\n type=\"button\"\n class=\"t-link t-link_close\"\n (click)=\"onClose()\"\n >\n {{ cancelWord$ | async }}\n </button>\n <h2\n automation-id=\"tui-mobile-calendar__label\"\n class=\"t-label\"\n >\n {{ single ? texts[0] : multi ? texts[2] : texts[1] }}\n </h2>\n <button\n automation-id=\"tui-mobile-calendar__confirm\"\n tuiLink\n tuiTouchable=\"opacity\"\n type=\"button\"\n class=\"t-link\"\n (click)=\"onConfirm()\"\n >\n {{ doneWord$ | async }}\n </button>\n</header>\n<cdk-virtual-scroll-viewport\n #yearsScrollRef\n orientation=\"horizontal\"\n class=\"t-years\"\n [itemSize]=\"yearWidth\"\n>\n <div class=\"t-years-wrapper\">\n <button\n *cdkVirtualFor=\"let index of years\"\n type=\"button\"\n class=\"t-year\"\n [attr.data-state]=\"getState(index)\"\n (click)=\"setYear(index)\"\n >\n {{ index }}\n </button>\n </div>\n</cdk-virtual-scroll-viewport>\n<div class=\"t-week\">\n <div\n *ngFor=\"let day of unorderedWeekDays$ | tuiOrderWeekDays | async\"\n class=\"t-day\"\n >\n {{ day }}\n </div>\n</div>\n<cdk-virtual-scroll-viewport\n #monthsScrollRef\n itemSize=\"354\"\n maxBufferPx=\"5000\"\n minBufferPx=\"0\"\n class=\"t-months\"\n (scrolledIndexChange)=\"onMonthChange($event)\"\n>\n <section\n *cdkVirtualFor=\"let month of months; templateCacheSize: 10\"\n class=\"t-month-wrapper\"\n >\n <h2 class=\"t-month\">{{ month | tuiMonth | async }}</h2>\n <tui-mobile-calendar-sheet\n class=\"t-calendar\"\n [disabledItemHandler]=\"disabledItemHandler | tuiMapper: disabledItemHandlerMapper : min : max\"\n [month]=\"month\"\n [single]=\"single\"\n [value]=\"value\"\n (dayClick)=\"onDayClick($event)\"\n />\n </section>\n</cdk-virtual-scroll-viewport>\n", styles: [":host{display:block;block-size:100%;font-family:-apple-system,BlinkMacSystemFont,Roboto,sans-serif;color:var(--tui-text-primary);overscroll-behavior:none;-webkit-tap-highlight-color:transparent}:host ::ng-deep .t-cell:before,:host ::ng-deep .t-cell:after{transform:translateZ(1px)}.t-header{position:relative;display:flex;align-items:center;block-size:3.5rem;padding:0 1rem;border-block-end:.5px solid var(--tui-border-normal)}:host._ios .t-header{block-size:2.75rem;border-block-end:none}.t-close{inline-size:1.5rem;block-size:1.5rem;margin-inline-end:2rem}:host._ios .t-close{display:none}.t-link{margin-inline-start:auto;flex-shrink:0;font-size:.875rem;line-height:1rem;font-weight:500;text-transform:uppercase;color:var(--tui-text-action)}.t-link_close{display:none}:host._ios .t-link{min-inline-size:3.75rem;text-align:end;font-size:.9375rem;line-height:1.125rem;font-weight:400;letter-spacing:.018125rem;text-transform:none}:host._ios .t-link_close{display:block;margin:0 auto 0 0;text-align:start}.t-label{flex-grow:1;margin:0;font-size:1.25rem;line-height:3.5rem;font-weight:500;letter-spacing:-.0125rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.t-label+.t-link{padding-inline-start:1rem}:host._ios .t-label{font-size:1.0625rem;font-weight:600;letter-spacing:-.025625rem;text-align:center}.t-years{scrollbar-width:none;-ms-overflow-style:none;block-size:4.0625rem;background-color:var(--tui-background-base);box-shadow:0 .5px var(--tui-border-normal)}.t-years::-webkit-scrollbar,.t-years::-webkit-scrollbar-thumb{display:none}:host._ios .t-years{background-color:transparent}.t-years-wrapper{display:flex;block-size:4.0625rem}.t-year{-webkit-appearance:none;appearance:none;padding:0;border:0;background:none;font:inherit;line-height:inherit;text-decoration:none;outline:none;inline-size:20vw;flex-shrink:0;font-size:.9375rem;font-weight:700;letter-spacing:.015625rem;cursor:pointer;opacity:.1;transform:scale(.73);transition:color,opacity,transform .2s}.t-year[data-state=adjacent]{transform:scale(.86);opacity:.3}.t-year[data-state=active]{color:var(--tui-text-action);opacity:1;transform:scale(1)}.t-week{display:flex;align-items:center;block-size:1.875rem;inline-size:20.75rem;max-inline-size:100%;margin:0 auto;font-size:.75rem}:host._ios .t-week{inline-size:22.625rem;font-size:.6875rem;font-weight:500;color:var(--tui-text-secondary)}.t-day{flex:1;text-align:center}.t-months{scrollbar-width:none;-ms-overflow-style:none;block-size:calc(100% - 9.5rem);box-shadow:0 -1px var(--tui-border-normal);overflow-x:hidden}.t-months::-webkit-scrollbar,.t-months::-webkit-scrollbar-thumb{display:none}:host._ios .t-months{block-size:calc(100% - 8.75rem)}.t-month-wrapper{display:flex;margin:.625rem 0 -.625rem;block-size:22.125rem;flex-direction:column}.t-month{block-size:2.75rem;inline-size:100%;line-height:2.75rem;padding-inline-start:1rem;font-size:.875rem;font-weight:500;text-transform:uppercase;margin:0 0 1.25rem;box-sizing:border-box;border-block-end:.5px solid var(--tui-border-normal)}:host._ios .t-month{block-size:3.125rem;margin:0;border-block-end:none;text-transform:none;font-size:1.375rem;line-height:3.125rem;letter-spacing:.02rem;font-weight:700}.t-calendar{margin:0 auto;transition:opacity .2s}.t-week,.t-years,.t-months{transition-property:opacity;transition-duration:var(--tui-duration, .3s);transition-timing-function:ease-in-out;opacity:0}:host(._initialized) .t-week,:host(._initialized) .t-years,:host(._initialized) .t-months{opacity:1}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }, { kind: "pipe", type: TuiMapperPipe, name: "tuiMapper" }, { kind: "component", type: TuiMobileCalendarSheet, selector: "tui-mobile-calendar-sheet" }, { kind: "pipe", type: TuiMonthPipe, name: "tuiMonth" }, { kind: "pipe", type: TuiOrderWeekDaysPipe, name: "tuiOrderWeekDays" }, { kind: "directive", type: TuiRipple, selector: "[tuiRipple]", inputs: ["tuiRipple"] }, { kind: "directive", type: TuiTouchable, selector: "[tuiTouchable]", inputs: ["tuiTouchable"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TuiMobileCalendar, decorators: [{
type: Component,
args: [{ standalone: true, selector: 'tui-mobile-calendar', imports: [
AsyncPipe,
CdkFixedSizeVirtualScroll,
CdkVirtualForOf,
CdkVirtualScrollViewport,
NgForOf,
NgIf,
TuiButton,
TuiLink,
TuiMapperPipe,
TuiMobileCalendarSheet,
TuiMonthPipe,
TuiOrderWeekDaysPipe,
TuiRipple,
TuiTouchable,
], changeDetection: ChangeDetectionStrategy.OnPush, providers: TUI_MOBILE_CALENDAR_PROVIDERS, host: {
'[class._ios]': 'isIOS',
'[class._initialized]': 'initialized',
'(mousedown.prevent)': '0',
}, template: "<header\n *ngIf=\"chooseDayOrRangeTexts$ | async as texts\"\n class=\"t-header\"\n>\n <button\n appearance=\"\"\n automation-id=\"tui-mobile-calendar__cancel\"\n tuiIconButton\n tuiRipple\n type=\"button\"\n class=\"t-close\"\n [iconStart]=\"icons.close\"\n [style.border-radius.%]=\"100\"\n (click)=\"onClose()\"\n >\n {{ closeWord$ | async }}\n </button>\n <button\n tuiLink\n tuiTouchable=\"opacity\"\n type=\"button\"\n class=\"t-link t-link_close\"\n (click)=\"onClose()\"\n >\n {{ cancelWord$ | async }}\n </button>\n <h2\n automation-id=\"tui-mobile-calendar__label\"\n class=\"t-label\"\n >\n {{ single ? texts[0] : multi ? texts[2] : texts[1] }}\n </h2>\n <button\n automation-id=\"tui-mobile-calendar__confirm\"\n tuiLink\n tuiTouchable=\"opacity\"\n type=\"button\"\n class=\"t-link\"\n (click)=\"onConfirm()\"\n >\n {{ doneWord$ | async }}\n </button>\n</header>\n<cdk-virtual-scroll-viewport\n #yearsScrollRef\n orientation=\"horizontal\"\n class=\"t-years\"\n [itemSize]=\"yearWidth\"\n>\n <div class=\"t-years-wrapper\">\n <button\n *cdkVirtualFor=\"let index of years\"\n type=\"button\"\n class=\"t-year\"\n [attr.data-state]=\"getState(index)\"\n (click)=\"setYear(index)\"\n >\n {{ index }}\n </button>\n </div>\n</cdk-virtual-scroll-viewport>\n<div class=\"t-week\">\n <div\n *ngFor=\"let day of unorderedWeekDays$ | tuiOrderWeekDays | async\"\n class=\"t-day\"\n >\n {{ day }}\n </div>\n</div>\n<cdk-virtual-scroll-viewport\n #monthsScrollRef\n itemSize=\"354\"\n maxBufferPx=\"5000\"\n minBufferPx=\"0\"\n class=\"t-months\"\n (scrolledIndexChange)=\"onMonthChange($event)\"\n>\n <section\n *cdkVirtualFor=\"let month of months; templateCacheSize: 10\"\n class=\"t-month-wrapper\"\n >\n <h2 class=\"t-month\">{{ month | tuiMonth | async }}</h2>\n <tui-mobile-calendar-sheet\n class=\"t-calendar\"\n [disabledItemHandler]=\"disabledItemHandler | tuiMapper: disabledItemHandlerMapper : min : max\"\n [month]=\"month\"\n [single]=\"single\"\n [value]=\"value\"\n (dayClick)=\"onDayClick($event)\"\n />\n </section>\n</cdk-virtual-scroll-viewport>\n", styles: [":host{display:block;block-size:100%;font-family:-apple-system,BlinkMacSystemFont,Roboto,sans-serif;color:var(--tui-text-primary);overscroll-behavior:none;-webkit-tap-highlight-color:transparent}:host ::ng-deep .t-cell:before,:host ::ng-deep .t-cell:after{transform:translateZ(1px)}.t-header{position:relative;display:flex;align-items:center;block-size:3.5rem;padding:0 1rem;border-block-end:.5px solid var(--tui-border-normal)}:host._ios .t-header{block-size:2.75rem;border-block-end:none}.t-close{inline-size:1.5rem;block-size:1.5rem;margin-inline-end:2rem}:host._ios .t-close{display:none}.t-link{margin-inline-start:auto;flex-shrink:0;font-size:.875rem;line-height:1rem;font-weight:500;text-transform:uppercase;color:var(--tui-text-action)}.t-link_close{display:none}:host._ios .t-link{min-inline-size:3.75rem;text-align:end;font-size:.9375rem;line-height:1.125rem;font-weight:400;letter-spacing:.018125rem;text-transform:none}:host._ios .t-link_close{display:block;margin:0 auto 0 0;text-align:start}.t-label{flex-grow:1;margin:0;font-size:1.25rem;line-height:3.5rem;font-weight:500;letter-spacing:-.0125rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.t-label+.t-link{padding-inline-start:1rem}:host._ios .t-label{font-size:1.0625rem;font-weight:600;letter-spacing:-.025625rem;text-align:center}.t-years{scrollbar-width:none;-ms-overflow-style:none;block-size:4.0625rem;background-color:var(--tui-background-base);box-shadow:0 .5px var(--tui-border-normal)}.t-years::-webkit-scrollbar,.t-years::-webkit-scrollbar-thumb{display:none}:host._ios .t-years{background-color:transparent}.t-years-wrapper{display:flex;block-size:4.0625rem}.t-year{-webkit-appearance:none;appearance:none;padding:0;border:0;background:none;font:inherit;line-height:inherit;text-decoration:none;outline:none;inline-size:20vw;flex-shrink:0;font-size:.9375rem;font-weight:700;letter-spacing:.015625rem;cursor:pointer;opacity:.1;transform:scale(.73);transition:color,opacity,transform .2s}.t-year[data-state=adjacent]{transform:scale(.86);opacity:.3}.t-year[data-state=active]{color:var(--tui-text-action);opacity:1;transform:scale(1)}.t-week{display:flex;align-items:center;block-size:1.875rem;inline-size:20.75rem;max-inline-size:100%;margin:0 auto;font-size:.75rem}:host._ios .t-week{inline-size:22.625rem;font-size:.6875rem;font-weight:500;color:var(--tui-text-secondary)}.t-day{flex:1;text-align:center}.t-months{scrollbar-width:none;-ms-overflow-style:none;block-size:calc(100% - 9.5rem);box-shadow:0 -1px var(--tui-border-normal);overflow-x:hidden}.t-months::-webkit-scrollbar,.t-months::-webkit-scrollbar-thumb{display:none}:host._ios .t-months{block-size:calc(100% - 8.75rem)}.t-month-wrapper{display:flex;margin:.625rem 0 -.625rem;block-size:22.125rem;flex-direction:column}.t-month{block-size:2.75rem;inline-size:100%;line-height:2.75rem;padding-inline-start:1rem;font-size:.875rem;font-weight:500;text-transform:uppercase;margin:0 0 1.25rem;box-sizing:border-box;border-block-end:.5px solid var(--tui-border-normal)}:host._ios .t-month{block-size:3.125rem;margin:0;border-block-end:none;text-transform:none;font-size:1.375rem;line-height:3.125rem;letter-spacing:.02rem;font-weight:700}.t-calendar{margin:0 auto;transition:opacity .2s}.t-week,.t-years,.t-months{transition-property:opacity;transition-duration:var(--tui-duration, .3s);transition-timing-function:ease-in-out;opacity:0}:host(._initialized) .t-week,:host(._initialized) .t-years,:host(._initialized) .t-months{opacity:1}\n"] }]
}], ctorParameters: function () { return []; }, propDecorators: { yearsScrollRef: [{
type: ViewChild,
args: ['yearsScrollRef']
}], monthsScrollRef: [{
type: ViewChild,
args: ['monthsScrollRef']
}], single: [{
type: Input
}], multi: [{
type: Input
}], min: [{
type: Input
}], max: [{
type: Input
}], disabledItemHandler: [{
type: Input
}], cancel: [{
type: Output
}], confirm: [{
type: Output
}], valueChange: [{
type: Output
}], value: [{
type: Input
}] } });
/**
* Generated bundle index. Do not edit.
*/
export { ANDROID_CYCLE, ANDROID_LABEL, ANDROID_WEEK, BUFFER, IOS_CYCLE, IOS_LABEL, IOS_WEEK, RANGE, SCROLL_DEBOUNCE_TIME, STARTING_YEAR, TUI_MOBILE_CALENDAR_PROVIDERS, TUI_VALUE_STREAM, TuiMobileCalendar, TuiMobileCalendarStrategy, YEARLY_CYCLE, YEARS_IN_ROW };
//# sourceMappingURL=taiga-ui-addon-mobile-components-mobile-calendar.mjs.map