diff --git a/projects/core/components/root/root.component.ts b/projects/core/components/root/root.component.ts index 00d7dad427025..8a2dba066ea1d 100644 --- a/projects/core/components/root/root.component.ts +++ b/projects/core/components/root/root.component.ts @@ -16,7 +16,10 @@ import {tuiWatch, tuiZonefreeScheduler} from '@taiga-ui/cdk/observables'; import {TUI_IS_MOBILE} from '@taiga-ui/cdk/tokens'; import {TuiAlerts} from '@taiga-ui/core/components/alert'; import {TUI_DIALOGS, TuiDialogs} from '@taiga-ui/core/components/dialog'; -import {TuiScrollControls} from '@taiga-ui/core/components/scrollbar'; +import { + TUI_SCROLLBAR_OPTIONS, + TuiScrollControls, +} from '@taiga-ui/core/components/scrollbar'; import {TuiDropdowns} from '@taiga-ui/core/directives'; import {TuiHints} from '@taiga-ui/core/directives/hint'; import {TuiBreakpointService} from '@taiga-ui/core/services'; @@ -57,15 +60,18 @@ export class TuiRoot { {initialValue: false}, ); - protected readonly scrollbars = inject(TUI_IS_MOBILE) - ? signal(false) - : toSignal( - inject>(TUI_DIALOGS).pipe( - map(({length}) => !length), - debounceTime(0, tuiZonefreeScheduler()), - ), - {initialValue: false}, - ); + protected readonly nativeScrollbar = inject(TUI_SCROLLBAR_OPTIONS).nativeScrollbar; + + protected readonly scrollbars = + this.nativeScrollbar || inject(TUI_IS_MOBILE) + ? signal(false) + : toSignal( + inject>(TUI_DIALOGS).pipe( + map(({length}) => !length), + debounceTime(0, tuiZonefreeScheduler()), + ), + {initialValue: false}, + ); constructor() { inject(DOCUMENT).documentElement.setAttribute( @@ -73,6 +79,12 @@ export class TuiRoot { inject(TUI_THEME).toLowerCase(), ); + if (!this.nativeScrollbar) { + inject(DOCUMENT).defaultView?.document.documentElement.classList.add( + 'tui-zero-scrollbar', + ); + } + ngDevMode && console.assert( !!inject(EVENT_MANAGER_PLUGINS).find( diff --git a/projects/core/components/root/root.style.less b/projects/core/components/root/root.style.less index bd5b193a3d9e7..5d14f041309c8 100644 --- a/projects/core/components/root/root.style.less +++ b/projects/core/components/root/root.style.less @@ -13,7 +13,6 @@ } } -html[data-tui-theme], .tui-zero-scrollbar { .scrollbar-hidden(); } diff --git a/projects/core/components/scrollbar/scroll-controls.component.ts b/projects/core/components/scrollbar/scroll-controls.component.ts index f2dff9b5ae722..f6ce3a851c257 100644 --- a/projects/core/components/scrollbar/scroll-controls.component.ts +++ b/projects/core/components/scrollbar/scroll-controls.component.ts @@ -8,6 +8,7 @@ import {tuiToAnimationOptions} from '@taiga-ui/core/utils'; import {distinctUntilChanged, map, startWith, throttleTime} from 'rxjs'; import {TuiScrollbarDirective} from './scrollbar.directive'; +import {TUI_SCROLLBAR_OPTIONS} from './scrollbar.options'; @Component({ standalone: true, @@ -21,7 +22,11 @@ import {TuiScrollbarDirective} from './scrollbar.directive'; export class TuiScrollControls { private readonly scrollRef = inject(TUI_SCROLL_REF).nativeElement; - protected readonly options = tuiToAnimationOptions(inject(TUI_ANIMATIONS_SPEED)); + protected readonly options = inject(TUI_SCROLLBAR_OPTIONS); + protected readonly animationOptions = tuiToAnimationOptions( + inject(TUI_ANIMATIONS_SPEED), + ); + protected readonly refresh$ = inject(WA_ANIMATION_FRAME).pipe( throttleTime(300, tuiZonefreeScheduler()), map(() => this.scrollbars), diff --git a/projects/core/components/scrollbar/scroll-controls.template.html b/projects/core/components/scrollbar/scroll-controls.template.html index 3efff73548647..e47361127eede 100644 --- a/projects/core/components/scrollbar/scroll-controls.template.html +++ b/projects/core/components/scrollbar/scroll-controls.template.html @@ -1,26 +1,29 @@ - -
+ + +
-
-
+ *ngIf="bars[0]" + class="t-bar t-bar_vertical" + [@tuiFadeIn]="animationOptions" + [class.t-bar_has-horizontal]="bars[1]" + (mousedown.capture.prevent)="(0)" + > +
+
- -
+ *ngIf="bars[1]" + class="t-bar t-bar_horizontal" + [@tuiFadeIn]="animationOptions" + [class.t-bar_has-vertical]="bars[0]" + (mousedown.capture.prevent)="(0)" + > +
+ + + diff --git a/projects/core/components/scrollbar/scrollbar.component.ts b/projects/core/components/scrollbar/scrollbar.component.ts index 0fc184a85ba29..feec834e28576 100644 --- a/projects/core/components/scrollbar/scrollbar.component.ts +++ b/projects/core/components/scrollbar/scrollbar.component.ts @@ -38,7 +38,7 @@ export const TUI_SCROLLABLE = 'tui-scrollable'; }, ], host: { - '[class._native-hidden]': '!isIOS || hidden', + '[class._native-hidden]': '!options.nativeScrollbar && (!isIOS || hidden)', [`(${TUI_SCROLLABLE}.stop)`]: 'scrollRef = $event.detail', [`(${TUI_SCROLL_INTO_VIEW}.stop)`]: 'scrollIntoView($event.detail)', }, @@ -54,7 +54,7 @@ export class TuiScrollbar { public hidden = false; protected get delegated(): boolean { - return this.scrollRef !== this.el; + return this.scrollRef !== this.el || this.options.nativeScrollbar; } protected get scrollRef(): HTMLElement { diff --git a/projects/core/components/scrollbar/scrollbar.options.ts b/projects/core/components/scrollbar/scrollbar.options.ts index d91cc59cd76bf..aa3aaaa074cd4 100644 --- a/projects/core/components/scrollbar/scrollbar.options.ts +++ b/projects/core/components/scrollbar/scrollbar.options.ts @@ -2,10 +2,12 @@ import type {Provider} from '@angular/core'; import {tuiCreateToken, tuiProvideOptions} from '@taiga-ui/cdk/utils/miscellaneous'; export interface TuiScrollbarOptions { + nativeScrollbar: boolean; mode: 'always' | 'hover'; } export const TUI_DEFAULT_SCROLLBAR_OPTIONS: TuiScrollbarOptions = { + nativeScrollbar: false, mode: 'always', }; diff --git a/projects/core/components/scrollbar/scrollbar.template.html b/projects/core/components/scrollbar/scrollbar.template.html index f78c321491db3..c536a234b777c 100644 --- a/projects/core/components/scrollbar/scrollbar.template.html +++ b/projects/core/components/scrollbar/scrollbar.template.html @@ -1,5 +1,5 @@ diff --git a/projects/demo/src/modules/components/line-clamp/examples/6/index.html b/projects/demo/src/modules/components/line-clamp/examples/6/index.html index 1b6338f13f35e..674fd7502ae3b 100644 --- a/projects/demo/src/modules/components/line-clamp/examples/6/index.html +++ b/projects/demo/src/modules/components/line-clamp/examples/6/index.html @@ -3,7 +3,7 @@ appendOnly itemSize="50" tuiScrollable - class="tui-zero-scrollbar" + [class.tui-zero-scrollbar]="!nativeScrollbar" >
{ const firstName = this.names[Math.floor(Math.random() * this.names.length)] ?? ''; const lastName = diff --git a/projects/demo/src/modules/components/pull-to-refresh/examples/3/index.html b/projects/demo/src/modules/components/pull-to-refresh/examples/3/index.html index 6aa1eac39ea83..02fab79592113 100644 --- a/projects/demo/src/modules/components/pull-to-refresh/examples/3/index.html +++ b/projects/demo/src/modules/components/pull-to-refresh/examples/3/index.html @@ -4,7 +4,8 @@ appendOnly itemSize="50" tuiScrollable - class="example-viewport tui-zero-scrollbar" + class="example-viewport" + [class.tui-zero-scrollbar]="!nativeScrollbar" >
`Item #${i}`); protected onPull(): void { diff --git a/projects/demo/src/modules/components/scrollbar/examples/6/index.html b/projects/demo/src/modules/components/scrollbar/examples/6/index.html index 386bc9bb8bf16..83cfff7696c87 100644 --- a/projects/demo/src/modules/components/scrollbar/examples/6/index.html +++ b/projects/demo/src/modules/components/scrollbar/examples/6/index.html @@ -9,7 +9,8 @@ appendOnly itemSize="50" tuiScrollable - class="example-viewport tui-zero-scrollbar" + class="example-viewport" + [class.tui-zero-scrollbar]="!nativeScrollbar" >
`Item #${i}`); protected add(): void { diff --git a/projects/demo/src/modules/components/scrollbar/examples/8/index.html b/projects/demo/src/modules/components/scrollbar/examples/8/index.html new file mode 100644 index 0000000000000..24bd1c125fb79 --- /dev/null +++ b/projects/demo/src/modules/components/scrollbar/examples/8/index.html @@ -0,0 +1,50 @@ + +
+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti dignissimos, doloremque. Aperiam + assumenda atque aut blanditiis corporis, eum, facilis harum laudantium magni necessitatibus nobis quas + repudiandae sint ut voluptatem! Optio. +

+

+ Accusamus aperiam assumenda aut consectetur, corporis delectus, dolor eaque eius est hic impedit labore + possimus provident quas rem, rerum sequi sint tempora tempore ut? Debitis esse neque odio odit provident? +

+

+ Cum eum illo, ipsa iure nostrum ut voluptates? Autem blanditiis corporis debitis deserunt ex expedita + facilis fuga, illum iusto magnam praesentium provident recusandae repudiandae, totam, voluptatem. Minima + numquam sapiente sunt. +

+

+ Beatae consectetur cupiditate dignissimos ducimus eos excepturi labore pariatur placeat quia similique. + Architecto aspernatur cumque debitis distinctio esse, facere fugit harum ipsum libero minus neque numquam + omnis quidem, tempora, ut! +

+

+ Mollitia, perspiciatis sunt! Architecto aspernatur assumenda beatae, blanditiis commodi consequuntur debitis + et id, laboriosam maxime molestiae neque nihil officiis omnis, quam quos sint veritatis voluptate? Alias + deserunt distinctio modi perferendis? +

+

+ Ab aspernatur aut cumque cupiditate deleniti, dolorem ducimus eligendi eos facere harum hic ipsam ipsum iste + itaque modi nam necessitatibus nostrum nulla omnis quae repellat, sapiente sit tempore. Ipsam, quidem! +

+

+ Ab debitis deleniti distinctio est ex facere magni nemo numquam placeat quia, quibusdam sequi! Aliquid at + consectetur culpa ea enim facilis, harum hic, inventore iste possimus praesentium quas tempora voluptates. +

+

+ Aliquam eligendi ipsam modi nemo numquam obcaecati officia, quidem unde? Accusantium amet, animi deleniti + dolorum ea earum eos, expedita ipsa minima modi, pariatur perspiciatis porro quibusdam quo repellat tempore + voluptates! +

+

+ Ab assumenda fugiat magni natus officiis perferendis ratione rem repellendus tenetur. At commodi laudantium + modi, natus nobis nulla odio odit sed sint tempora tenetur voluptas? At odio praesentium quas ut! +

+

+ Atque aut consectetur consequuntur debitis eius facere ipsa ipsam maiores minima minus mollitia qui quos + repudiandae sapiente, soluta? Ad, amet dolore doloribus ducimus eos exercitationem molestiae quisquam soluta + ullam voluptate. +

+
+
diff --git a/projects/demo/src/modules/components/scrollbar/examples/8/index.less b/projects/demo/src/modules/components/scrollbar/examples/8/index.less new file mode 100644 index 0000000000000..dcfb27db8ee52 --- /dev/null +++ b/projects/demo/src/modules/components/scrollbar/examples/8/index.less @@ -0,0 +1,13 @@ +.box { + inline-size: 16rem; + block-size: 16rem; + border: 1px solid; +} + +.content { + padding: 0 0.6875rem; +} + +p { + white-space: nowrap; +} diff --git a/projects/demo/src/modules/components/scrollbar/examples/8/index.ts b/projects/demo/src/modules/components/scrollbar/examples/8/index.ts new file mode 100644 index 0000000000000..d64c95ba23e31 --- /dev/null +++ b/projects/demo/src/modules/components/scrollbar/examples/8/index.ts @@ -0,0 +1,19 @@ +import {Component} from '@angular/core'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {encapsulation} from '@demo/emulate/encapsulation'; +import {TuiScrollbar, tuiScrollbarOptionsProvider} from '@taiga-ui/core'; + +@Component({ + standalone: true, + imports: [TuiScrollbar], + templateUrl: './index.html', + styleUrls: ['./index.less'], + encapsulation, + changeDetection, + providers: [ + tuiScrollbarOptionsProvider({ + nativeScrollbar: true, + }), + ], +}) +export default class Example {} diff --git a/projects/demo/src/modules/components/scrollbar/index.ts b/projects/demo/src/modules/components/scrollbar/index.ts index 3ba4e14b85a8d..ea62badc4b743 100644 --- a/projects/demo/src/modules/components/scrollbar/index.ts +++ b/projects/demo/src/modules/components/scrollbar/index.ts @@ -17,5 +17,6 @@ export default class Page { 'Light scrollbar', 'Virtual scroll', 'Show scroll bars on hover', + 'Native scrollbar', ]; } diff --git a/projects/demo/src/modules/components/table/examples/5/index.html b/projects/demo/src/modules/components/table/examples/5/index.html index 9e06c747ba425..9af3e319e99db 100644 --- a/projects/demo/src/modules/components/table/examples/5/index.html +++ b/projects/demo/src/modules/components/table/examples/5/index.html @@ -3,7 +3,8 @@ #viewport appendOnly tuiScrollable - class="viewport tui-zero-scrollbar" + class="viewport" + [class.tui-zero-scrollbar]="!nativeScrollbar" [itemSize]="45" [maxBufferPx]="500" [minBufferPx]="400" diff --git a/projects/demo/src/modules/components/table/examples/5/index.ts b/projects/demo/src/modules/components/table/examples/5/index.ts index 0386a009d15b9..1d644eb825f74 100644 --- a/projects/demo/src/modules/components/table/examples/5/index.ts +++ b/projects/demo/src/modules/components/table/examples/5/index.ts @@ -3,13 +3,13 @@ import { CdkVirtualForOf, CdkVirtualScrollViewport, } from '@angular/cdk/scrolling'; -import {Component} from '@angular/core'; +import {Component, inject} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import type {TuiComparator} from '@taiga-ui/addon-table'; import {TuiTable} from '@taiga-ui/addon-table'; import {TuiDay, tuiToInt} from '@taiga-ui/cdk'; -import {TuiScrollable, TuiScrollbar} from '@taiga-ui/core'; +import {TUI_SCROLLBAR_OPTIONS, TuiScrollable, TuiScrollbar} from '@taiga-ui/core'; interface User { readonly dob: TuiDay; @@ -75,6 +75,8 @@ function getAge({dob}: User): number { changeDetection, }) export default class Example { + protected readonly nativeScrollbar = inject(TUI_SCROLLBAR_OPTIONS).nativeScrollbar; + protected readonly data = DATA; protected readonly columns = ['name', 'dob', 'age'];