diff --git a/.nx/cache/nx_files.nxt b/.nx/cache/nx_files.nxt new file mode 100644 index 00000000..c16b7df1 Binary files /dev/null and b/.nx/cache/nx_files.nxt differ diff --git a/CHANGELOG.md b/CHANGELOG.md index 245784be..0f9d95d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 14.0.0-beta.7 + +- regret: Use `offsetWidth` and `offsetHeight` again instead of `getBoundingClientRect` due to incorrect value when scrollbar is used in an overlay such as `matSelect`. +- fix: `(reachedEnd)` does not work without subscribing to `(reachedBottom)`. + +### Breaking changes: + +- Remove `NgScrollbarReachModule`. +- Rename the directive `MatSelectViewport` to `NgScrollbarMatSelectViewport`. + ## 14.0.0-beta.6 - feat: Add scrollbar buttons using `buttons` options, closes [#465](https://github.com/MurhafSousli/ngx-scrollbar/issues/465). @@ -8,7 +18,7 @@ - refactor: Use `swithcMap` instead of `expand` for track long ongoing pointer down event. - refactor: Only use `getRtlScrollAxisType` once on initialization in `SmoothScrollManager`. - refactor: `matSelectViewport` use instant scroll function to scroll to the selected item in the list. -- refactor: Use `getBoundingClientRect` in all directives instead of `clientWidth` and `clientHeight` for high precision calculation. +- refactor: Use `getBoundingClientRect` in all directives instead of `offsetWidth` and `offsetHeight` for high precision calculation. ### Breaking Changes diff --git a/angular.json b/angular.json index 0bac3f41..f7cec184 100644 --- a/angular.json +++ b/angular.json @@ -58,8 +58,8 @@ }, { "type": "anyComponentStyle", - "maximumWarning": "12kb", - "maximumError": "12kb" + "maximumWarning": "13kb", + "maximumError": "13kb" } ], "outputHashing": "all" diff --git a/projects/ngx-scrollbar-demo/src/app/app.component.html b/projects/ngx-scrollbar-demo/src/app/app.component.html index eae5c94a..84ebe6b4 100644 --- a/projects/ngx-scrollbar-demo/src/app/app.component.html +++ b/projects/ngx-scrollbar-demo/src/app/app.component.html @@ -21,15 +21,10 @@ Material Integration - Integration + Other Integrations - - - - - diff --git a/projects/ngx-scrollbar-demo/src/app/app.component.scss b/projects/ngx-scrollbar-demo/src/app/app.component.scss index 208f5ba2..caafba7e 100644 --- a/projects/ngx-scrollbar-demo/src/app/app.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/app.component.scss @@ -5,12 +5,22 @@ ng-scrollbar { --scrollbar-track-color: rgb(0 0 0 / 30%); --scrollbar-border-radius: 0; + --scrollbar-button-color: var(--scrollbar-thumb-color); + --scrollbar-button-hover-color: var(--scrollbar-button-color); + --scrollbar-button-active-color: var(--scrollbar-button-hover-color); + + // Scrollbar button arrow fill + --scrollbar-button-fill: white; + --scrollbar-button-hover-fill: var(--scrollbar-button-fill); + --scrollbar-button-active-fill: var(--scrollbar-button-hover-fill); + + color: white; height: calc(100% - 60px); ::ng-deep { .app-scrollbar-track { - box-shadow: inset 0 0 0 1px rgb(0 0 0 / 10%); + //box-shadow: inset 0 0 0 1px rgb(0 0 0 / 10%); } .app-scrollbar-thumb { diff --git a/projects/ngx-scrollbar-demo/src/app/app.routes.ts b/projects/ngx-scrollbar-demo/src/app/app.routes.ts index 7417a231..a6283bb4 100644 --- a/projects/ngx-scrollbar-demo/src/app/app.routes.ts +++ b/projects/ngx-scrollbar-demo/src/app/app.routes.ts @@ -11,11 +11,7 @@ export const routes: Routes = [ loadComponent: () => import('./material-page/material-page.component').then(c => c.MaterialPageComponent), }, { - path: 'integration', + path: 'other-integrations', loadComponent: () => import('./integration-page/integration-page.component').then(c => c.IntegrationPageComponent), - }, - // { - // path: 'lab', - // loadComponent: () => import('./lab/lab.component').then(c => c.LabComponent), - // } + } ]; diff --git a/projects/ngx-scrollbar-demo/src/app/example-infinite-scroll/example-infinite-scroll.component.scss b/projects/ngx-scrollbar-demo/src/app/example-infinite-scroll/example-infinite-scroll.component.scss index 1488af97..88b3eb4e 100644 --- a/projects/ngx-scrollbar-demo/src/app/example-infinite-scroll/example-infinite-scroll.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/example-infinite-scroll/example-infinite-scroll.component.scss @@ -15,7 +15,9 @@ ng-scrollbar { --scrollbar-track-color: rgb(0 0 0 / 5%); --scrollbar-thumb-color: var(--color-primary); - --scrollbar-thickness: 12; + --scrollbar-thickness: 10; + --scrollbar-offset: 6; + --scrollbar-border-radius: 7px; border-radius: 3px; border: 2px solid rgb(0 0 0 / 5%); diff --git a/projects/ngx-scrollbar-demo/src/app/example-mat-select/example-mat-select.component.scss b/projects/ngx-scrollbar-demo/src/app/example-mat-select/example-mat-select.component.scss index 41abddc0..91474943 100644 --- a/projects/ngx-scrollbar-demo/src/app/example-mat-select/example-mat-select.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/example-mat-select/example-mat-select.component.scss @@ -1,10 +1,13 @@ ng-scrollbar { - --scrollbar-thumb-color: var(--color-primary); + --scrollbar-thumb-color: var(--color-track-orange-passion); --scrollbar-thickness: 8; --scrollbar-track-color: rgb(0 0 0 / 20%); + --scrollbar-offset: 6; + --scrollbar-border-radius: 7px; } .mat-mdc-select { + --mat-select-enabled-arrow-color: rgb(255 255 255 / 54%); margin-bottom: 30px !important; color: white; } diff --git a/projects/ngx-scrollbar-demo/src/app/example-mat-select/example-mat-select.component.ts b/projects/ngx-scrollbar-demo/src/app/example-mat-select/example-mat-select.component.ts index a1c4f390..e4b11faa 100644 --- a/projects/ngx-scrollbar-demo/src/app/example-mat-select/example-mat-select.component.ts +++ b/projects/ngx-scrollbar-demo/src/app/example-mat-select/example-mat-select.component.ts @@ -5,7 +5,7 @@ import { MatCardModule } from '@angular/material/card'; import { MatSelectModule } from '@angular/material/select'; import { Chance } from 'chance'; import { NgScrollbar } from 'ngx-scrollbar'; -import { MatSelectViewport } from 'ngx-scrollbar/utils'; +import { NgScrollbarMatSelectViewport } from 'ngx-scrollbar/utils'; @Component({ selector: 'app-example-mat-select', @@ -16,7 +16,7 @@ import { MatSelectViewport } from 'ngx-scrollbar/utils'; NgForOf, NgScrollbar, MatCardModule, - MatSelectViewport + NgScrollbarMatSelectViewport ], templateUrl: './example-mat-select.component.html', styleUrl: './example-mat-select.component.scss', diff --git a/projects/ngx-scrollbar-demo/src/app/example-nested-virtual-scroll/example-nested-virtual-scroll.component.scss b/projects/ngx-scrollbar-demo/src/app/example-nested-virtual-scroll/example-nested-virtual-scroll.component.scss index 8025b012..b7c781a3 100644 --- a/projects/ngx-scrollbar-demo/src/app/example-nested-virtual-scroll/example-nested-virtual-scroll.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/example-nested-virtual-scroll/example-nested-virtual-scroll.component.scss @@ -12,14 +12,17 @@ .outer-ng-scrollbar { color: black; border: 2px solid rgb(0 0 0 / 5%); - --scrollbar-track-color: var(--color-track-orange-passion); + //--scrollbar-track-color: var(--color-track-orange-passion); + --scrollbar-track-color: rgb(255 255 255 / 15%); --scrollbar-thumb-color: var(--color-orange-passion); - --scrollbar-thickness: 12; + --scrollbar-offset: 3; + --scrollbar-thickness: 10; --scrollbar-border-radius: 10px; } .cdk-virtual-scroll-orientation-vertical ::ng-deep > .ng-scroll-content { - background: #643761; + //background: #643761; + background: var(--primary-color); } .cards { @@ -73,6 +76,7 @@ --scrollbar-track-color: #d3d3d3; --scrollbar-offset: 7; --scrollbar-thickness: 6; + --scrollbar-border-radius: 7px; --scrollbar-thumb-color: var(--color-accent); } } diff --git a/projects/ngx-scrollbar-demo/src/app/example-ngx-datatable/example-ngx-datatable.component.scss b/projects/ngx-scrollbar-demo/src/app/example-ngx-datatable/example-ngx-datatable.component.scss index 40e9f2f5..f284c00c 100644 --- a/projects/ngx-scrollbar-demo/src/app/example-ngx-datatable/example-ngx-datatable.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/example-ngx-datatable/example-ngx-datatable.component.scss @@ -1,7 +1,7 @@ ng-scrollbar { --scrollbar-thickness: 6; --scrollbar-hover-thickness: 10; - --scrollbar-offset: 0; + --scrollbar-offset: 1; --scrollbar-border-radius: 0; --scrollbar-thumb-color: var(--color-primary); --scrollbar-track-color: rgb(0 0 0 / 20%); diff --git a/projects/ngx-scrollbar-demo/src/app/example2/example2.component.html b/projects/ngx-scrollbar-demo/src/app/example2/example2.component.html index 972396f8..e4d6097d 100644 --- a/projects/ngx-scrollbar-demo/src/app/example2/example2.component.html +++ b/projects/ngx-scrollbar-demo/src/app/example2/example2.component.html @@ -7,7 +7,7 @@ externalViewport appearance="compact" (afterInit)="onScrollbarUpdate(scrollbarRef)" - (afterUpdate)="onScrollbarUpdate(scrollbarRef)"> + (afterUpdate)="onScrollbarUpdate(scrollbarRef, 200)">
- + diff --git a/projects/ngx-scrollbar-demo/src/app/example2/example2.component.scss b/projects/ngx-scrollbar-demo/src/app/example2/example2.component.scss index a81449af..73c443bc 100644 --- a/projects/ngx-scrollbar-demo/src/app/example2/example2.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/example2/example2.component.scss @@ -4,6 +4,7 @@ ng-scrollbar { --scrollbar-track-color: rgb(0 0 0 / 10%); --scrollbar-thumb-color: rgb(0 0 0 / 20%); --scrollbar-thumb-hover-color: rgb(0 0 0 / 30%); + --scrollbar-border-radius: 7px; width: 100%; border: 1px solid rgb(0 0 0 / 25%); height: 200px; @@ -58,8 +59,8 @@ ng-scrollbar { } header { - font-size: 1.1em; - padding: .2em; + font-size: 16px; + padding: 8px; letter-spacing: 1px; background-image: linear-gradient(to bottom, #3d434a, #242931); color: white; diff --git a/projects/ngx-scrollbar-demo/src/app/example2/example2.component.ts b/projects/ngx-scrollbar-demo/src/app/example2/example2.component.ts index cc5204fc..ac979cc8 100644 --- a/projects/ngx-scrollbar-demo/src/app/example2/example2.component.ts +++ b/projects/ngx-scrollbar-demo/src/app/example2/example2.component.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; import { NgScrollbarExt, NgScrollbarModule } from 'ngx-scrollbar'; -import { NgScrollbarReachedModule } from 'ngx-scrollbar/reached-event'; +import { NgScrollReached } from 'ngx-scrollbar/reached-event'; @Component({ selector: 'app-example2', @@ -14,7 +14,7 @@ import { NgScrollbarReachedModule } from 'ngx-scrollbar/reached-event'; '[class.example-component]': 'true' }, standalone: true, - imports: [CommonModule, NgScrollbarModule, NgScrollbarReachedModule, MatCardModule, MatButtonModule] + imports: [CommonModule, NgScrollbarModule, NgScrollReached, MatCardModule, MatButtonModule] }) export class Example2Component { @@ -102,7 +102,7 @@ export class Example2Component { ]; } - onScrollbarUpdate(scrollbarRef: NgScrollbarExt): void { - scrollbarRef.scrollTo({ bottom: 0, duration: 200 }); + onScrollbarUpdate(scrollbarRef: NgScrollbarExt, duration: number = 0): void { + scrollbarRef.scrollTo({ bottom: 0, duration }); } } diff --git a/projects/ngx-scrollbar-demo/src/app/example3/example3.component.scss b/projects/ngx-scrollbar-demo/src/app/example3/example3.component.scss index eb200384..a969a860 100644 --- a/projects/ngx-scrollbar-demo/src/app/example3/example3.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/example3/example3.component.scss @@ -7,6 +7,7 @@ --scrollbar-thickness: 12; --scrollbar-track-color: rgb(0 0 0 / 50%); --scrollbar-border-radius: 6px; + --scrollbar-offset: 4; } .mat-mdc-card-content { diff --git a/projects/ngx-scrollbar-demo/src/app/lab/css-variables-form/css-variables-form.component.ts b/projects/ngx-scrollbar-demo/src/app/lab/css-variables-form/css-variables-form.component.ts index 27aeccfa..878f808c 100644 --- a/projects/ngx-scrollbar-demo/src/app/lab/css-variables-form/css-variables-form.component.ts +++ b/projects/ngx-scrollbar-demo/src/app/lab/css-variables-form/css-variables-form.component.ts @@ -19,9 +19,9 @@ export class CssVariablesFormComponent implements OnInit { this.form = new FormGroup({ thickness: new FormControl('12'), hoverThickness: new FormControl('15'), - trackColor: new FormControl('#975857'), + trackColor: new FormControl('#77b4e8'), thumbColor: new FormControl('var(--color-scrollbar-example-x)'), - thumbHoverColor: new FormControl('#ff6060'), //var(--color-scrollbar-example-x)'), + thumbHoverColor: new FormControl('var(--color-scrollbar-example-x)'), trackOffset: new FormControl('0'), borderRadius: new FormControl('0px'), overscrollBehavior: new FormControl('initial'), diff --git a/projects/ngx-scrollbar-demo/src/app/lab/lab.component.html b/projects/ngx-scrollbar-demo/src/app/lab/lab.component.html index 88cdad25..3ecaff19 100644 --- a/projects/ngx-scrollbar-demo/src/app/lab/lab.component.html +++ b/projects/ngx-scrollbar-demo/src/app/lab/lab.component.html @@ -97,6 +97,15 @@ +
+
Disable dropped events
+ + True + False + +
+
Sensor throttle time
@@ -153,7 +162,12 @@ (reachedStart)="onReached('start')" (reachedEnd)="onReached('end')" (reachedBottom)="onReached('bottom')" - [disableReached]="disableReached"> + [disableReached]="disableReached" + (droppedTop)="onDropped('top')" + (droppedStart)="onDropped('start')" + (droppedEnd)="onDropped('end')" + (droppedBottom)="onDropped('bottom')" + [disableDropped]="disableDropped">
@@ -171,7 +185,7 @@
- +
diff --git a/projects/ngx-scrollbar-demo/src/app/lab/lab.component.ts b/projects/ngx-scrollbar-demo/src/app/lab/lab.component.ts index 2366b871..072af393 100644 --- a/projects/ngx-scrollbar-demo/src/app/lab/lab.component.ts +++ b/projects/ngx-scrollbar-demo/src/app/lab/lab.component.ts @@ -12,7 +12,7 @@ import { ScrollbarOrientation, ScrollbarVisibility } from 'ngx-scrollbar'; -import { NgScrollbarReached } from 'ngx-scrollbar/reached-event'; +import { NgScrollDropped, NgScrollReached } from 'ngx-scrollbar/reached-event'; import { NzResizableModule, NzResizeDirection, NzResizeEvent } from 'ng-zorro-antd/resizable'; import { NzIconModule } from 'ng-zorro-antd/icon'; @@ -31,7 +31,8 @@ import { SmoothScrollFormComponent, SmoothScrollOptionsForm } from './smooth-scr FormsModule, ReactiveFormsModule, NgScrollbar, - NgScrollbarReached, + NgScrollReached, + NgScrollDropped, MatCardModule, MatButtonToggleModule, MatExpansionModule, @@ -57,6 +58,7 @@ export class LabComponent { interactionDisabled: boolean = false; disableSensor: boolean = false; disableReached: boolean = false; + disableDropped: boolean = false; sensorThrottleTime: number = 0; trackScrollDuration: number = 50; @@ -87,6 +89,8 @@ export class LabComponent { reached: WritableSignal = signal({}); + dropped: WritableSignal = signal({}); + scrollReached: WritableSignal = signal(false); get content(): string { @@ -122,6 +126,13 @@ export class LabComponent { }, 600); } + onDropped(eventName: string): void { + this.dropped.set({ [eventName]: true }); + setTimeout(() => { + this.dropped.set({ [eventName]: false }) + }, 600); + } + onScrollTo(event: SmoothScrollOptionsForm): void { // Prepare scrollTo options from event const options: Partial = { diff --git a/projects/ngx-scrollbar-demo/src/app/lab/reached-notifier/reached-notifier.component.scss b/projects/ngx-scrollbar-demo/src/app/lab/reached-notifier/reached-notifier.component.scss index 13051c10..4077e0fb 100644 --- a/projects/ngx-scrollbar-demo/src/app/lab/reached-notifier/reached-notifier.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/lab/reached-notifier/reached-notifier.component.scss @@ -10,6 +10,13 @@ } } +.notifier-wrapper { + display: flex; + gap: 1em; + align-items: center; + margin-bottom: 0.5em; +} + .reached-event-title { margin-bottom: 5px; } @@ -27,7 +34,19 @@ } &.changed { + transition: unset !important; + } +} + +.reached { + .mat-mdc-chip.changed { background-color: var(--color-highlight) !important; transition: unset !important; } } + +.dropped { + .mat-mdc-chip.changed { + background-color: #85ff3f !important; + } +} diff --git a/projects/ngx-scrollbar-demo/src/app/lab/reached-notifier/reached-notifier.component.ts b/projects/ngx-scrollbar-demo/src/app/lab/reached-notifier/reached-notifier.component.ts index 003afb60..638f18cd 100644 --- a/projects/ngx-scrollbar-demo/src/app/lab/reached-notifier/reached-notifier.component.ts +++ b/projects/ngx-scrollbar-demo/src/app/lab/reached-notifier/reached-notifier.component.ts @@ -4,20 +4,41 @@ import { MatChipsModule } from '@angular/material/chips'; @Component({ selector: 'app-reached-notifier', template: ` - - - Top - - - Bottom - - - Start - - - End - - +
+
Reached
+ + + Top + + + Bottom + + + Start + + + End + + +
+ +
+
Dropped
+ + + Top + + + Bottom + + + Start + + + End + + +
`, styleUrls: ['./reached-notifier.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, @@ -26,6 +47,7 @@ import { MatChipsModule } from '@angular/material/chips'; }) export class ReachedNotifierComponent { @Input() reached: ReachedEvent; + @Input() dropped: ReachedEvent; } export interface ReachedEvent { diff --git a/projects/ngx-scrollbar-demo/src/app/prime-ng-table/prime-ng.component.html b/projects/ngx-scrollbar-demo/src/app/prime-ng-table/prime-ng.component.html index 81c44326..a71660f5 100644 --- a/projects/ngx-scrollbar-demo/src/app/prime-ng-table/prime-ng.component.html +++ b/projects/ngx-scrollbar-demo/src/app/prime-ng-table/prime-ng.component.html @@ -26,7 +26,7 @@ asyncDetection="auto"> diff --git a/projects/ngx-scrollbar-demo/src/app/prime-ng-table/prime-ng.component.scss b/projects/ngx-scrollbar-demo/src/app/prime-ng-table/prime-ng.component.scss index 1c4f29c5..e5f4335a 100644 --- a/projects/ngx-scrollbar-demo/src/app/prime-ng-table/prime-ng.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/prime-ng-table/prime-ng.component.scss @@ -25,7 +25,9 @@ ng-scrollbar { --scrollbar-track-color: rgb(0 0 0 / 5%); --scrollbar-thumb-color: var(--color-primary); - --scrollbar-thickness: 12; + --scrollbar-thickness: 10; + --scrollbar-offset: 6; + --scrollbar-border-radius: 7px; border-radius: 3px; border: 2px solid rgb(0 0 0 / 5%); diff --git a/projects/ngx-scrollbar-demo/src/app/shared/header/header.component.html b/projects/ngx-scrollbar-demo/src/app/shared/header/header.component.html index 3f280397..4daf52d0 100644 --- a/projects/ngx-scrollbar-demo/src/app/shared/header/header.component.html +++ b/projects/ngx-scrollbar-demo/src/app/shared/header/header.component.html @@ -5,7 +5,7 @@ d="M0,288L48,266.7C96,245,192,203,288,202.7C384,203,480,245,576,250.7C672,256,768,224,864,224C960,224,1056,256,1152,261.3C1248,267,1344,245,1392,234.7L1440,224L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z"> -
+

ngx-scrollbar

@@ -27,13 +27,14 @@

Custom overlay-scrollbars with native scrolling mechanism

- - - npm - + + + codecov + Custom overlay-scrollbars with native scrolling mechanism + + + npm +
-
library_books
-
Go To Documentation
+
+ library_books +
+
Go To Documentation
diff --git a/projects/ngx-scrollbar-demo/src/app/shared/header/header.component.scss b/projects/ngx-scrollbar-demo/src/app/shared/header/header.component.scss index 59a2b3ba..94fcef9b 100644 --- a/projects/ngx-scrollbar-demo/src/app/shared/header/header.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/shared/header/header.component.scss @@ -42,15 +42,14 @@ app-logo { margin: 2em 0; display: flex; flex-wrap: wrap; - justify-content: center; - align-items: center; + place-content: center; + gap: 4px; a, iframe { margin-top: 7px; } a { - margin-left: 7px; display: flex; } } diff --git a/projects/ngx-scrollbar-demo/src/styles.scss b/projects/ngx-scrollbar-demo/src/styles.scss index 7cfc6a37..acb2726c 100644 --- a/projects/ngx-scrollbar-demo/src/styles.scss +++ b/projects/ngx-scrollbar-demo/src/styles.scss @@ -49,8 +49,8 @@ $purple-passion-palette: ( ) ); -$app-primary: mat.define-palette($purple-passion-palette, 500); -//$app-primary: mat.define-palette(mat.$light-green-palette, 500); +//$app-primary: mat.define-palette($purple-passion-palette, 500); +$app-primary: mat.define-palette(mat.$blue-palette, 500); $app-accent: mat.define-palette(mat.$orange-palette, 500); $app-warn: mat.define-palette(mat.$red-palette); $app-success: mat.define-palette(mat.$light-green-palette); @@ -90,7 +90,10 @@ body { --color-orange-passion: #{mat.get-color-from-palette(mat.define-palette($app-accent, 500))}; - --color-track-orange-passion: #975857; + --color-track-orange-passion: #963634; + + --mat-option-selected-state-label-text-color: var(--color-track-orange-passion); + --mat-option-selected-state-label-text-color: var(--color-track-orange-passion); --color-app-background: #{mat.get-color-from-palette(mat.define-palette($app-primary, 400))}; --color-app-logo: var(--color-app-background); @@ -205,6 +208,6 @@ a { .mat-mdc-select-panel { padding: 0 !important; - --mat-minimal-pseudo-checkbox-selected-checkmark-color: var(--color-primary); - background-image: linear-gradient( to bottom, var(--color-example3), #EFB436); + --mat-minimal-pseudo-checkbox-selected-checkmark-color: var(--color-track-orange-passion); + background-image: linear-gradient( to bottom, var(--color-example3), var(--color-accent)); } diff --git a/projects/ngx-scrollbar/.eslintrc.json b/projects/ngx-scrollbar/.eslintrc.json index d4266efc..dde2dd6a 100644 --- a/projects/ngx-scrollbar/.eslintrc.json +++ b/projects/ngx-scrollbar/.eslintrc.json @@ -34,6 +34,8 @@ "@angular-eslint/component-class-suffix": 0, "@angular-eslint/directive-class-suffix": 0, "@angular-eslint/no-input-rename": 0, + "@angular-eslint/no-output-rename": 0, + "@angular-eslint/no-output-native": 0, "@angular-eslint/no-host-metadata-property": 0 } }, diff --git a/projects/ngx-scrollbar/package.json b/projects/ngx-scrollbar/package.json index 1fbe265d..5a893323 100644 --- a/projects/ngx-scrollbar/package.json +++ b/projects/ngx-scrollbar/package.json @@ -1,6 +1,6 @@ { "name": "ngx-scrollbar", - "version": "14.0.0-beta.6", + "version": "14.0.0-beta.7", "license": "MIT", "homepage": "https://ngx-scrollbar.netlify.com/", "author": { diff --git a/projects/ngx-scrollbar/reached-event/src/ng-scroll-dropped.ts b/projects/ngx-scrollbar/reached-event/src/ng-scroll-dropped.ts new file mode 100644 index 00000000..29e03e5a --- /dev/null +++ b/projects/ngx-scrollbar/reached-event/src/ng-scroll-dropped.ts @@ -0,0 +1,60 @@ +import { + Directive, + Input, + Output, + numberAttribute, + booleanAttribute, + input, + EventEmitter, + InputSignalWithTransform +} from '@angular/core'; +import { ReachedDroppedBase } from './reached-dropped-base'; + +@Directive({ + selector: 'ng-scrollbar[droppedTop], ng-scrollbar[droppedBottom], ng-scrollbar[droppedStart], ng-scrollbar[droppedEnd]', + standalone: true, +}) +export class NgScrollDropped extends ReachedDroppedBase { + + /** Dropped offset value in px */ + @Input({ alias: 'droppedOffset', transform: numberAttribute }) set offsetSetter(value: number) { + this.setCssVariable('--dropped-offset', value); + } + + @Input({ alias: 'droppedTopOffset', transform: numberAttribute }) set offsetTopSetter(value: number) { + this.setCssVariable('--dropped-offset-top', value); + } + + @Input({ alias: 'droppedBottomOffset', transform: numberAttribute }) set offsetBottomSetter(value: number) { + this.setCssVariable('--dropped-offset-bottom', value); + } + + @Input({ alias: 'droppedStartOffset', transform: numberAttribute }) set offsetStartSetter(value: number) { + this.setCssVariable('--dropped-offset-start', value); + } + + @Input({ alias: 'droppedEndOffset', transform: numberAttribute }) set offsetEndSetter(value: number) { + this.setCssVariable('--dropped-offset-end', value); + } + + disabled: InputSignalWithTransform = input(false, { + alias: 'disableDropped', + transform: booleanAttribute + }); + + @Output('droppedTop') top: EventEmitter = new EventEmitter(); + + @Output('droppedBottom') bottom: EventEmitter = new EventEmitter(); + + @Output('droppedStart') start: EventEmitter = new EventEmitter(); + + @Output('droppedEnd') end: EventEmitter = new EventEmitter(); + + protected triggerElementsWrapperClass: string = 'ng-scroll-dropped-wrapper'; + + protected triggerElementClass: string = 'scroll-dropped-trigger-element'; + + protected isTriggered(entry: IntersectionObserverEntry): boolean { + return !entry.isIntersecting; + } +} diff --git a/projects/ngx-scrollbar/reached-event/src/ng-scroll-reached.ts b/projects/ngx-scrollbar/reached-event/src/ng-scroll-reached.ts new file mode 100644 index 00000000..f839c904 --- /dev/null +++ b/projects/ngx-scrollbar/reached-event/src/ng-scroll-reached.ts @@ -0,0 +1,60 @@ +import { + Directive, + Input, + Output, + numberAttribute, + booleanAttribute, + input, + EventEmitter, + InputSignalWithTransform +} from '@angular/core'; +import { ReachedDroppedBase } from './reached-dropped-base'; + +@Directive({ + selector: 'ng-scrollbar[reachedTop], ng-scrollbar[reachedBottom], ng-scrollbar[reachedStart], ng-scrollbar[reachedEnd]', + standalone: true, +}) +export class NgScrollReached extends ReachedDroppedBase { + + /** Reached offset value in px */ + @Input({ alias: 'reachedOffset', transform: numberAttribute }) set offsetSetter(value: number) { + this.setCssVariable('--reached-offset', value); + } + + @Input({ alias: 'reachedTopOffset', transform: numberAttribute }) set offsetTopSetter(value: number) { + this.setCssVariable('--reached-offset-top', value); + } + + @Input({ alias: 'reachedBottomOffset', transform: numberAttribute }) set offsetBottomSetter(value: number) { + this.setCssVariable('--reached-offset-bottom', value); + } + + @Input({ alias: 'reachedStartOffset', transform: numberAttribute }) set offsetStartSetter(value: number) { + this.setCssVariable('--reached-offset-start', value); + } + + @Input({ alias: 'reachedEndOffset', transform: numberAttribute }) set offsetEndSetter(value: number) { + this.setCssVariable('--reached-offset-end', value); + } + + disabled: InputSignalWithTransform = input(false, { + alias: 'disableReached', + transform: booleanAttribute + }); + + @Output('reachedTop') top: EventEmitter = new EventEmitter(); + + @Output('reachedBottom') bottom: EventEmitter = new EventEmitter(); + + @Output('reachedStart') start: EventEmitter = new EventEmitter(); + + @Output('reachedEnd') end: EventEmitter = new EventEmitter(); + + protected triggerElementsWrapperClass: string = 'ng-scroll-reached-wrapper'; + + protected triggerElementClass: string = 'scroll-reached-trigger-element'; + + protected isTriggered(entry: IntersectionObserverEntry): boolean { + return entry.isIntersecting; + } +} diff --git a/projects/ngx-scrollbar/reached-event/src/ng-scrollbar-reached.module.ts b/projects/ngx-scrollbar/reached-event/src/ng-scrollbar-reached.module.ts deleted file mode 100644 index 7ffbad69..00000000 --- a/projects/ngx-scrollbar/reached-event/src/ng-scrollbar-reached.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { NgScrollbarReached } from './ng-scrollbar-reached'; - -@NgModule({ - imports: [ - NgScrollbarReached - ], - exports: [ - NgScrollbarReached - ] -}) -export class NgScrollbarReachedModule { -} diff --git a/projects/ngx-scrollbar/reached-event/src/public_api.ts b/projects/ngx-scrollbar/reached-event/src/public_api.ts index 42093f63..5e685305 100644 --- a/projects/ngx-scrollbar/reached-event/src/public_api.ts +++ b/projects/ngx-scrollbar/reached-event/src/public_api.ts @@ -1,2 +1,2 @@ -export * from './ng-scrollbar-reached.module'; -export * from './ng-scrollbar-reached'; +export * from './ng-scroll-reached'; +export * from './ng-scroll-dropped'; diff --git a/projects/ngx-scrollbar/reached-event/src/ng-scrollbar-reached.ts b/projects/ngx-scrollbar/reached-event/src/reached-dropped-base.ts similarity index 50% rename from projects/ngx-scrollbar/reached-event/src/ng-scrollbar-reached.ts rename to projects/ngx-scrollbar/reached-event/src/reached-dropped-base.ts index 34df308b..615c5beb 100644 --- a/projects/ngx-scrollbar/reached-event/src/ng-scrollbar-reached.ts +++ b/projects/ngx-scrollbar/reached-event/src/reached-dropped-base.ts @@ -1,101 +1,84 @@ -import { isPlatformBrowser } from '@angular/common'; import { Directive, - Input, - Output, + inject, + effect, + runInInjectionContext, OnInit, OnDestroy, NgZone, - Renderer2, - WritableSignal, Injector, - inject, - signal, - effect, - numberAttribute, - booleanAttribute, - runInInjectionContext, + Renderer2, EventEmitter, - PLATFORM_ID + PLATFORM_ID, + InputSignalWithTransform } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; import { _NgScrollbar, NG_SCROLLBAR } from 'ngx-scrollbar'; -type ReachedEventAction = { +type EventAction = { emit: () => void; } -@Directive({ - selector: 'ng-scrollbar[reachedTop], ng-scrollbar[reachedBottom], ng-scrollbar[reachedStart], ng-scrollbar[reachedEnd]', - standalone: true, -}) -export class NgScrollbarReached implements OnInit, OnDestroy { - - private readonly isBrowser: boolean = isPlatformBrowser(inject(PLATFORM_ID)); +@Directive() +export abstract class ReachedDroppedBase implements OnInit, OnDestroy { - private readonly zone: NgZone = inject(NgZone); + protected readonly isBrowser: boolean = isPlatformBrowser(inject(PLATFORM_ID)); - private readonly renderer: Renderer2 = inject(Renderer2); + protected readonly zone: NgZone = inject(NgZone); - private readonly injector: Injector = inject(Injector); + protected readonly renderer: Renderer2 = inject(Renderer2); - private readonly scrollbar: _NgScrollbar = inject(NG_SCROLLBAR); + protected readonly injector: Injector = inject(Injector); - private readonly disabled: WritableSignal = signal(false); + protected readonly scrollbar: _NgScrollbar = inject(NG_SCROLLBAR); /** An array that contains all trigger elements **/ - private triggerElements: HTMLElement[] = []; + protected triggerElements: HTMLElement[] = []; /** The intersection observer reference */ - private intersectionObserver: IntersectionObserver; + protected intersectionObserver: IntersectionObserver; /** An array that contains the chosen outputs */ - private subscribedEvents: string[] = []; + protected subscribedEvents: string[] = []; - /** The scrollbars element that contains the trigger elements */ - private triggerElementsWrapper: HTMLElement; + /** The wrapper element that contains the trigger elements */ + protected triggerElementsWrapper: HTMLElement; - /** Reached offset value in px */ - @Input({ alias: 'reachedOffset', transform: numberAttribute }) set offsetSetter(value: number) { - this.setCssVariable('--reached-offset', value); - } + /** The wrapper element class name */ + protected abstract triggerElementsWrapperClass: string; - @Input({ alias: 'reachedTopOffset', transform: numberAttribute }) set offsetTopSetter(value: number) { - this.setCssVariable('--reached-offset-top', value); - } + /** The trigger element class name */ + protected abstract triggerElementClass: string; - @Input({ alias: 'reachedBottomOffset', transform: numberAttribute }) set offsetBottomSetter(value: number) { - this.setCssVariable('--reached-offset-bottom', value); - } + abstract disabled: InputSignalWithTransform; - @Input({ alias: 'reachedStartOffset', transform: numberAttribute }) set offsetStartSetter(value: number) { - this.setCssVariable('--reached-offset-start', value); - } + abstract top: EventEmitter; - @Input({ alias: 'reachedEndOffset', transform: numberAttribute }) set offsetEndSetter(value: number) { - this.setCssVariable('--reached-offset-end', value); - } + abstract bottom: EventEmitter; - @Input({ alias: 'disableReached', transform: booleanAttribute }) - set disableReachedSetter(value: boolean) { - this.disabled.set(value); - } + abstract start: EventEmitter; + + abstract end: EventEmitter; - @Output() reachedTop: EventEmitter = new EventEmitter(); - @Output() reachedBottom: EventEmitter = new EventEmitter(); - @Output() reachedStart: EventEmitter = new EventEmitter(); - @Output() reachedEnd: EventEmitter = new EventEmitter(); + protected abstract isTriggered(entry: IntersectionObserverEntry): boolean; /** A mapper function to ease forwarding the intersected element to its proper output */ - readonly reachedEventActions: Record = { - top: { emit: (): void => this.scrollbar.isVerticallyScrollable() ? this.reachedTop.emit() : null }, - bottom: { emit: (): void => this.scrollbar.isVerticallyScrollable() ? this.reachedBottom.emit() : null }, - start: { emit: (): void => this.scrollbar.isHorizontallyScrollable() ? this.reachedStart.emit() : null }, - end: { emit: (): void => this.scrollbar.isHorizontallyScrollable() ? this.reachedEnd.emit() : null } + readonly eventActions: Record = { + top: { emit: (): void => this.scrollbar.isVerticallyScrollable() ? this.top.emit() : null }, + bottom: { emit: (): void => this.scrollbar.isVerticallyScrollable() ? this.bottom.emit() : null }, + start: { emit: (): void => this.scrollbar.isHorizontallyScrollable() ? this.start.emit() : null }, + end: { emit: (): void => this.scrollbar.isHorizontallyScrollable() ? this.end.emit() : null } }; - private onReached(trigger: string): void { + private onAction(trigger: string): void { if (trigger) { - this.reachedEventActions[trigger]?.emit(); + this.eventActions[trigger]?.emit(); + } + } + + protected setCssVariable(property: string, value: number): void { + if (value) { + this.scrollbar.nativeElement.style.setProperty(property, `${ value }px`); } } @@ -103,13 +86,13 @@ export class NgScrollbarReached implements OnInit, OnDestroy { this.zone.runOutsideAngular(() => { // Create the scrollbars element inside the viewport this.triggerElementsWrapper = this.renderer.createElement('div'); - this.renderer.addClass(this.triggerElementsWrapper, 'ng-scroll-reached-wrapper'); + this.renderer.addClass(this.triggerElementsWrapper, this.triggerElementsWrapperClass); this.renderer.appendChild(this.scrollbar.viewport.contentWrapperElement, this.triggerElementsWrapper); // Create a trigger element for each subscribed event this.subscribedEvents.forEach((event: string) => { const triggerElement: HTMLElement = this.renderer.createElement('div'); - this.renderer.addClass(triggerElement, 'scroll-reached-trigger-element'); + this.renderer.addClass(triggerElement, this.triggerElementClass); this.renderer.setAttribute(triggerElement, 'trigger', event); this.renderer.appendChild(this.triggerElementsWrapper, triggerElement); this.triggerElements.push(triggerElement); @@ -122,10 +105,10 @@ export class NgScrollbarReached implements OnInit, OnDestroy { this.intersectionObserver = new IntersectionObserver((entries: IntersectionObserverEntry[]) => { if (intersectionObserverInit) { entries.forEach((entry: IntersectionObserverEntry) => { - if (entry.isIntersecting) { + if (this.isTriggered(entry)) { // Forward the detected trigger element only after the observer is initialized // Only observe the trigger elements when scrollable - this.zone.run(() => this.onReached(entry.target.getAttribute('trigger'))); + this.zone.run(() => this.onAction(entry.target.getAttribute('trigger'))); } }); } else { @@ -146,23 +129,17 @@ export class NgScrollbarReached implements OnInit, OnDestroy { this.triggerElements = []; } - private setCssVariable(property: string, value: number): void { - if (value) { - this.scrollbar.nativeElement.style.setProperty(property, `${ value }px`); - } - } - ngOnInit(): void { - if (this.reachedTop.observed) { + if (this.top.observed) { this.subscribedEvents.push('top'); } - if (this.reachedBottom.observed) { + if (this.bottom.observed) { this.subscribedEvents.push('bottom'); } - if (this.reachedStart.observed) { + if (this.start.observed) { this.subscribedEvents.push('start'); } - if (this.reachedBottom.observed) { + if (this.end.observed) { this.subscribedEvents.push('end'); } diff --git a/projects/ngx-scrollbar/src/lib/ng-scrollbar.scss b/projects/ngx-scrollbar/src/lib/ng-scrollbar.scss index 8d5afe8f..e9767d61 100644 --- a/projects/ngx-scrollbar/src/lib/ng-scrollbar.scss +++ b/projects/ngx-scrollbar/src/lib/ng-scrollbar.scss @@ -32,9 +32,6 @@ // Scrollbar track color --scrollbar-track-color: transparent; - // Scrollbar track transition - --scrollbar-track-transition: none; - // Scrollbar thumb color --scrollbar-thumb-color: rgb(0 0 0 / 20%); diff --git a/projects/ngx-scrollbar/src/lib/scrollbar/shared.scss b/projects/ngx-scrollbar/src/lib/scrollbar/shared.scss index 5554da54..a471f555 100644 --- a/projects/ngx-scrollbar/src/lib/scrollbar/shared.scss +++ b/projects/ngx-scrollbar/src/lib/scrollbar/shared.scss @@ -45,7 +45,6 @@ width: 100%; height: 100%; background-color: var(--scrollbar-track-color); - transition: var(--scrollbar-track-transition); border-radius: var(--scrollbar-border-radius); cursor: default; z-index: 1; diff --git a/projects/ngx-scrollbar/src/lib/styles/reached-events.scss b/projects/ngx-scrollbar/src/lib/styles/reached-events.scss index 6f449c19..bf35a7ab 100644 --- a/projects/ngx-scrollbar/src/lib/styles/reached-events.scss +++ b/projects/ngx-scrollbar/src/lib/styles/reached-events.scss @@ -7,44 +7,60 @@ --reached-offset-start: var(--reached-offset); --reached-offset-end: var(--reached-offset); + --dropped-offset: 1px; + --dropped-offset-top: var(--dropped-offset); + --dropped-offset-bottom: var(--dropped-offset); + --dropped-offset-start: var(--dropped-offset); + --dropped-offset-end: var(--dropped-offset); + + @include selector.LTR { - ::ng-deep .scroll-reached-trigger-element { - &[trigger="start"] { - left: 0; - right: unset; - } + ::ng-deep { + .scroll-reached-trigger-element, + .scroll-dropped-trigger-element { + &[trigger="start"] { + left: 0; + right: unset; + } - &[trigger="end"] { - right: 0; - left: unset; + &[trigger="end"] { + right: 0; + left: unset; + } } } } @include selector.RTL { - ::ng-deep .scroll-reached-trigger-element { - &[trigger="start"] { - right: 0; - left: unset; - } + ::ng-deep { + .scroll-reached-trigger-element, + .scroll-dropped-trigger-element { + &[trigger="start"] { + right: 0; + left: unset; + } - &[trigger="end"] { - left: 0; - right: unset; + &[trigger="end"] { + left: 0; + right: unset; + } } } } ::ng-deep { .ng-scroll-reached-wrapper, - .scroll-reached-trigger-element { + .ng-scroll-dropped-wrapper, + .scroll-reached-trigger-element, + .scroll-dropped-trigger-element { position: absolute; user-select: none; pointer-events: none; z-index: -9999; } - .ng-scroll-reached-wrapper { + .ng-scroll-reached-wrapper, + .ng-scroll-dropped-wrapper { visibility: hidden; top: 0; left: 0; @@ -56,7 +72,8 @@ // Should only hide the END trigger when content isn't horizontally scrollable &[isHorizontallyScrollable="false"] { - .scroll-reached-trigger-element { + .scroll-reached-trigger-element , + .scroll-dropped-trigger-element { &[trigger="end"] { display: none; } @@ -65,7 +82,8 @@ // Should only hide the BOTTOM trigger when content isn't vertically scrollable &[isVerticallyScrollable="false"] { - .scroll-reached-trigger-element { + .scroll-reached-trigger-element , + .scroll-dropped-trigger-element { &[trigger="bottom"] { display: none; } @@ -104,4 +122,36 @@ } } } + + .scroll-dropped-trigger-element { + background: blue; + + &[trigger="top"], &[trigger="bottom"] { + left: 0; + right: 0; + } + + &[trigger="start"], &[trigger="end"] { + top: 0; + bottom: 0; + } + + &[trigger="top"] { + top: 0; + height: var(--dropped-offset-top); + } + + &[trigger="bottom"] { + bottom: 0; + height: var(--dropped-offset-bottom); + } + + &[trigger="start"] { + width: var(--dropped-offset-start); + } + + &[trigger="end"] { + width: var(--dropped-offset-end); + } + } } diff --git a/projects/ngx-scrollbar/src/lib/tests/dropped-events.spec.ts b/projects/ngx-scrollbar/src/lib/tests/dropped-events.spec.ts new file mode 100644 index 00000000..f94d1606 --- /dev/null +++ b/projects/ngx-scrollbar/src/lib/tests/dropped-events.spec.ts @@ -0,0 +1,137 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Component, ViewChild } from '@angular/core'; +import { BidiModule } from '@angular/cdk/bidi'; +import { NgScrollbar, NgScrollbarModule } from 'ngx-scrollbar'; +import { NgScrollDropped } from 'ngx-scrollbar/reached-event'; + +@Component({ + template: ` + +
+
+ `, + standalone: true, + imports: [BidiModule, NgScrollbarModule, NgScrollDropped] +}) +class TestComponent { + offset: number; + topOffset: number; + bottomOffset: number; + startOffset: number; + endOffset: number; + isRtl: boolean = false; + disabled: boolean = false; + + @ViewChild(NgScrollbar, { static: true }) scrollbar: NgScrollbar; + + onScrollDropped(value: string): void { + console.log(value); + } +} + +describe('Dropped Events Directives', () => { + let fixture: ComponentFixture; + let component: TestComponent; + let onScrollDroppedSpy: jasmine.Spy; + + beforeEach(() => { + fixture = TestBed.createComponent(TestComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + onScrollDroppedSpy = spyOn(component, 'onScrollDropped'); + }); + + it('[DroppedOffset]: should emit (droppedTop) (droppedBottom) (droppedStart) (droppedEnd)', async () => { + component.offset = 10; + fixture.detectChanges(); + + await component.scrollbar.scrollTo({ top: 0, duration: 0 }); + await component.scrollbar.scrollTo({ top: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('top'); + await component.scrollbar.scrollTo({ bottom: 0, duration: 0 }); + await component.scrollbar.scrollTo({ bottom: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('bottom'); + await component.scrollbar.scrollTo({ end: 0, duration: 0 }); + await component.scrollbar.scrollTo({ end: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('end'); + await component.scrollbar.scrollTo({ start: 0, duration: 0 }); + await component.scrollbar.scrollTo({ start: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('start'); + }); + + it('[DroppedTopEvent]: should emit (droppedTop)', async () => { + component.topOffset = 10; + fixture.detectChanges(); + + await component.scrollbar.scrollTo({ top: 0, duration: 0 }); + await component.scrollbar.scrollTo({ top: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('top'); + }); + + it('[DroppedBottomEvent]: should emit (droppedBottom)', async () => { + component.bottomOffset = 10; + fixture.detectChanges(); + + await component.scrollbar.scrollTo({ bottom: 0, duration: 0 }); + await component.scrollbar.scrollTo({ bottom: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('bottom'); + }); + + it('[DroppedStartEvent]: should emit (droppedStart)', async () => { + component.startOffset = 10; + fixture.detectChanges(); + + await component.scrollbar.scrollTo({ start: 0, duration: 0 }); + await component.scrollbar.scrollTo({ start: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('start'); + }); + + it('[DroppedEndEvent]: should emit (droppedEnd)', async () => { + component.endOffset = 10; + fixture.detectChanges(); + + await component.scrollbar.scrollTo({ end: 0, duration: 0 }); + await component.scrollbar.scrollTo({ end: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('end'); + }); + + it('[DroppedStartEvent]: should emit (droppedStart) in RTL mode', async () => { + component.startOffset = 10; + component.isRtl = true; + fixture.detectChanges(); + + await component.scrollbar.scrollTo({ start: 0, duration: 0 }); + await component.scrollbar.scrollTo({ start: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('start'); + }); + + it('[DroppedEndEvent]: should emit (droppedEnd) in RTL mode', async () => { + component.endOffset = 10; + component.isRtl = true; + fixture.detectChanges(); + + await component.scrollbar.scrollTo({ end: 0, duration: 0 }); + await component.scrollbar.scrollTo({ end: 11, duration: 50 }); + expect(onScrollDroppedSpy).toHaveBeenCalledWith('end'); + }); + + + it('[reachDisabled]: should not emit when scroll is dropped destination', async () => { + component.disabled = true; + fixture.detectChanges(); + await component.scrollbar.scrollTo({ bottom: 0, duration: 0 }); + await component.scrollbar.scrollTo({ bottom: 11, duration: 50 }); + expect(onScrollDroppedSpy).not.toHaveBeenCalledWith('bottom'); + }); +}); diff --git a/projects/ngx-scrollbar/src/lib/tests/reached-events.spec.ts b/projects/ngx-scrollbar/src/lib/tests/reached-events.spec.ts index 22b54ebd..121ecea1 100644 --- a/projects/ngx-scrollbar/src/lib/tests/reached-events.spec.ts +++ b/projects/ngx-scrollbar/src/lib/tests/reached-events.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Component, ViewChild } from '@angular/core'; import { BidiModule } from '@angular/cdk/bidi'; import { NgScrollbar, NgScrollbarModule } from 'ngx-scrollbar'; -import { NgScrollbarReachedModule } from 'ngx-scrollbar/reached-event'; +import { NgScrollReached } from 'ngx-scrollbar/reached-event'; @Component({ template: ` @@ -22,7 +22,7 @@ import { NgScrollbarReachedModule } from 'ngx-scrollbar/reached-event'; `, standalone: true, - imports: [BidiModule, NgScrollbarModule, NgScrollbarReachedModule] + imports: [BidiModule, NgScrollbarModule, NgScrollReached] }) class TestComponent { offset: number; diff --git a/projects/ngx-scrollbar/src/lib/tests/visibility.spec.ts b/projects/ngx-scrollbar/src/lib/tests/visibility.spec.ts index 2608e780..51fba101 100644 --- a/projects/ngx-scrollbar/src/lib/tests/visibility.spec.ts +++ b/projects/ngx-scrollbar/src/lib/tests/visibility.spec.ts @@ -75,7 +75,6 @@ describe('Visibility styles', () => { it('[Visibility] should be able to override styles related to scrollbar track using CSS variables', async () => { setDimensions(component, { cmpWidth: 100, cmpHeight: 100, contentWidth: 100, contentHeight: 200 }); // Override track color and transition using CSS variables - component.nativeElement.style.setProperty('--scrollbar-track-transition', 'height 0.66s linear, width 0.33s linear'); component.nativeElement.style.setProperty('--scrollbar-track-color', 'red'); component.ngOnInit(); component.ngAfterViewInit(); @@ -84,7 +83,6 @@ describe('Visibility styles', () => { const trackDebugElement: DebugElement = fixture.debugElement.query(By.directive(TrackYDirective)); const trackStyles: CSSStyleDeclaration = getComputedStyle(trackDebugElement.nativeElement); - expect(trackStyles.transition).toBe('height 0.66s linear 0s, width 0.33s linear 0s'); expect(trackStyles.backgroundColor).toBe('rgb(255, 0, 0)'); }); diff --git a/projects/ngx-scrollbar/src/lib/thumb/thumb.ts b/projects/ngx-scrollbar/src/lib/thumb/thumb.ts index 582a3896..6da4d569 100644 --- a/projects/ngx-scrollbar/src/lib/thumb/thumb.ts +++ b/projects/ngx-scrollbar/src/lib/thumb/thumb.ts @@ -13,7 +13,7 @@ export class ThumbXDirective extends ThumbAdapter { } get size(): number { - return this.clientRect.width; + return this.nativeElement.offsetWidth; } protected get dragStartOffset(): number { @@ -33,7 +33,7 @@ export class ThumbYDirective extends ThumbAdapter { } get size(): number { - return this.clientRect.height; + return this.nativeElement.offsetHeight; } protected get dragStartOffset(): number { diff --git a/projects/ngx-scrollbar/src/lib/track/track.ts b/projects/ngx-scrollbar/src/lib/track/track.ts index c6877f78..3b35aef8 100644 --- a/projects/ngx-scrollbar/src/lib/track/track.ts +++ b/projects/ngx-scrollbar/src/lib/track/track.ts @@ -17,7 +17,7 @@ export class TrackXDirective extends TrackAdapter { } get size(): number { - return this.clientRect.width; + return this.nativeElement.offsetWidth; } protected get viewportScrollSize(): number { @@ -104,7 +104,7 @@ export class TrackYDirective extends TrackAdapter { } get size(): number { - return this.clientRect.height; + return this.nativeElement.offsetHeight; } protected get viewportScrollSize(): number { diff --git a/projects/ngx-scrollbar/src/lib/viewport/viewport-adapter.ts b/projects/ngx-scrollbar/src/lib/viewport/viewport-adapter.ts index 8b6e4806..b9f8feae 100644 --- a/projects/ngx-scrollbar/src/lib/viewport/viewport-adapter.ts +++ b/projects/ngx-scrollbar/src/lib/viewport/viewport-adapter.ts @@ -20,12 +20,12 @@ export class ViewportAdapter { /** Viewport clientHeight */ get offsetHeight(): number { - return this.nativeElement.getBoundingClientRect().height; + return this.nativeElement.offsetHeight; } /** Viewport clientWidth */ get offsetWidth(): number { - return this.nativeElement.getBoundingClientRect().width; + return this.nativeElement.offsetWidth; } /** Viewport scrollTop */ @@ -40,12 +40,12 @@ export class ViewportAdapter { /** Content height, falls back to scroll height */ get contentHeight(): number { - return this.contentWrapperElement.getBoundingClientRect().height; + return this.contentWrapperElement.offsetHeight; } /** Content width, falls back to scroll height */ get contentWidth(): number { - return this.contentWrapperElement.getBoundingClientRect().width; + return this.contentWrapperElement.offsetWidth; } /** The horizontal remaining scrollable distance */ diff --git a/projects/ngx-scrollbar/utils/src/mat-select-viewport.ts b/projects/ngx-scrollbar/utils/src/mat-select-viewport.ts index 4644a690..9d954d8e 100644 --- a/projects/ngx-scrollbar/utils/src/mat-select-viewport.ts +++ b/projects/ngx-scrollbar/utils/src/mat-select-viewport.ts @@ -4,9 +4,9 @@ import { _NgScrollbar, NG_SCROLLBAR } from 'ngx-scrollbar'; @Directive({ standalone: true, - selector: 'ng-scrollbar[matSelectViewport], ng-scrollbar[mat-select-viewport]' + selector: 'ng-scrollbar[matSelectViewport]' }) -export class MatSelectViewport { +export class NgScrollbarMatSelectViewport { private readonly scrollbar: _NgScrollbar = inject(NG_SCROLLBAR);