diff --git a/CHANGELOG.md b/CHANGELOG.md index 1602d2c8..830a2272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 15.1.0 + +- feat: Add a new option `hoverOffset` to activate hover effect on the offset area around the scrollbar, closes [#616](https://github.com/MurhafSousli/ngx-scrollbar/issues/616). +- fix: Scrollbar thumb color is not changing on hover, closes [#625](https://github.com/MurhafSousli/ngx-scrollbar/issues/625). + ## 15.0.4 - fix: global Angular app styles gets broken in Firefox, closes [#615](https://github.com/MurhafSousli/ngx-scrollbar/issues/615). 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 73c443bc..10cc13e3 100644 --- a/projects/ngx-scrollbar-demo/src/app/example2/example2.component.scss +++ b/projects/ngx-scrollbar-demo/src/app/example2/example2.component.scss @@ -1,5 +1,6 @@ ng-scrollbar { --scrollbar-thickness: 7; + --scrollbar-hover-thickness: 7; --scrollbar-offset: 16; --scrollbar-track-color: rgb(0 0 0 / 10%); --scrollbar-thumb-color: rgb(0 0 0 / 20%); 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 de0f4379..7de502a9 100644 --- a/projects/ngx-scrollbar-demo/src/app/lab/lab.component.html +++ b/projects/ngx-scrollbar-demo/src/app/lab/lab.component.html @@ -61,6 +61,15 @@ +
+
Hover offset
+ + True + False + +
+
Buttons
- + Styling @@ -153,6 +164,7 @@ [position]="position" [style]="cssVariables" [buttons]="buttons" + [hoverOffset]="hoverOffset" [disableInteraction]="interactionDisabled" [disableSensor]="disableSensor" [sensorThrottleTime]="sensorThrottleTime" 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 450c0d91..bbc2e745 100644 --- a/projects/ngx-scrollbar-demo/src/app/lab/lab.component.ts +++ b/projects/ngx-scrollbar-demo/src/app/lab/lab.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, signal, ViewChild, WritableSignal } from '@angular/core'; +import { Component, inject, signal, ViewChild, WritableSignal, ChangeDetectionStrategy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; @@ -51,10 +51,15 @@ import { SmoothScrollFormComponent, SmoothScrollOptionsForm } from './smooth-scr }) export class LabComponent { + private sanitizer: DomSanitizer = inject(DomSanitizer); + @ViewChild(NgScrollbar, { static: true }) component: NgScrollbar; + stylingPanelExpanded: boolean; + direction: 'ltr' | 'rtl' = 'ltr'; buttons: boolean = true; + hoverOffset: boolean = false; interactionDisabled: boolean = false; disableSensor: boolean = false; disableReached: boolean = false; @@ -97,9 +102,6 @@ export class LabComponent { return 'Lorem ipsum dolor sit amet' + content.repeat(this.slider.contentSize); } - constructor(private sanitizer: DomSanitizer) { - } - width: number = 448; height: number = 300; id: number = -1; @@ -154,6 +156,11 @@ export class LabComponent { } } + onExpandedChange(expanded: boolean): void { + if (!expanded) { + this.stylingPanelExpanded = false; + } + } } const content: string = ` diff --git a/projects/ngx-scrollbar/package.json b/projects/ngx-scrollbar/package.json index aab5c0ed..80f584d1 100644 --- a/projects/ngx-scrollbar/package.json +++ b/projects/ngx-scrollbar/package.json @@ -1,6 +1,6 @@ { "name": "ngx-scrollbar", - "version": "15.0.4", + "version": "15.1.0", "license": "MIT", "homepage": "https://ngx-scrollbar.netlify.com/", "author": { @@ -20,9 +20,9 @@ "scroll-reached" ], "peerDependencies": { - "@angular/common": ">=17.0.0", - "@angular/core": ">=17.0.0", - "@angular/cdk": ">=17.0.0", + "@angular/common": ">=17.1.0", + "@angular/core": ">=17.1.0", + "@angular/cdk": ">=17.1.0", "rxjs": ">=7.0.0" }, "dependencies": { diff --git a/projects/ngx-scrollbar/src/lib/ng-scrollbar-core.ts b/projects/ngx-scrollbar/src/lib/ng-scrollbar-core.ts index bad8a7aa..6de5ad99 100644 --- a/projects/ngx-scrollbar/src/lib/ng-scrollbar-core.ts +++ b/projects/ngx-scrollbar/src/lib/ng-scrollbar-core.ts @@ -58,7 +58,8 @@ const defaultOptions: NgScrollbarOptions = { sensorThrottleTime: 0, disableSensor: false, disableInteraction: false, - buttons: false + buttons: false, + hoverOffset: false }; interface ViewportState { @@ -154,6 +155,11 @@ export abstract class NgScrollbarCore implements _NgScrollbar, OnInit, AfterView transform: numberAttribute }); + /** A flag used to activate hover effect on the offset area around the scrollbar */ + hoverOffset: InputSignalWithTransform = input(this.options.hoverOffset, { + transform: booleanAttribute + }); + viewportDimension: WritableSignal = signal({ contentHeight: 0, contentWidth: 0, diff --git a/projects/ngx-scrollbar/src/lib/ng-scrollbar.model.ts b/projects/ngx-scrollbar/src/lib/ng-scrollbar.model.ts index 2f1acee4..448fffe9 100644 --- a/projects/ngx-scrollbar/src/lib/ng-scrollbar.model.ts +++ b/projects/ngx-scrollbar/src/lib/ng-scrollbar.model.ts @@ -66,4 +66,6 @@ export interface NgScrollbarOptions { disableSensor?: boolean; /** Show scrollbar buttons */ buttons?: boolean; + /** A flag used to activate hover effect on the offset area around the scrollbar */ + hoverOffset?: boolean; } diff --git a/projects/ngx-scrollbar/src/lib/scrollbar/horizontal.scss b/projects/ngx-scrollbar/src/lib/scrollbar/horizontal.scss index e2122e49..62dc0c67 100644 --- a/projects/ngx-scrollbar/src/lib/scrollbar/horizontal.scss +++ b/projects/ngx-scrollbar/src/lib/scrollbar/horizontal.scss @@ -56,11 +56,11 @@ .ng-scrollbar-track-wrapper { height: var(--_track-x-thickness); flex-direction: row; +} - &:hover { - --_track-x-thickness: var(--_scrollbar-hover-thickness-px); - --_thumb-x-color: var(var(--INTERNAL-scrollbar-thumb-min-size)); - } +.ng-scrollbar-hover:hover { + --_track-x-thickness: var(--_scrollbar-hover-thickness-px); + --_thumb-x-color: var(--INTERNAL-scrollbar-thumb-hover-color); } .ng-scrollbar-thumb { diff --git a/projects/ngx-scrollbar/src/lib/scrollbar/scrollbar.ts b/projects/ngx-scrollbar/src/lib/scrollbar/scrollbar.ts index 971f073b..b8ca462b 100644 --- a/projects/ngx-scrollbar/src/lib/scrollbar/scrollbar.ts +++ b/projects/ngx-scrollbar/src/lib/scrollbar/scrollbar.ts @@ -11,8 +11,10 @@ import { ScrollbarManager } from '../utils/scrollbar-manager'; standalone: true, selector: 'scrollbar-y', template: ` -
-
+
+
@@ -68,8 +70,10 @@ export class ScrollbarY extends ScrollbarAdapter { selector: 'scrollbar-x', host: { '[attr.dir]': 'cmp.direction()' }, template: ` -
-
+
+
diff --git a/projects/ngx-scrollbar/src/lib/scrollbar/shared.scss b/projects/ngx-scrollbar/src/lib/scrollbar/shared.scss index 7aa5002b..f4224c64 100644 --- a/projects/ngx-scrollbar/src/lib/scrollbar/shared.scss +++ b/projects/ngx-scrollbar/src/lib/scrollbar/shared.scss @@ -19,6 +19,7 @@ z-index: 100; opacity: var(--_scrollbar-hover-opacity); transition: var(--_scrollbar-opacity-transition); + pointer-events: var(--_scrollbar-pointer-events); } .ng-scrollbar-track-wrapper { @@ -32,7 +33,6 @@ bottom: var(--_scrollbar-track-bottom); right: var(--_scrollbar-track-right); left: var(--_scrollbar-track-left); - pointer-events: var(--_scrollbar-pointer-events); transition: var(--INTERNAL-scrollbar-track-wrapper-transition); position: absolute; overflow: hidden; diff --git a/projects/ngx-scrollbar/src/lib/scrollbar/vertical.scss b/projects/ngx-scrollbar/src/lib/scrollbar/vertical.scss index e98e951d..3fec1278 100644 --- a/projects/ngx-scrollbar/src/lib/scrollbar/vertical.scss +++ b/projects/ngx-scrollbar/src/lib/scrollbar/vertical.scss @@ -24,11 +24,11 @@ .ng-scrollbar-track-wrapper { width: var(--_track-y-thickness); flex-direction: column; +} - &:hover { - --_track-y-thickness: var(--_scrollbar-hover-thickness-px); - --_thumb-y-color: var(var(--INTERNAL-scrollbar-thumb-min-size)); - } +.ng-scrollbar-hover:hover { + --_track-y-thickness: var(--_scrollbar-hover-thickness-px); + --_thumb-y-color: var(--INTERNAL-scrollbar-thumb-hover-color); } .ng-scrollbar-thumb { diff --git a/projects/ngx-scrollbar/src/lib/tests/appearance.spec.ts b/projects/ngx-scrollbar/src/lib/tests/appearance.spec.ts index a1469ca9..3c172430 100644 --- a/projects/ngx-scrollbar/src/lib/tests/appearance.spec.ts +++ b/projects/ngx-scrollbar/src/lib/tests/appearance.spec.ts @@ -27,9 +27,8 @@ describe('Appearance [native / compact] styles', () => { fixture = TestBed.createComponent(NgScrollbar); component = fixture.componentInstance; - // Set scrollbar thickness to 13px + component.nativeElement.style.setProperty('--scrollbar-thickness', '5'); - component.nativeElement.style.setProperty('--scrollbar-margin', '4'); component.nativeElement.style.setProperty('--scrollbar-offset', '4'); }); diff --git a/projects/ngx-scrollbar/src/lib/tests/hover-effect.spec.ts b/projects/ngx-scrollbar/src/lib/tests/hover-effect.spec.ts new file mode 100644 index 00000000..b9087a9a --- /dev/null +++ b/projects/ngx-scrollbar/src/lib/tests/hover-effect.spec.ts @@ -0,0 +1,64 @@ +import { ComponentFixture, ComponentFixtureAutoDetect, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { firstValueFrom } from 'rxjs'; +import { NgScrollbar } from 'ngx-scrollbar'; +import { setDimensions } from './common-test.'; + +describe('Hover effect', () => { + let component: NgScrollbar; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NgScrollbar], + providers: [ + { provide: ComponentFixtureAutoDetect, useValue: true } + ] + }).compileComponents(); + + fixture = TestBed.createComponent(NgScrollbar); + component = fixture.componentInstance; + component.nativeElement.style.setProperty('--scrollbar-thickness', '5'); + component.nativeElement.style.setProperty('--scrollbar-hover-thickness', '10'); + component.nativeElement.style.setProperty('--scrollbar-offset', '4'); + }); + + it('Should activate hover effect only when mouse is over the scrollbar in case [hoverOffset]="false"', async () => { + setDimensions(component, { cmpHeight: 200, cmpWidth: 200, contentHeight: 400, contentWidth: 400 }); + fixture.componentRef.setInput('hoverOffset', false); + component.ngOnInit(); + component.ngAfterViewInit(); + await firstValueFrom(component.afterInit); + + const stickyYElement: Element = fixture.debugElement.query(By.css('scrollbar-y .ng-scrollbar-sticky')).nativeElement; + const stickyXElement: Element = fixture.debugElement.query(By.css('scrollbar-x .ng-scrollbar-sticky')).nativeElement; + + const trackYElement: Element = fixture.debugElement.query(By.css('scrollbar-y .ng-scrollbar-track-wrapper')).nativeElement; + const trackXElement: Element = fixture.debugElement.query(By.css('scrollbar-x .ng-scrollbar-track-wrapper')).nativeElement; + + expect(stickyYElement.classList).not.toContain('ng-scrollbar-hover'); + expect(stickyXElement.classList).not.toContain('ng-scrollbar-hover'); + expect(trackYElement.classList).toContain('ng-scrollbar-hover'); + expect(trackXElement.classList).toContain('ng-scrollbar-hover'); + }); + + it('Should activate hover effect when mouse is over the offset area in case [hoverOffset]="true"', async () => { + setDimensions(component, { cmpHeight: 200, cmpWidth: 200, contentHeight: 400, contentWidth: 400 }); + fixture.componentRef.setInput('hoverOffset', true); + component.ngOnInit(); + component.ngAfterViewInit(); + await firstValueFrom(component.afterInit); + + const stickyYElement: Element = fixture.debugElement.query(By.css('scrollbar-y .ng-scrollbar-sticky')).nativeElement; + const stickyXElement: Element = fixture.debugElement.query(By.css('scrollbar-x .ng-scrollbar-sticky')).nativeElement; + + const trackYElement: Element = fixture.debugElement.query(By.css('scrollbar-y .ng-scrollbar-track-wrapper')).nativeElement; + const trackXElement: Element = fixture.debugElement.query(By.css('scrollbar-x .ng-scrollbar-track-wrapper')).nativeElement; + + expect(stickyYElement.classList).toContain('ng-scrollbar-hover'); + expect(stickyXElement.classList).toContain('ng-scrollbar-hover'); + expect(trackYElement.classList).not.toContain('ng-scrollbar-hover'); + expect(trackXElement.classList).not.toContain('ng-scrollbar-hover'); + }); +}); + diff --git a/projects/ngx-scrollbar/src/lib/tests/visibility.spec.ts b/projects/ngx-scrollbar/src/lib/tests/visibility.spec.ts index 51fba101..486ea6db 100644 --- a/projects/ngx-scrollbar/src/lib/tests/visibility.spec.ts +++ b/projects/ngx-scrollbar/src/lib/tests/visibility.spec.ts @@ -15,7 +15,7 @@ describe('Visibility styles', () => { imports: [NgScrollbar], providers: [ { provide: ComponentFixtureAutoDetect, useValue: true } - ], + ] }).compileComponents(); fixture = TestBed.createComponent(NgScrollbar); diff --git a/projects/ngx-scrollbar/src/lib/utils/scrollbar-base.ts b/projects/ngx-scrollbar/src/lib/utils/scrollbar-base.ts index 6da3c861..d19a4132 100644 --- a/projects/ngx-scrollbar/src/lib/utils/scrollbar-base.ts +++ b/projects/ngx-scrollbar/src/lib/utils/scrollbar-base.ts @@ -16,6 +16,7 @@ export interface _NgScrollbar { dragging: WritableSignal; direction: Signal; trackScrollDuration: number; + hoverOffset: Signal; buttons: Signal; disableSensor: Signal; sensorThrottleTime: Signal;