Skip to content

Commit

Permalink
feat(addon-mobile): ResponsiveDialog add new service and refactor `…
Browse files Browse the repository at this point in the history
…SheetDialog` to new spec (#9459)

Co-authored-by: taiga-family-bot <taiga-family-bot@users.noreply.github.com>
  • Loading branch information
waterplea and taiga-family-bot authored Oct 17, 2024
1 parent 2598626 commit 10198a1
Show file tree
Hide file tree
Showing 56 changed files with 587 additions and 237 deletions.
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions projects/addon-doc/components/language-switcher/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class TuiDocLanguageSwitcher {
['japan', 'JP'],
['kazakh', 'KZ'],
['korean', 'KR'],
['lithuanian', 'LT'],
['malay', 'MY'],
['polish', 'PL'],
['portuguese', 'PT'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {DOCUMENT, NgForOf, NgIf, NgTemplateOutlet} from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
inject,
Expand Down Expand Up @@ -115,7 +114,7 @@ export class TuiDocNavigation {
const readyToScroll$ = inject(TUI_DOC_PAGE_LOADED);

inject(NAVIGATION_TITLE)
.pipe(tuiWatch(inject(ChangeDetectorRef)), takeUntilDestroyed())
.pipe(tuiWatch(), takeUntilDestroyed())
.subscribe((title) => {
titleService.setTitle(title);
this.openActivePageGroup();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {VIRTUAL_SCROLL_STRATEGY} from '@angular/cdk/scrolling';
import type {Provider} from '@angular/core';
import {ChangeDetectorRef, Optional} from '@angular/core';
import {Optional} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import type {TuiDayRange} from '@taiga-ui/cdk/date-time';
import {tuiWatch} from '@taiga-ui/cdk/observables';
Expand All @@ -27,11 +27,10 @@ export const TUI_MOBILE_CALENDAR_PROVIDERS: Provider[] = [
},
{
provide: TUI_VALUE_STREAM,
deps: [[new Optional(), TUI_CALENDAR_DATE_STREAM], ChangeDetectorRef],
deps: [[new Optional(), TUI_CALENDAR_DATE_STREAM]],
useFactory: (
value$: Observable<TuiDayRange | null> | null,
cdr: ChangeDetectorRef,
): Observable<TuiDayRange | null> =>
(value$ || EMPTY).pipe(tuiWatch(cdr), takeUntilDestroyed()),
(value$ || EMPTY).pipe(tuiWatch(), takeUntilDestroyed()),
},
];
Original file line number Diff line number Diff line change
@@ -1,73 +1,45 @@
import {AsyncPipe, NgForOf, NgIf} from '@angular/common';
import {NgForOf, NgIf} from '@angular/common';
import type {AfterViewInit, ElementRef, QueryList} from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
inject,
ViewChild,
ViewChildren,
} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {ChangeDetectionStrategy, Component, inject, ViewChildren} from '@angular/core';
import {EMPTY_QUERY} from '@taiga-ui/cdk/constants';
import {TuiClickOutside} from '@taiga-ui/cdk/directives/click-outside';
import type {TuiPopover} from '@taiga-ui/cdk/services';
import {tuiInjectElement} from '@taiga-ui/cdk/utils/dom';
import {tuiPure} from '@taiga-ui/cdk/utils/miscellaneous';
import {tuiSlideInTop} from '@taiga-ui/core/animations';
import {TuiButton} from '@taiga-ui/core/components/button';
import {
TUI_ANIMATIONS_SPEED,
TUI_CLOSE_WORD,
TUI_COMMON_ICONS,
} from '@taiga-ui/core/tokens';
import {TUI_ANIMATIONS_SPEED} from '@taiga-ui/core/tokens';
import {tuiGetDuration} from '@taiga-ui/core/utils/miscellaneous';
import {shouldCall} from '@taiga-ui/event-plugins';
import {
injectContext,
PolymorpheusOutlet,
PolymorpheusTemplate,
} from '@taiga-ui/polymorpheus';
import {BehaviorSubject} from 'rxjs';
import {injectContext, PolymorpheusOutlet} from '@taiga-ui/polymorpheus';

import type {TuiSheetDialogOptions} from './sheet-dialog.options';

// So we re-enter ngZone and trigger change detection
function isCloseable(this: TuiSheetDialogComponent<unknown>): boolean {
return this.context.closeable;
return this.context.closeable === true;
}

@Component({
standalone: true,
selector: 'tui-sheet-dialog',
imports: [
AsyncPipe,
NgForOf,
NgIf,
PolymorpheusOutlet,
PolymorpheusTemplate,
TuiButton,
TuiClickOutside,
],
imports: [NgForOf, NgIf, PolymorpheusOutlet, TuiButton],
templateUrl: './sheet-dialog.template.html',
styleUrls: ['./sheet-dialog.style.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [tuiSlideInTop],
host: {
'[@tuiSlideInTop]': 'slideInTop',
'[style.top.px]': 'offset',
'[class._closeable]': 'closeable',
'[style.--tui-offset.px]': 'context.offset',
'[class._closeable]': 'context.closeable === true',
'(document:touchstart.passive.silent)': 'onPointerChange(1)',
'(document:touchend.silent)': 'onPointerChange(-1)',
'(document:touchcancel.silent)': 'onPointerChange(-1)',
'(scroll.silent)': 'onPointerChange(0)',
'(click.self)': 'close()',
},
})
export class TuiSheetDialogComponent<I> implements AfterViewInit {
@ViewChild('sheet')
private readonly sheet?: ElementRef<HTMLElement>;

@ViewChildren('stops')
private readonly stopsRefs: QueryList<ElementRef<HTMLElement>> = EMPTY_QUERY;
private readonly stops: QueryList<ElementRef<HTMLElement>> = EMPTY_QUERY;

private readonly el = tuiInjectElement();
private pointers = 0;
Expand All @@ -80,65 +52,27 @@ export class TuiSheetDialogComponent<I> implements AfterViewInit {
},
};

protected readonly stuck$ = new BehaviorSubject(false);

protected readonly stuck$$ = this.stuck$
.pipe(takeUntilDestroyed())
.subscribe((add) =>
add ? this.el.classList.add('_stuck') : this.el.classList.remove('_stuck'),
);

protected readonly icons = inject(TUI_COMMON_ICONS);
protected readonly closeWord$ = inject(TUI_CLOSE_WORD);
protected readonly context =
injectContext<TuiPopover<TuiSheetDialogOptions<I>, any>>();

public ngAfterViewInit(): void {
this.el.scrollTop =
[...this.getStops(this.stopsRefs), this.sheetTop][this.context.initial] ?? 0;
}

protected get offset(): number {
return this.context.offset;
}

protected get closeable(): boolean {
return this.context.closeable;
}

protected get isSmall(): boolean {
return this.sheetTop > (this.sheet?.nativeElement.clientHeight || Infinity);
[
...this.stops.map((e) => e.nativeElement.offsetTop - this.context.offset),
this.el.clientHeight ?? Infinity,
][this.context.initial] ?? 0;
}

@shouldCall(isCloseable)
protected close(): void {
// TODO: Refactor focus visible on mobile
this.el.dispatchEvent(new Event('mousedown', {bubbles: true}));
this.context.$implicit.complete();
}

protected onPointerChange(delta: number): void {
this.pointers += delta;

if (!delta) {
const stuck = this.el.scrollTop > this.sheetTop;

this.stuck$.value !== stuck && this.stuck$.next(stuck);
}

if (this.context.closeable && !this.pointers && !this.el.scrollTop) {
if (!this.pointers && this.el.scrollTop <= 0) {
this.close();
}
}

private get sheetTop(): number {
return this.sheet?.nativeElement.offsetTop ?? Infinity;
}

@tuiPure
private getStops(stops: QueryList<ElementRef<HTMLElement>>): readonly number[] {
return stops.map(
({nativeElement}) => nativeElement.offsetTop + nativeElement.clientHeight,
);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import type {Provider} from '@angular/core';
import type {TuiPopover} from '@taiga-ui/cdk/services';
import {tuiCreateToken, tuiProvideOptions} from '@taiga-ui/cdk/utils/miscellaneous';
import type {PolymorpheusContent} from '@taiga-ui/polymorpheus';
import type {Observable} from 'rxjs';

export interface TuiSheetDialogOptions<I = undefined> {
readonly closeable: boolean;
readonly closeable: Observable<boolean> | boolean;
readonly data: I;
readonly initial: number;
readonly label: PolymorpheusContent;
readonly label: PolymorpheusContent<TuiPopover<TuiSheetDialogOptions<I>, any>>;
readonly offset: number;
readonly stops: readonly string[];
readonly bar: boolean;
}

export const TUI_SHEET_DIALOG_DEFAULT_OPTIONS: TuiSheetDialogOptions = {
Expand All @@ -18,6 +21,7 @@ export const TUI_SHEET_DIALOG_DEFAULT_OPTIONS: TuiSheetDialogOptions = {
offset: 16,
closeable: true,
data: undefined,
bar: true,
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import {inject, Injectable} from '@angular/core';
import {TuiPopoverService} from '@taiga-ui/cdk/services';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {TuiPopoverService, TuiThemeColorService} from '@taiga-ui/cdk/services';
import {TUI_DIALOGS} from '@taiga-ui/core/components/dialog';
import {pairwise, startWith} from 'rxjs';

import {TuiSheetDialogComponent} from './sheet-dialog.component';
import type {TuiSheetDialogOptions} from './sheet-dialog.options';
import {TUI_SHEET_DIALOG_OPTIONS} from './sheet-dialog.options';

const THEME = '#404040';

@Injectable({
providedIn: 'root',
useFactory: () =>
Expand All @@ -15,6 +19,20 @@ import {TUI_SHEET_DIALOG_OPTIONS} from './sheet-dialog.options';
inject(TUI_SHEET_DIALOG_OPTIONS),
),
})
export class TuiSheetDialogService extends TuiPopoverService<
TuiSheetDialogOptions<any>
> {}
export class TuiSheetDialogService extends TuiPopoverService<TuiSheetDialogOptions<any>> {
private readonly theme = inject(TuiThemeColorService);
private initial = this.theme.color;

protected readonly $ = this.items$
.pipe(startWith([]), pairwise(), takeUntilDestroyed())
.subscribe(([prev, next]) => {
if (!prev.length && next.length) {
this.initial = this.theme.color;
this.theme.color = THEME;
}

if (!next.length && prev.length) {
this.theme.color = this.initial;
}
});
}
Loading

0 comments on commit 10198a1

Please sign in to comment.