Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
MurhafSousli committed Feb 18, 2024
1 parent 108fa48 commit 696306e
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
ng-scrollbar {
--scrollbar-thumb-color: var(--color-primary);
--scrollbar-thickness: 8;
--scrollbar-track-color: rgba(0, 0, 0, 0.2);
}

.mat-mdc-select {
margin-bottom: 30px !important;
color: white;
}

Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,18 @@
<div class="sample">
<header>TODO</header>
<ng-scrollbar #scrollbarRef="ngScrollbar"
externalViewport=".test"
externalViewport
appearance="compact"
(afterInit)="onScrollbarUpdate(scrollbarRef)"
(afterUpdate)="onScrollbarUpdate(scrollbarRef)">
<div style="height: 100%">
<div style="height: 100%">
<div class="test">
<ul>
<li></li>
<li *ngFor="let item of list"
[class.not]="!item.completed"
[class.ok]="item.completed">{{item.title}}</li>
<li></li>
</ul>
</div>
</div>
<div scrollViewport>
<ul>
<li></li>
<li *ngFor="let item of list"
[class.not]="!item.completed"
[class.ok]="item.completed">{{item.title}}</li>
<li></li>
</ul>
</div>
</ng-scrollbar>
</div>
Expand Down
16 changes: 12 additions & 4 deletions projects/ngx-scrollbar-demo/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ body {
--color-warn: #{mat.get-color-from-palette($app-warn)};
--color-success: #{mat.get-color-from-palette($app-success)};

--mat-sidenav-content-background-color: #{mat.get-color-from-palette(mat.define-palette($app-primary, 900))};
--mat-sidenav-content-background-color: #{mat.get-color-from-palette(mat.define-palette($app-primary, 900))};

--mat-sidenav-container-background-color: #{mat.get-color-from-palette(mat.define-palette($app-primary, 500))};
--mat-sidenav-container-background-color: #{mat.get-color-from-palette(mat.define-palette($app-primary, 500))};


--color-orange-passion: #{mat.get-color-from-palette(mat.define-palette($app-accent, 500))};
--color-track-orange-passion: #975857;
--color-orange-passion: #{mat.get-color-from-palette(mat.define-palette($app-accent, 500))};
--color-track-orange-passion: #975857;

--color-app-background: #{mat.get-color-from-palette(mat.define-palette($app-primary, 400))};
--color-app-logo: var(--color-app-background);
Expand All @@ -95,6 +95,8 @@ body {
--color-example3: #{mat.get-color-from-palette(mat.define-palette(mat.$yellow-palette, 700))};
--color-example4: var(--color-light-gray);
--color-highlight: #3cffe3;

--mat-select-panel-background-color: var(--color-accent);
}

* {
Expand Down Expand Up @@ -193,3 +195,9 @@ ul {
height: 32px;
align-items: center;
}

.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);
}
8 changes: 4 additions & 4 deletions projects/ngx-scrollbar/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ngx-scrollbar",
"version": "14.0.0-beta.0",
"version": "14.0.0-beta.1",
"license": "MIT",
"homepage": "https://ngx-scrollbar.netlify.com/",
"author": {
Expand All @@ -20,9 +20,9 @@
"scroll-reached"
],
"peerDependencies": {
"@angular/common": "^17.0.0",
"@angular/core": "^17.0.0",
"@angular/cdk": ">=16.0.0"
"@angular/common": ">=17.0.0",
"@angular/core": ">=17.0.0",
"@angular/cdk": ">=17.0.0"
},
"dependencies": {
"tslib": "^2.3.0"
Expand Down
2 changes: 2 additions & 0 deletions projects/ngx-scrollbar/src/lib/ng-scrollbar-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,10 @@ export abstract class NgScrollbarCore implements _NgScrollbar, OnInit, AfterView
/** Resize observer subscription */
private sizeChangeSub: Subscription;

/** Viewport adapter instance */
abstract viewport: ViewportAdapter;

/** The scrollbars component instance used for testing purpose */
abstract scrollbars: Scrollbars;

ngOnInit(): void {
Expand Down
74 changes: 51 additions & 23 deletions projects/ngx-scrollbar/src/lib/ng-scrollbar-ext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import {
Input,
ContentChild,
inject,
createComponent,
OnInit,
Injector,
Renderer2,
ComponentRef,
ApplicationRef,
ChangeDetectionStrategy,
ComponentFactoryResolver
ChangeDetectionStrategy
} from '@angular/core';
import { ScrollViewport, ViewportAdapter } from './viewport';
import { NgScrollbar } from './ng-scrollbar';
Expand All @@ -24,6 +25,10 @@ import { Scrollbars } from './scrollbars/scrollbars';
styleUrls: ['ng-scrollbar.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
// This component appends a content wrapper element to the viewport
// A hydration mismatch error will be thrown (NG0500) during DOM manipulation.
// To avoid this error, the 'ngSkipHydration' attribute is added to skip hydration.
ngSkipHydration: 'true',
'[class.ng-scrollbar-external-viewport]': 'true'
},
providers: [
Expand All @@ -33,34 +38,33 @@ import { Scrollbars } from './scrollbars/scrollbars';
})
export class NgScrollbarExt extends NgScrollbarCore implements OnInit {

private readonly appRef: ApplicationRef = inject(ApplicationRef);
private readonly renderer: Renderer2 = inject(Renderer2);

private readonly cfr: ComponentFactoryResolver = inject(ComponentFactoryResolver);
private readonly appRef: ApplicationRef = inject(ApplicationRef);

private viewportAdapter: ViewportAdapter;

private scrollbarWrapperRef: ComponentRef<Scrollbars>;

get viewport(): ViewportAdapter {
return this.viewportAdapter;
}

get scrollbars(): Scrollbars {
return this.scrollbarWrapperRef?.instance;
}
scrollbars: Scrollbars;

/**
* The selector used to query the viewport element
* Selector used to query the viewport element.
*/
@Input() externalViewport: string;

/**
* The selector used to query the content wrapper element
* Selector used to query the content wrapper element.
*/
@Input() externalContentWrapper: string;

/**
* The selector used to query the spacer element (virtual scroll integration)
* Selector used to query the spacer element (virtual scroll integration).
* In the case of integrating the scrollbar with a virtual scroll component,
* a spacer element is typically created to match the real size of the content.
* The scrollbar will use the size of this spacer element for calculations instead of the content wrapper size.
*/
@Input() externalSpacer: string;

Expand All @@ -85,33 +89,57 @@ export class NgScrollbarExt extends NgScrollbarCore implements OnInit {
}
}

// If an external spacer selector is provided, attempt to query for it
let spacerElement: HTMLElement;
if (this.externalSpacer) {
spacerElement = this.nativeElement.querySelector(this.externalSpacer);
if (!spacerElement) {
console.error(`[NgScrollbar]: Could not find the spacer element for the provided selector "${ this.externalSpacer }"`);
console.error(`[NgScrollbar]: Spacer element not found for the provided selector "${ this.externalSpacer }"`);
}
}

// If an external content wrapper selector is provided, attempt to query for it
let contentWrapperElement: HTMLElement;
if (this.externalContentWrapper) {
contentWrapperElement = this.nativeElement.querySelector(this.externalContentWrapper);
if (!contentWrapperElement) {
console.error(`[NgScrollbar]: Could not find the content wrapper element for the provided selector "${ this.externalContentWrapper }"`);
console.error(`[NgScrollbar]: Content wrapper element not found for the provided selector "${ this.externalContentWrapper }"`);
}
}

this.viewport.init(contentWrapperElement, spacerElement);
// If no external spacer or content wrapper is provided, create a content wrapper element
if (!this.externalSpacer && !this.externalContentWrapper) {
contentWrapperElement = this.renderer.createElement('div');

// Move all content of the viewport into the content wrapper
const childNodes: ChildNode[] = Array.from(this.viewport.nativeElement.childNodes);
childNodes.forEach((node: ChildNode) => this.renderer.appendChild(contentWrapperElement, node));

// Create/destroy ScrollbarWrapper component when disabled state changes
if (!this.scrollbarWrapperRef) {
const injector: Injector = Injector.create({ providers: [{ provide: NG_SCROLLBAR, useValue: this }] });
this.scrollbarWrapperRef = this.cfr.resolveComponentFactory(Scrollbars).create(injector);
// Attach the host view of the component to the main change detection tree, so that its lifecycle hooks will run.
this.appRef.attachView(this.scrollbarWrapperRef.hostView);
// Move the created component inside the content wrapper
this.viewport.contentWrapperElement.appendChild(this.scrollbarWrapperRef.location.nativeElement);
// Append the content wrapper to the viewport
this.renderer.appendChild(this.viewport.nativeElement, contentWrapperElement);
}

// Initialize viewport
this.viewport.init(contentWrapperElement, spacerElement);

// Attach scrollbars
this.attachScrollbars();

super.ngOnInit();
}

private attachScrollbars(): void {
// Create the scrollbars component
const scrollbarsRef: ComponentRef<Scrollbars> = createComponent(Scrollbars, {
environmentInjector: this.appRef.injector,
elementInjector: Injector.create({ providers: [{ provide: NG_SCROLLBAR, useValue: this }] })
});
// Attach scrollbar to the content wrapper
this.viewport.contentWrapperElement.appendChild(scrollbarsRef.location.nativeElement);
// Attach the host view of the component to the main change detection tree, so that its lifecycle hooks run.
this.appRef.attachView(scrollbarsRef.hostView);
// Set the scrollbars instance
this.scrollbars = scrollbarsRef.instance;

}
}
24 changes: 11 additions & 13 deletions projects/ngx-scrollbar/src/lib/viewport/viewport-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { ViewportClasses } from '../ng-scrollbar.model';

export class ViewportAdapter {

/** The element that wraps the content inside the viewport,
* Used to measure the content size and observe content size changes */
/**
* The element that wraps the content inside the viewport,
* used to measure the content size and observe its changes
*/
contentWrapperElement: HTMLElement;

/** Viewport clientHeight */
Expand Down Expand Up @@ -53,24 +55,20 @@ export class ViewportAdapter {
/**
* Initialize viewport
*/
init(contentSelector?: HTMLElement, spacerSelector?: HTMLElement): void {
init(contentSelector: HTMLElement, spacerSelector?: HTMLElement): void {
// Add content wrapper class
contentSelector.classList.add(ViewportClasses.Content);

// When integrating the scrollbar with virtual scroll, the content wrapper will have fake size,
// and a spacer element will have the real size
// Therefore, if spaceElement is provided, it will be observed instead of the content wrapper
if (spacerSelector) {
// Set relative position on the spacer element to enable the functionality of sticky for the scrollbars
spacerSelector.style.position = 'relative';
this.contentWrapperElement = spacerSelector;
}

let realContentWrapper: HTMLElement = contentSelector ?? this.nativeElement?.firstElementChild as HTMLElement;

// Add content wrapper class
realContentWrapper?.classList.add(ViewportClasses.Content);

// If spacer is not provided, set it as the content wrapper
if (!this.contentWrapperElement && realContentWrapper) {
this.contentWrapperElement = realContentWrapper;
} else {
// If spacer is not provided, set it as the content wrapper
this.contentWrapperElement = contentSelector;
}
}

Expand Down
1 change: 1 addition & 0 deletions projects/ngx-scrollbar/utils/src/cdk-virtual-scrollbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class NgScrollbarCdkVirtualScroll {

constructor() {
this.scrollbar.externalViewport = '.cdk-virtual-scroll-viewport';
this.scrollbar.externalContentWrapper = '.cdk-virtual-scroll-content-wrapper';
this.scrollbar.externalSpacer = '.cdk-virtual-scroll-spacer';
}
}

0 comments on commit 696306e

Please sign in to comment.