diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b2f356b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Omnedia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed9172c --- /dev/null +++ b/README.md @@ -0,0 +1,151 @@ +# ngx-scrollbar + +`@omnedia/ngx-scrollbar` is an Angular library that provides a customizable and lightweight scrollbar component. This component offers a custom scrollbar with smooth scrolling functionality and allows for full control over its appearance through styling. + +## Features + +- Custom scrollbar for the y-Direction with adjustable styles and behavior. +- Smooth scrolling with click-and-drag functionality. +- Easily integratable into Angular projects as a standalone component. +- Allows for styling customization via CSS classes and inline styles. + +## Installation + +Install the library using npm: + +```bash +npm install @omnedia/ngx-scrollbar +``` + +## Usage + +Import the `NgxScrollbarComponent` in your Angular module or component: + +```typescript +import { NgxScrollbarComponent } from '@omnedia/ngx-scrollbar'; + +@Component({ + ... + imports: [ + ... + NgxScrollbarComponent, + ], + ... +}) +``` + +Use the component in your template: + +```html + +
+ +

Scrollable content 1

+

Scrollable content 2

+

Scrollable content 3

+
+
+``` + +## How It Works + +- Custom Scrollbar: The ngx-scrollbar component wraps your content inside a custom scrollbar container. The appearance of the scrollbar can be controlled via the styleClass input, allowing for full customization. +- Smooth Scrolling: The scrollbar supports smooth scrolling, and users can drag the scrollbar for precise control. +- Dynamic Calculation: The size of the scrollbar is calculated based on the content height relative to the container. + +## API + +```html + + + +``` + +- styleClass (optional): A custom CSS class to apply to the scrollbar component for additional styling. + +## Example + +```html + +
+

Item 1

+

Item 2

+

Item 3

+ +
+
+``` + +This will create a custom scrollbar for the content with the specified custom styles. + +## Styling + +You can style the component globally or use the styleClass input to apply your custom styles.
+Make sure to give the `om-scrollbar` component a height of 100% in your component.
+The `max-height` is defined by the components parent component. + +## Custom Scrollbar Styling Example + +In this example, the scrollbar bar is styled to be larger and colored differently: + +```html + +``` + +```css +/* Component styling */ +om-scrollbar { + height: 100%; +} + +.nav-bar { + height: 500px +} + +.nav-items { + display: flex; + flex-direction: column; + gap: 1rem; + font-size: 2rem; +} + + +/* Global styling */ +.custom-scrollbar.om-scrollbar-bar { + background-color: #ff6347; /* Tomato color for the scrollbar */ + width: 0.75rem; + border-radius: 12px; + opacity: 0.7; + transition: opacity 0.2s; +} + +.custom-scrollbar .om-scrollbar-bar:hover { + opacity: 1; /* While hovering */ +} + +.custom-scrollbar .om-scrollbar.om-scrollbar-bar-active { + opacity 1; /* While dragging */ +} +``` + +## Contributing + +Contributions are welcome. Please submit a pull request or open an issue to discuss your ideas. + +## License + +This project is licensed under the MIT License. \ No newline at end of file diff --git a/ng-package.json b/ng-package.json new file mode 100644 index 0000000..4429279 --- /dev/null +++ b/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/ngx-scrollbar", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..c682075 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "@omnedia/ngx-scrollbar", + "description": "A simple component library to create a custom scrollbar for the Y-Direction. ", + "version": "1.0.0", + "peerDependencies": { + "@angular/common": "^18.2.0", + "@angular/core": "^18.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + }, + "sideEffects": false, + "keywords": [ + "npm", + "scrollbar", + "y", + "direction", + "custom" + ], + "repository": { + "url": "https://github.com/omnedia/ngx-scrollbar" + }, + "author": "Markus Block (markus.block@omnedia.com)", + "license": "MIT" +} diff --git a/src/lib/ngx-scrollbar.component.html b/src/lib/ngx-scrollbar.component.html new file mode 100644 index 0000000..ca0a6cb --- /dev/null +++ b/src/lib/ngx-scrollbar.component.html @@ -0,0 +1,10 @@ +
+
+
+ +
+
+ +
+
+
\ No newline at end of file diff --git a/src/lib/ngx-scrollbar.component.scss b/src/lib/ngx-scrollbar.component.scss new file mode 100644 index 0000000..d671788 --- /dev/null +++ b/src/lib/ngx-scrollbar.component.scss @@ -0,0 +1,44 @@ +.om-scrollbar { + --om-scrollbar-height: 0; + --om-scrollbar-top: 0; + --om-scrollbar-hover-display: block; + position: relative; + + height: 100%; + + .om-scrollbar-bar { + position: absolute; + display: var(--om-scrollbar-hover-display); + width: 0.625rem; + height: var(--om-scrollbar-height); + top: var(--om-scrollbar-top); + background-color: grey; + right: -1.5rem; + border-radius: 10px; + transition: opacity 0.2; + + &:hover, + &.om-scrollbar-bar-active { + opacity: 0.9; + } + } + + &:hover > .sidebar-scrollbar { + display: block; + } + + .om-scrollbar-wrapper { + overflow-y: auto; + height: 100%; + + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } + } + + .om-scrollbar-content { + height: fit-content; + } +} diff --git a/src/lib/ngx-scrollbar.component.ts b/src/lib/ngx-scrollbar.component.ts new file mode 100644 index 0000000..ca12757 --- /dev/null +++ b/src/lib/ngx-scrollbar.component.ts @@ -0,0 +1,89 @@ +import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ElementRef, HostListener, Input, ViewChild } from '@angular/core'; + +@Component({ + selector: 'om-scrollbar', + standalone: true, + imports: [CommonModule], + templateUrl: "./ngx-scrollbar.component.html", + styleUrl: "./ngx-scrollbar.component.scss", +}) +export class NgxScrollbarComponent implements AfterViewInit { + @ViewChild('OmScrollbarContainer') sidebarRef!: ElementRef; + @ViewChild('OmScrollbarWrapper') wrapperRef!: ElementRef; + @ViewChild('OmScrollbarContent') contentRef!: ElementRef; + @ViewChild('OmScrollbar') scrollbarRef!: ElementRef; + + @Input('styleClass') + styleClass?: string; + + style: any = {}; + + private scrollPercent = 0; + mouseDown = false; + private lastY?: number; + + @HostListener('mousedown', ['$event']) + onMouseDown(event: MouseEvent) { + const target = event.target as HTMLElement; + + if (target.classList.contains('om-scrollbar-bar')) { + event.preventDefault(); + this.mouseDown = true; + } + } + + @HostListener('mouseup') + onMouseUp() { + this.lastY = undefined; + this.mouseDown = false; + } + + ngAfterViewInit(): void { + window.addEventListener('mouseup', () => { + this.lastY = undefined; + this.mouseDown = false; + }); + window.addEventListener('mousemove', (event) => this.onDrag(event)); + + this.calcScrollbar(); + } + + calcScrollbar(): void { + const sideBarHeight = this.sidebarRef.nativeElement.clientHeight; + const contentHeight = this.contentRef.nativeElement.clientHeight; + const scrollBarHeight = Math.floor(sideBarHeight / contentHeight * 100); + + this.style['--om-scrollbar-height'] = `${scrollBarHeight}%`; + } + + onScroll(event: Event): void { + const target = event.target as HTMLElement; + const contentHeight = this.contentRef.nativeElement.clientHeight; + const scrollTop = target.scrollTop; + + this.scrollPercent = Math.floor(scrollTop / contentHeight * 100); + + this.style['--om-scrollbar-top'] = `${this.scrollPercent}%`; + } + + onDrag(event: MouseEvent): void { + if (!this.mouseDown) { + return; + } + + event.preventDefault(); + + const offsetY = event.clientY; + + const scrollPx = offsetY - (this.lastY ?? offsetY); + this.lastY = offsetY; + + const contentHeight = this.sidebarRef.nativeElement.clientHeight; + const scrollPercent = (scrollPx) / (contentHeight); + + const scrollContentPx = this.contentRef.nativeElement.clientHeight * scrollPercent; + + this.wrapperRef.nativeElement.scrollBy(0, scrollContentPx); + } +} diff --git a/src/public-api.ts b/src/public-api.ts new file mode 100644 index 0000000..a4d2972 --- /dev/null +++ b/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of ngx-scrollbar + */ + +export * from './lib/ngx-scrollbar.component'; diff --git a/tsconfig.lib.json b/tsconfig.lib.json new file mode 100644 index 0000000..2359bf6 --- /dev/null +++ b/tsconfig.lib.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": [ + "**/*.spec.ts" + ] +} diff --git a/tsconfig.lib.prod.json b/tsconfig.lib.prod.json new file mode 100644 index 0000000..9215caa --- /dev/null +++ b/tsconfig.lib.prod.json @@ -0,0 +1,11 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000..254686d --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +}