Skip to content

Commit

Permalink
feat(addon-mobile): BottomSheet add new component (#10342)
Browse files Browse the repository at this point in the history
Co-authored-by: taiga-family-bot <[email protected]>
  • Loading branch information
waterplea and taiga-family-bot authored Feb 12, 2025
1 parent 86c26c8 commit 1dac30b
Show file tree
Hide file tree
Showing 25 changed files with 671 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Inject,
Input,
QueryList,
ViewChild,
ViewChildren,
} from '@angular/core';
import {EMPTY_QUERY} from '@taiga-ui/cdk';

const OPTIONS = {
duration: 20,
easing: 'ease-in',
fill: 'forwards',
} as const;

@Component({
selector: 'tui-bottom-sheet',
templateUrl: './bottom-sheet.template.html',
styleUrls: ['./bottom-sheet.style.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[style.--t-start]': 'stops[0]',
'[style.scroll-snap-type]': 'stops.length > 1 ? "y mandatory" : null',
'(scroll.silent)': 'onScroll()',
},
})
export class TuiBottomSheetComponent {
@ViewChildren('stops')
private readonly elements: QueryList<ElementRef<HTMLElement>> = EMPTY_QUERY;

@ViewChild('content')
private readonly content?: ElementRef<HTMLElement>;

@Input()
stops: readonly string[] = ['1.5rem'];

constructor(@Inject(ElementRef) private readonly el: ElementRef<HTMLElement>) {}

onScroll(): void {
const {clientHeight, scrollTop, scrollHeight} = this.el.nativeElement;
const top = this.elements.get(0)?.nativeElement.clientHeight || 0;
const max = this.content?.nativeElement.clientHeight || Infinity;
const height = Math.min(clientHeight, max);
const scrolled = Math.min(scrollTop, height - top);
const transform = `translate3d(0, ${-1 * scrolled}px, 0)`;

this.el.nativeElement.style.setProperty('--t-height', `${scrollHeight}px`);
this.el.nativeElement.animate([{transform}], OPTIONS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';

import {TuiBottomSheetComponent} from './bottom-sheet.component';

@NgModule({
imports: [CommonModule],
declarations: [TuiBottomSheetComponent],
exports: [TuiBottomSheetComponent],
})
export class TuiBottomSheetModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
@import '@taiga-ui/core/styles/taiga-ui-local';

/* stylelint-disable */
:host {
.scrollbar-hidden();

position: absolute;
top: 100%;
left: 0;
right: 0;
display: block;
block-size: calc(100% - 1rem);
max-inline-size: 40rem;
margin: calc(-1 * var(--t-start)) auto 0;
background: var(--tui-elevation-01);
border-top-left-radius: var(--tui-radius-l);
border-top-right-radius: var(--tui-radius-l);
overflow: auto;
box-shadow:
0 1rem var(--tui-elevation-01),
var(--tui-shadow);

&::before {
content: '';
position: sticky;
top: 0.625rem;
left: 50%;
z-index: 1;
display: block;
inline-size: 2rem;
block-size: 0.25rem;
margin-block-end: -0.25rem;
transform: translateX(-50%);
background: var(--tui-elevation-01);
box-shadow:
0 0 0 20rem var(--tui-elevation-01),
inset 0 0 0 1rem var(--tui-base-03);
border-radius: 1rem;
clip-path: polygon(-100vw -1rem, 100vw -1rem, 100vw calc(100% + 0.625rem), -100vw calc(100% + 0.625rem));
}

&::after {
content: '';
position: absolute;
top: calc(100% - var(--t-start));
inline-size: 1rem;
block-size: calc(var(--t-height) - 100% + var(--t-start));
scroll-snap-align: start;
scroll-snap-stop: always;
}
}

.t-content {
position: sticky;
top: 0;
padding: 2.25rem 1rem 1.5rem;
}

.t-bottom {
block-size: calc(100% - var(--t-start));
}

.t-stop {
position: absolute;
block-size: var(--t-start);
inline-size: 1rem;
pointer-events: none;
transform: translateY(-100%);
scroll-snap-align: start;
scroll-snap-stop: always;

&:last-child {
scroll-snap-align: none;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div
*ngFor="let stop of stops"
#stops
class="t-stop"
[style.top]="stop"
></div>
<div
#content
class="t-content"
>
<ng-content></ng-content>
</div>
<div class="t-bottom"></div>
2 changes: 2 additions & 0 deletions projects/addon-mobile/components/bottom-sheet/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './bottom-sheet.component';
export * from './bottom-sheet.module';
5 changes: 5 additions & 0 deletions projects/addon-mobile/components/bottom-sheet/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"lib": {
"entryFile": "index.ts"
}
}
1 change: 1 addition & 0 deletions projects/addon-mobile/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from '@taiga-ui/addon-mobile/components/app-bar';
export * from '@taiga-ui/addon-mobile/components/bottom-sheet';
export * from '@taiga-ui/addon-mobile/components/mobile-calendar';
export * from '@taiga-ui/addon-mobile/components/mobile-calendar-dialog';
export * from '@taiga-ui/addon-mobile/components/mobile-dialog';
Expand Down
1 change: 1 addition & 0 deletions projects/demo-playwright/tests/demo/is-flaky-examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const FLAKY_EXAMPLES = new Map<string, number[]>([
['/components/select', [4]], // Imitating server response (delay(3000))
['/components/table', [3]], // Imitating server response (delay(3000))
['/components/tiles', [0]], // YouTube iframe player
['/components/bottom-sheet', [1]], // Google maps
['/icons/customization', [0]], // TODO: investigate flaky test
['/navigation/stepper', [2]], // TODO: flaky test for proprietary demo (autoscroll problems)
['/navigation/tab-bar', [3]], // Imitating server response (timer(3000))
Expand Down
9 changes: 9 additions & 0 deletions projects/demo/src/modules/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,15 @@ export const ROUTES: Routes = [
title: 'BadgedContent',
},
},
{
path: 'components/bottom-sheet',
loadChildren: async () =>
(await import('../components/bottom-sheet/bottom-sheet.module'))
.ExampleTuiBottomSheetModule,
data: {
title: 'BottomSheet',
},
},
{
path: 'layout/block-status',
loadChildren: async () =>
Expand Down
6 changes: 6 additions & 0 deletions projects/demo/src/modules/app/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ export const pages: TuiDocPages = [
keywords: 'блок, статус, block, status, block-status, blockstatus, layout',
route: '/layout/block-status',
},
{
section: 'Components',
title: 'BottomSheet',
keywords: 'mobile, dialog, popup, map, details, шторка, sheet',
route: '/components/bottom-sheet',
},
{
section: 'Components',
title: 'Button',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {Component} from '@angular/core';
import {changeDetection} from '@demo/emulate/change-detection';
import {TuiDocExample} from '@taiga-ui/addon-doc';

@Component({
selector: 'example-bottom-sheet',
templateUrl: './bottom-sheet.template.html',
changeDetection,
})
export class ExampleTuiBottomSheetComponent {
readonly exampleModule = import('./examples/import/import-module.md?raw');
readonly exampleHtml = import('./examples/import/insert-template.md?raw');

readonly example1: TuiDocExample = {
TypeScript: import('./examples/1/index.ts?raw'),
HTML: import('./examples/1/index.html?raw'),
LESS: import('./examples/1/index.less?raw'),
};

readonly example2: TuiDocExample = {
TypeScript: import('./examples/2/index.ts?raw'),
HTML: import('./examples/2/index.html?raw'),
LESS: import('./examples/2/index.less?raw'),
};

readonly example3: TuiDocExample = {
TypeScript: import('./examples/3/index.ts?raw'),
HTML: import('./examples/3/index.html?raw'),
LESS: import('./examples/3/index.less?raw'),
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {tuiGetDocModules} from '@taiga-ui/addon-doc';
import {TuiBottomSheetModule} from '@taiga-ui/addon-mobile';
import {TuiRepeatTimesModule} from '@taiga-ui/cdk';
import {TuiScrollbarModule} from '@taiga-ui/core';
import {
TuiAppearanceModule,
TuiButtonModule,
TuiCardModule,
TuiHeaderModule,
TuiTitleModule,
} from '@taiga-ui/experimental';
import {TuiTextareaModule} from '@taiga-ui/kit';

import {ExampleTuiBottomSheetComponent} from './bottom-sheet.component';
import {TuiBottomSheetExample1} from './examples/1';
import {TuiBottomSheetExample2} from './examples/2';
import {TuiBottomSheetExample3} from './examples/3';

@NgModule({
imports: [
CommonModule,
FormsModule,
TuiAppearanceModule,
TuiBottomSheetModule,
TuiButtonModule,
TuiCardModule,
TuiScrollbarModule,
TuiTextareaModule,
TuiHeaderModule,
TuiTitleModule,
TuiRepeatTimesModule,
tuiGetDocModules(ExampleTuiBottomSheetComponent),
],
declarations: [
ExampleTuiBottomSheetComponent,
TuiBottomSheetExample1,
TuiBottomSheetExample2,
TuiBottomSheetExample3,
],
exports: [ExampleTuiBottomSheetComponent],
})
export class ExampleTuiBottomSheetModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<tui-doc-page
header="BottomSheet"
package="ADDON-MOBILE"
type="components"
>
<ng-template pageTab>
<p>A non-modal draggable sheet</p>

<tui-doc-example
id="basic"
heading="Basic"
[content]="example1"
>
<tui-bottom-sheet-example-1></tui-bottom-sheet-example-1>
</tui-doc-example>

<tui-doc-example
id="scroll"
heading="Reacting to scroll"
[content]="example2"
>
<tui-bottom-sheet-example-2></tui-bottom-sheet-example-2>
</tui-doc-example>

<tui-doc-example
id="stops"
heading="Stops"
[content]="example3"
>
<tui-bottom-sheet-example-3></tui-bottom-sheet-example-3>
</tui-doc-example>
</ng-template>

<ng-template pageTab="Setup">
<ol class="b-demo-steps">
<li>
<p>Import:</p>

<tui-doc-code
filename="my.module.ts"
[code]="exampleModule"
></tui-doc-code>
</li>

<li>
<p>Add to the template:</p>

<tui-doc-code
filename="my.component.html"
[code]="exampleHtml"
></tui-doc-code>
</li>
</ol>
</ng-template>
</tui-doc-page>
Loading

0 comments on commit 1dac30b

Please sign in to comment.