ngx-zoneless-scrollbar 21.0.10 → 21.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -155,11 +155,11 @@ export class MyComponent {
155
155
 
156
156
  ### CSS Variables
157
157
 
158
- Customize the scrollbar appearance using CSS:
158
+ Customize the scrollbar appearance using CSS custom properties:
159
159
 
160
160
  ```css
161
161
  ngx-zoneless-scrollbar {
162
- /* Scrollbar size */
162
+ /* Scrollbar dimensions */
163
163
  --scrollbar-size: 8px;
164
164
 
165
165
  /* Track styling */
@@ -170,10 +170,31 @@ ngx-zoneless-scrollbar {
170
170
  --scrollbar-thumb-color: rgba(0, 0, 0, 0.3);
171
171
  --scrollbar-thumb-color-hover: rgba(0, 0, 0, 0.5);
172
172
  --scrollbar-thumb-radius: 4px;
173
+
174
+ /* Advanced styling */
175
+ --scrollbar-thumb-shadow: none;
176
+ --scrollbar-thumb-shadow-hover: none;
177
+ --scrollbar-thumb-border: none;
178
+ --scrollbar-thumb-border-hover: none;
173
179
  }
174
180
  ```
175
181
 
176
- ### Custom Styles Example
182
+ ### Available CSS Custom Properties
183
+
184
+ | Property | Default | Description |
185
+ | -------------------------------- | -------------------- | ------------------------------------------------------------ |
186
+ | `--scrollbar-size` | `7px` | Width of vertical scrollbar / height of horizontal scrollbar |
187
+ | `--scrollbar-track-color` | `transparent` | Background color of the scrollbar track |
188
+ | `--scrollbar-track-radius` | `4px` | Border radius of the scrollbar track |
189
+ | `--scrollbar-thumb-color` | `rgba(0, 0, 0, 0.3)` | Color of the scrollbar thumb (supports gradients) |
190
+ | `--scrollbar-thumb-color-hover` | `rgba(0, 0, 0, 0.5)` | Color of the scrollbar thumb on hover (supports gradients) |
191
+ | `--scrollbar-thumb-radius` | `4px` | Border radius of the scrollbar thumb |
192
+ | `--scrollbar-thumb-shadow` | `none` | Box shadow of the scrollbar thumb (e.g., for glow effects) |
193
+ | `--scrollbar-thumb-shadow-hover` | `none` | Box shadow of the scrollbar thumb on hover |
194
+ | `--scrollbar-thumb-border` | `none` | Border of the scrollbar thumb |
195
+ | `--scrollbar-thumb-border-hover` | `none` | Border of the scrollbar thumb on hover |
196
+
197
+ ### Custom Styles Examples
177
198
 
178
199
  ```css
179
200
  /* Dark theme scrollbar */
@@ -185,6 +206,35 @@ ngx-zoneless-scrollbar {
185
206
  /* Thin scrollbar */
186
207
  .thin-scrollbar ngx-zoneless-scrollbar {
187
208
  --scrollbar-size: 4px;
209
+ --scrollbar-thumb-radius: 2px;
210
+ }
211
+
212
+ /* Neon glow effect */
213
+ .neon-scrollbar ngx-zoneless-scrollbar {
214
+ --scrollbar-size: 14px;
215
+ --scrollbar-thumb-color: #00ff88;
216
+ --scrollbar-thumb-color-hover: #00ffcc;
217
+ --scrollbar-thumb-radius: 7px;
218
+ --scrollbar-thumb-shadow: 0 0 10px #00ff88, 0 0 20px #00ff88;
219
+ --scrollbar-thumb-shadow-hover: 0 0 15px #00ffcc, 0 0 30px #00ffcc, 0 0 45px #00ffcc;
220
+ }
221
+
222
+ /* Gradient scrollbar */
223
+ .gradient-scrollbar ngx-zoneless-scrollbar {
224
+ --scrollbar-size: 16px;
225
+ --scrollbar-thumb-color: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
226
+ --scrollbar-thumb-color-hover: linear-gradient(135deg, #fa89f7 0%, #ff4757 100%);
227
+ --scrollbar-thumb-radius: 999px;
228
+ }
229
+
230
+ /* Square corners with border */
231
+ .squared-scrollbar ngx-zoneless-scrollbar {
232
+ --scrollbar-size: 12px;
233
+ --scrollbar-thumb-color: #ff6b35;
234
+ --scrollbar-thumb-color-hover: #ff4500;
235
+ --scrollbar-thumb-radius: 0px;
236
+ --scrollbar-thumb-border: 2px solid #fff;
237
+ --scrollbar-thumb-border-hover: 2px solid #000;
188
238
  }
189
239
  ```
190
240
 
@@ -78,7 +78,7 @@ class NgxZonelessScrollbar {
78
78
  this.isHorizontallyScrollable.set(isHorizontal);
79
79
  this.afterUpdate.emit({
80
80
  isVerticallyScrollable: isVertical,
81
- isHorizontallyScrollable: isHorizontal
81
+ isHorizontallyScrollable: isHorizontal,
82
82
  });
83
83
  }
84
84
  }
@@ -92,7 +92,7 @@ class NgxZonelessScrollbar {
92
92
  this.viewportRef?.nativeElement?.scrollTo({
93
93
  top: options.top ?? 0,
94
94
  left: options.left ?? 0,
95
- behavior: options.behavior ?? 'smooth'
95
+ behavior: options.behavior ?? 'smooth',
96
96
  });
97
97
  // Resolve after a short delay for smooth scroll
98
98
  setTimeout(resolve, options.behavior === 'instant' ? 0 : 300);
@@ -124,34 +124,24 @@ class NgxZonelessScrollbar {
124
124
  }
125
125
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: NgxZonelessScrollbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
126
126
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.18", type: NgxZonelessScrollbar, isStandalone: true, selector: "ngx-zoneless-scrollbar", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { afterUpdate: "afterUpdate" }, host: { properties: { "attr.orientation": "orientation()" } }, viewQueries: [{ propertyName: "viewportRef", first: true, predicate: ["viewport"], descendants: true }, { propertyName: "contentRef", first: true, predicate: ["content"], descendants: true }], ngImport: i0, template: `
127
- <div
128
- #viewport
129
- class="ngx-zoneless-scrollbar-viewport"
130
- [class.vertical-scrollable]="isVerticallyScrollable()"
131
- [class.horizontal-scrollable]="isHorizontallyScrollable()"
132
- >
127
+ <div #viewport class="ngx-zoneless-scrollbar-viewport" [class.vertical-scrollable]="isVerticallyScrollable()" [class.horizontal-scrollable]="isHorizontallyScrollable()">
133
128
  <div #content class="ngx-zoneless-scrollbar-content">
134
129
  <ng-content></ng-content>
135
130
  </div>
136
131
  </div>
137
- `, isInline: true, styles: [":host{display:block;height:100%;width:100%;position:relative;--scrollbar-size: 7px;--scrollbar-track-color: transparent;--scrollbar-track-radius: 4px;--scrollbar-thumb-color: rgba(0, 0, 0, .3);--scrollbar-thumb-color-hover: rgba(0, 0, 0, .5);--scrollbar-thumb-radius: 4px}.ngx-zoneless-scrollbar-viewport{height:100%;width:100%;overflow:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;&::-webkit-scrollbar{width:var(--scrollbar-size);height:var(--scrollbar-size)}&::-webkit-scrollbar-track{background:var(--scrollbar-track-color);border-radius:var(--scrollbar-track-radius)}&::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb-color);border-radius:var(--scrollbar-thumb-radius);transition:background .2s ease}&::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-color-hover)}scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb-color) var(--scrollbar-track-color)}:host([orientation=\"vertical\"]) .ngx-zoneless-scrollbar-viewport{overflow-x:hidden;overflow-y:auto}:host([orientation=\"horizontal\"]) .ngx-zoneless-scrollbar-viewport{overflow-x:auto;overflow-y:hidden}.ngx-zoneless-scrollbar-content{min-height:100%;min-width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
132
+ `, isInline: true, styles: [":host{display:block;height:100%;width:100%;position:relative;--scrollbar-size: 7px;--scrollbar-track-color: transparent;--scrollbar-track-radius: 4px;--scrollbar-thumb-color: rgba(0, 0, 0, .3);--scrollbar-thumb-color-hover: rgba(0, 0, 0, .5);--scrollbar-thumb-radius: 4px;--scrollbar-thumb-shadow: none;--scrollbar-thumb-shadow-hover: none;--scrollbar-thumb-border: none;--scrollbar-thumb-border-hover: none}.ngx-zoneless-scrollbar-viewport{height:100%;width:100%;overflow:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;&::-webkit-scrollbar{width:var(--scrollbar-size);height:var(--scrollbar-size)}&::-webkit-scrollbar-track{background:var(--scrollbar-track-color);border-radius:var(--scrollbar-track-radius)}&::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb-color);border-radius:var(--scrollbar-thumb-radius);border:var(--scrollbar-thumb-border);box-shadow:var(--scrollbar-thumb-shadow);transition:background .2s ease,box-shadow .2s ease,border .2s ease}&::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-color-hover);border:var(--scrollbar-thumb-border-hover);box-shadow:var(--scrollbar-thumb-shadow-hover)}scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb-color) var(--scrollbar-track-color)}:host([orientation=\"vertical\"]) .ngx-zoneless-scrollbar-viewport{overflow-x:hidden;overflow-y:auto}:host([orientation=\"horizontal\"]) .ngx-zoneless-scrollbar-viewport{overflow-x:auto;overflow-y:hidden}.ngx-zoneless-scrollbar-content{min-height:100%;min-width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
138
133
  }
139
134
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: NgxZonelessScrollbar, decorators: [{
140
135
  type: Component,
141
136
  args: [{ selector: 'ngx-zoneless-scrollbar', standalone: true, template: `
142
- <div
143
- #viewport
144
- class="ngx-zoneless-scrollbar-viewport"
145
- [class.vertical-scrollable]="isVerticallyScrollable()"
146
- [class.horizontal-scrollable]="isHorizontallyScrollable()"
147
- >
137
+ <div #viewport class="ngx-zoneless-scrollbar-viewport" [class.vertical-scrollable]="isVerticallyScrollable()" [class.horizontal-scrollable]="isHorizontallyScrollable()">
148
138
  <div #content class="ngx-zoneless-scrollbar-content">
149
139
  <ng-content></ng-content>
150
140
  </div>
151
141
  </div>
152
142
  `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
153
- '[attr.orientation]': 'orientation()'
154
- }, styles: [":host{display:block;height:100%;width:100%;position:relative;--scrollbar-size: 7px;--scrollbar-track-color: transparent;--scrollbar-track-radius: 4px;--scrollbar-thumb-color: rgba(0, 0, 0, .3);--scrollbar-thumb-color-hover: rgba(0, 0, 0, .5);--scrollbar-thumb-radius: 4px}.ngx-zoneless-scrollbar-viewport{height:100%;width:100%;overflow:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;&::-webkit-scrollbar{width:var(--scrollbar-size);height:var(--scrollbar-size)}&::-webkit-scrollbar-track{background:var(--scrollbar-track-color);border-radius:var(--scrollbar-track-radius)}&::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb-color);border-radius:var(--scrollbar-thumb-radius);transition:background .2s ease}&::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-color-hover)}scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb-color) var(--scrollbar-track-color)}:host([orientation=\"vertical\"]) .ngx-zoneless-scrollbar-viewport{overflow-x:hidden;overflow-y:auto}:host([orientation=\"horizontal\"]) .ngx-zoneless-scrollbar-viewport{overflow-x:auto;overflow-y:hidden}.ngx-zoneless-scrollbar-content{min-height:100%;min-width:100%}\n"] }]
143
+ '[attr.orientation]': 'orientation()',
144
+ }, styles: [":host{display:block;height:100%;width:100%;position:relative;--scrollbar-size: 7px;--scrollbar-track-color: transparent;--scrollbar-track-radius: 4px;--scrollbar-thumb-color: rgba(0, 0, 0, .3);--scrollbar-thumb-color-hover: rgba(0, 0, 0, .5);--scrollbar-thumb-radius: 4px;--scrollbar-thumb-shadow: none;--scrollbar-thumb-shadow-hover: none;--scrollbar-thumb-border: none;--scrollbar-thumb-border-hover: none}.ngx-zoneless-scrollbar-viewport{height:100%;width:100%;overflow:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;&::-webkit-scrollbar{width:var(--scrollbar-size);height:var(--scrollbar-size)}&::-webkit-scrollbar-track{background:var(--scrollbar-track-color);border-radius:var(--scrollbar-track-radius)}&::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb-color);border-radius:var(--scrollbar-thumb-radius);border:var(--scrollbar-thumb-border);box-shadow:var(--scrollbar-thumb-shadow);transition:background .2s ease,box-shadow .2s ease,border .2s ease}&::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-color-hover);border:var(--scrollbar-thumb-border-hover);box-shadow:var(--scrollbar-thumb-shadow-hover)}scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb-color) var(--scrollbar-track-color)}:host([orientation=\"vertical\"]) .ngx-zoneless-scrollbar-viewport{overflow-x:hidden;overflow-y:auto}:host([orientation=\"horizontal\"]) .ngx-zoneless-scrollbar-viewport{overflow-x:auto;overflow-y:hidden}.ngx-zoneless-scrollbar-content{min-height:100%;min-width:100%}\n"] }]
155
145
  }], propDecorators: { viewportRef: [{
156
146
  type: ViewChild,
157
147
  args: ['viewport']
@@ -1 +1 @@
1
- {"version":3,"file":"ngx-zoneless-scrollbar.mjs","sources":["../../../projects/ngx-zoneless-scrollbar/src/lib/ngx-zoneless-scrollbar.component.ts","../../../projects/ngx-zoneless-scrollbar/src/public-api.ts","../../../projects/ngx-zoneless-scrollbar/src/ngx-zoneless-scrollbar.ts"],"sourcesContent":["import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n OnDestroy,\n output,\n signal,\n ViewChild,\n WritableSignal\n} from '@angular/core';\nimport { ScrollbarOrientation, ScrollbarUpdateEvent } from './ngx-zoneless-scrollbar.models';\n\n/**\n * NgxZonelessScrollbar\n *\n * A lightweight, zoneless-compatible scrollbar component for Angular.\n * Uses native browser scrolling with CSS-styled scrollbars.\n *\n * Features:\n * - Uses native browser scrolling (100% reliable in zoneless mode)\n * - CSS-styled scrollbars for modern browsers (Chrome, Firefox, Safari)\n * - Detects if content is scrollable and exposes this via signals\n * - Emits events after content updates\n * - Touch-friendly with momentum scrolling\n *\n * @example\n * ```html\n * <ngx-zoneless-scrollbar (afterUpdate)=\"onScrollbarUpdated($event)\">\n * <div>Your scrollable content here</div>\n * </ngx-zoneless-scrollbar>\n * ```\n */\n@Component({\n selector: 'ngx-zoneless-scrollbar',\n standalone: true,\n template: `\n <div\n #viewport\n class=\"ngx-zoneless-scrollbar-viewport\"\n [class.vertical-scrollable]=\"isVerticallyScrollable()\"\n [class.horizontal-scrollable]=\"isHorizontallyScrollable()\"\n >\n <div #content class=\"ngx-zoneless-scrollbar-content\">\n <ng-content></ng-content>\n </div>\n </div>\n `,\n styles: [\n `\n :host {\n display: block;\n height: 100%;\n width: 100%;\n position: relative;\n\n /* CSS Custom Properties for easy customization */\n --scrollbar-size: 7px;\n --scrollbar-track-color: transparent;\n --scrollbar-track-radius: 4px;\n --scrollbar-thumb-color: rgba(0, 0, 0, 0.3);\n --scrollbar-thumb-color-hover: rgba(0, 0, 0, 0.5);\n --scrollbar-thumb-radius: 4px;\n }\n\n .ngx-zoneless-scrollbar-viewport {\n height: 100%;\n width: 100%;\n overflow: auto;\n -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */\n overscroll-behavior: contain; /* Prevent scroll chaining */\n\n /* Custom scrollbar styling for webkit browsers (Chrome, Safari, Edge) */\n &::-webkit-scrollbar {\n width: var(--scrollbar-size);\n height: var(--scrollbar-size);\n }\n\n &::-webkit-scrollbar-track {\n background: var(--scrollbar-track-color);\n border-radius: var(--scrollbar-track-radius);\n }\n\n &::-webkit-scrollbar-thumb {\n background: var(--scrollbar-thumb-color);\n border-radius: var(--scrollbar-thumb-radius);\n transition: background 0.2s ease;\n }\n\n &::-webkit-scrollbar-thumb:hover {\n background: var(--scrollbar-thumb-color-hover);\n }\n\n /* Firefox scrollbar styling */\n scrollbar-width: thin;\n scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color);\n }\n\n /* Orientation-specific overflow behavior */\n :host([orientation='vertical']) .ngx-zoneless-scrollbar-viewport {\n overflow-x: hidden;\n overflow-y: auto;\n }\n\n :host([orientation='horizontal']) .ngx-zoneless-scrollbar-viewport {\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .ngx-zoneless-scrollbar-content {\n min-height: 100%;\n min-width: 100%;\n }\n `\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[attr.orientation]': 'orientation()'\n }\n})\nexport class NgxZonelessScrollbar implements AfterViewInit, OnDestroy {\n @ViewChild('viewport') viewportRef!: ElementRef<HTMLElement>;\n @ViewChild('content') contentRef!: ElementRef<HTMLElement>;\n\n /**\n * Orientation of scrolling\n * - 'vertical': Only vertical scrolling (default)\n * - 'horizontal': Only horizontal scrolling\n * - 'auto': Both directions as needed\n */\n readonly orientation = input<ScrollbarOrientation>('vertical');\n\n /**\n * Emits after the scrollbar state is updated with scrollability info\n */\n readonly afterUpdate = output<ScrollbarUpdateEvent>();\n\n /**\n * Signal indicating if content is vertically scrollable\n */\n readonly isVerticallyScrollable: WritableSignal<boolean> = signal(false);\n\n /**\n * Signal indicating if content is horizontally scrollable\n */\n readonly isHorizontallyScrollable: WritableSignal<boolean> = signal(false);\n\n private resizeObserver: ResizeObserver | null = null;\n\n ngAfterViewInit(): void {\n // Initial check after view is ready\n this.checkScrollability();\n\n // Set up ResizeObserver to detect content changes\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => {\n this.checkScrollability();\n });\n\n // Observe both viewport and content for size changes\n this.resizeObserver.observe(this.viewportRef.nativeElement);\n this.resizeObserver.observe(this.contentRef.nativeElement);\n }\n }\n\n ngOnDestroy(): void {\n this.resizeObserver?.disconnect();\n }\n\n /**\n * Check if content is scrollable and emit update event\n */\n private checkScrollability(): void {\n const viewport = this.viewportRef?.nativeElement;\n const content = this.contentRef?.nativeElement;\n\n if (!viewport || !content) return;\n\n const isVertical = content.scrollHeight > viewport.clientHeight;\n const isHorizontal = content.scrollWidth > viewport.clientWidth;\n\n // Only update and emit if values changed\n const prevVertical = this.isVerticallyScrollable();\n const prevHorizontal = this.isHorizontallyScrollable();\n\n if (isVertical !== prevVertical || isHorizontal !== prevHorizontal) {\n this.isVerticallyScrollable.set(isVertical);\n this.isHorizontallyScrollable.set(isHorizontal);\n\n this.afterUpdate.emit({\n isVerticallyScrollable: isVertical,\n isHorizontallyScrollable: isHorizontal\n });\n }\n }\n\n /**\n * Scroll to a specific position\n * @param options - ScrollToOptions with top, left, and behavior\n * @returns Promise that resolves when scroll is complete\n */\n scrollTo(options: ScrollToOptions): Promise<void> {\n return new Promise((resolve) => {\n this.viewportRef?.nativeElement?.scrollTo({\n top: options.top ?? 0,\n left: options.left ?? 0,\n behavior: options.behavior ?? 'smooth'\n });\n // Resolve after a short delay for smooth scroll\n setTimeout(resolve, options.behavior === 'instant' ? 0 : 300);\n });\n }\n\n /**\n * Manually trigger an update check\n */\n update(): void {\n this.checkScrollability();\n }\n\n /**\n * Get the viewport element for direct access if needed\n */\n get viewportElement(): HTMLElement | null {\n return this.viewportRef?.nativeElement ?? null;\n }\n\n /**\n * Get the current scroll position\n */\n get scrollTop(): number {\n return this.viewportRef?.nativeElement?.scrollTop ?? 0;\n }\n\n /**\n * Get the current horizontal scroll position\n */\n get scrollLeft(): number {\n return this.viewportRef?.nativeElement?.scrollLeft ?? 0;\n }\n}\n","/*\n * Public API Surface of ngx-zoneless-scrollbar\n */\n\nexport * from './lib/ngx-zoneless-scrollbar.component';\nexport * from './lib/ngx-zoneless-scrollbar.models';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;AAcA;;;;;;;;;;;;;;;;;;;AAmBG;MAwFU,oBAAoB,CAAA;AACR,IAAA,WAAW;AACZ,IAAA,UAAU;AAEhC;;;;;AAKG;AACM,IAAA,WAAW,GAAG,KAAK,CAAuB,UAAU,CAAC;AAE9D;;AAEG;IACM,WAAW,GAAG,MAAM,EAAwB;AAErD;;AAEG;AACM,IAAA,sBAAsB,GAA4B,MAAM,CAAC,KAAK,CAAC;AAExE;;AAEG;AACM,IAAA,wBAAwB,GAA4B,MAAM,CAAC,KAAK,CAAC;IAElE,cAAc,GAA0B,IAAI;IAEpD,eAAe,GAAA;;QAEb,IAAI,CAAC,kBAAkB,EAAE;;AAGzB,QAAA,IAAI,OAAO,cAAc,KAAK,WAAW,EAAE;AACzC,YAAA,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,CAAC,MAAK;gBAC5C,IAAI,CAAC,kBAAkB,EAAE;AAC3B,YAAA,CAAC,CAAC;;YAGF,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;YAC3D,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAC5D;IACF;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,cAAc,EAAE,UAAU,EAAE;IACnC;AAEA;;AAEG;IACK,kBAAkB,GAAA;AACxB,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,aAAa;AAChD,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa;AAE9C,QAAA,IAAI,CAAC,QAAQ,IAAI,CAAC,OAAO;YAAE;QAE3B,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,YAAY;QAC/D,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW;;AAG/D,QAAA,MAAM,YAAY,GAAG,IAAI,CAAC,sBAAsB,EAAE;AAClD,QAAA,MAAM,cAAc,GAAG,IAAI,CAAC,wBAAwB,EAAE;QAEtD,IAAI,UAAU,KAAK,YAAY,IAAI,YAAY,KAAK,cAAc,EAAE;AAClE,YAAA,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,UAAU,CAAC;AAC3C,YAAA,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,YAAY,CAAC;AAE/C,YAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AACpB,gBAAA,sBAAsB,EAAE,UAAU;AAClC,gBAAA,wBAAwB,EAAE;AAC3B,aAAA,CAAC;QACJ;IACF;AAEA;;;;AAIG;AACH,IAAA,QAAQ,CAAC,OAAwB,EAAA;AAC/B,QAAA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAI;AAC7B,YAAA,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,QAAQ,CAAC;AACxC,gBAAA,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;AACrB,gBAAA,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC;AACvB,gBAAA,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI;AAC/B,aAAA,CAAC;;AAEF,YAAA,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,KAAK,SAAS,GAAG,CAAC,GAAG,GAAG,CAAC;AAC/D,QAAA,CAAC,CAAC;IACJ;AAEA;;AAEG;IACH,MAAM,GAAA;QACJ,IAAI,CAAC,kBAAkB,EAAE;IAC3B;AAEA;;AAEG;AACH,IAAA,IAAI,eAAe,GAAA;AACjB,QAAA,OAAO,IAAI,CAAC,WAAW,EAAE,aAAa,IAAI,IAAI;IAChD;AAEA;;AAEG;AACH,IAAA,IAAI,SAAS,GAAA;QACX,OAAO,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,SAAS,IAAI,CAAC;IACxD;AAEA;;AAEG;AACH,IAAA,IAAI,UAAU,GAAA;QACZ,OAAO,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC;IACzD;wGAvHW,oBAAoB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAApB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,oBAAoB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,WAAA,EAAA,aAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,kBAAA,EAAA,eAAA,EAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,aAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,UAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,YAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,SAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EApFrB;;;;;;;;;;;AAWT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,2pCAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAyEU,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBAvFhC,SAAS;+BACE,wBAAwB,EAAA,UAAA,EACtB,IAAI,EAAA,QAAA,EACN;;;;;;;;;;;GAWT,EAAA,eAAA,EAoEgB,uBAAuB,CAAC,MAAM,EAAA,IAAA,EACzC;AACJ,wBAAA,oBAAoB,EAAE;AACvB,qBAAA,EAAA,MAAA,EAAA,CAAA,2pCAAA,CAAA,EAAA;8BAGsB,WAAW,EAAA,CAAA;sBAAjC,SAAS;uBAAC,UAAU;gBACC,UAAU,EAAA,CAAA;sBAA/B,SAAS;uBAAC,SAAS;;;AC3HtB;;AAEG;;ACFH;;AAEG;;;;"}
1
+ {"version":3,"file":"ngx-zoneless-scrollbar.mjs","sources":["../../../projects/ngx-zoneless-scrollbar/src/lib/ngx-zoneless-scrollbar.component.ts","../../../projects/ngx-zoneless-scrollbar/src/public-api.ts","../../../projects/ngx-zoneless-scrollbar/src/ngx-zoneless-scrollbar.ts"],"sourcesContent":["import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, input, OnDestroy, output, signal, ViewChild, WritableSignal } from '@angular/core';\nimport { ScrollbarOrientation, ScrollbarUpdateEvent } from './ngx-zoneless-scrollbar.models';\n\n/**\n * NgxZonelessScrollbar\n *\n * A lightweight, zoneless-compatible scrollbar component for Angular.\n * Uses native browser scrolling with CSS-styled scrollbars.\n *\n * Features:\n * - Uses native browser scrolling (100% reliable in zoneless mode)\n * - CSS-styled scrollbars for modern browsers (Chrome, Firefox, Safari)\n * - Detects if content is scrollable and exposes this via signals\n * - Emits events after content updates\n * - Touch-friendly with momentum scrolling\n *\n * @example\n * ```html\n * <ngx-zoneless-scrollbar (afterUpdate)=\"onScrollbarUpdated($event)\">\n * <div>Your scrollable content here</div>\n * </ngx-zoneless-scrollbar>\n * ```\n */\n@Component({\n selector: 'ngx-zoneless-scrollbar',\n standalone: true,\n template: `\n <div #viewport class=\"ngx-zoneless-scrollbar-viewport\" [class.vertical-scrollable]=\"isVerticallyScrollable()\" [class.horizontal-scrollable]=\"isHorizontallyScrollable()\">\n <div #content class=\"ngx-zoneless-scrollbar-content\">\n <ng-content></ng-content>\n </div>\n </div>\n `,\n styles: [\n `\n :host {\n display: block;\n height: 100%;\n width: 100%;\n position: relative;\n\n /* CSS Custom Properties for easy customization */\n --scrollbar-size: 7px;\n --scrollbar-track-color: transparent;\n --scrollbar-track-radius: 4px;\n --scrollbar-thumb-color: rgba(0, 0, 0, 0.3);\n --scrollbar-thumb-color-hover: rgba(0, 0, 0, 0.5);\n --scrollbar-thumb-radius: 4px;\n --scrollbar-thumb-shadow: none;\n --scrollbar-thumb-shadow-hover: none;\n --scrollbar-thumb-border: none;\n --scrollbar-thumb-border-hover: none;\n }\n\n .ngx-zoneless-scrollbar-viewport {\n height: 100%;\n width: 100%;\n overflow: auto;\n -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */\n overscroll-behavior: contain; /* Prevent scroll chaining */\n\n /* Custom scrollbar styling for webkit browsers (Chrome, Safari, Edge) */\n &::-webkit-scrollbar {\n width: var(--scrollbar-size);\n height: var(--scrollbar-size);\n }\n\n &::-webkit-scrollbar-track {\n background: var(--scrollbar-track-color);\n border-radius: var(--scrollbar-track-radius);\n }\n\n &::-webkit-scrollbar-thumb {\n background: var(--scrollbar-thumb-color);\n border-radius: var(--scrollbar-thumb-radius);\n border: var(--scrollbar-thumb-border);\n box-shadow: var(--scrollbar-thumb-shadow);\n transition: background 0.2s ease, box-shadow 0.2s ease, border 0.2s ease;\n }\n\n &::-webkit-scrollbar-thumb:hover {\n background: var(--scrollbar-thumb-color-hover);\n border: var(--scrollbar-thumb-border-hover);\n box-shadow: var(--scrollbar-thumb-shadow-hover);\n }\n\n /* Firefox scrollbar styling */\n scrollbar-width: thin;\n scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color);\n }\n\n /* Orientation-specific overflow behavior */\n :host([orientation='vertical']) .ngx-zoneless-scrollbar-viewport {\n overflow-x: hidden;\n overflow-y: auto;\n }\n\n :host([orientation='horizontal']) .ngx-zoneless-scrollbar-viewport {\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .ngx-zoneless-scrollbar-content {\n min-height: 100%;\n min-width: 100%;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[attr.orientation]': 'orientation()',\n },\n})\nexport class NgxZonelessScrollbar implements AfterViewInit, OnDestroy {\n @ViewChild('viewport') viewportRef!: ElementRef<HTMLElement>;\n @ViewChild('content') contentRef!: ElementRef<HTMLElement>;\n\n /**\n * Orientation of scrolling\n * - 'vertical': Only vertical scrolling (default)\n * - 'horizontal': Only horizontal scrolling\n * - 'auto': Both directions as needed\n */\n readonly orientation = input<ScrollbarOrientation>('vertical');\n\n /**\n * Emits after the scrollbar state is updated with scrollability info\n */\n readonly afterUpdate = output<ScrollbarUpdateEvent>();\n\n /**\n * Signal indicating if content is vertically scrollable\n */\n readonly isVerticallyScrollable: WritableSignal<boolean> = signal(false);\n\n /**\n * Signal indicating if content is horizontally scrollable\n */\n readonly isHorizontallyScrollable: WritableSignal<boolean> = signal(false);\n\n private resizeObserver: ResizeObserver | null = null;\n\n ngAfterViewInit(): void {\n // Initial check after view is ready\n this.checkScrollability();\n\n // Set up ResizeObserver to detect content changes\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => {\n this.checkScrollability();\n });\n\n // Observe both viewport and content for size changes\n this.resizeObserver.observe(this.viewportRef.nativeElement);\n this.resizeObserver.observe(this.contentRef.nativeElement);\n }\n }\n\n ngOnDestroy(): void {\n this.resizeObserver?.disconnect();\n }\n\n /**\n * Check if content is scrollable and emit update event\n */\n private checkScrollability(): void {\n const viewport = this.viewportRef?.nativeElement;\n const content = this.contentRef?.nativeElement;\n\n if (!viewport || !content) return;\n\n const isVertical = content.scrollHeight > viewport.clientHeight;\n const isHorizontal = content.scrollWidth > viewport.clientWidth;\n\n // Only update and emit if values changed\n const prevVertical = this.isVerticallyScrollable();\n const prevHorizontal = this.isHorizontallyScrollable();\n\n if (isVertical !== prevVertical || isHorizontal !== prevHorizontal) {\n this.isVerticallyScrollable.set(isVertical);\n this.isHorizontallyScrollable.set(isHorizontal);\n\n this.afterUpdate.emit({\n isVerticallyScrollable: isVertical,\n isHorizontallyScrollable: isHorizontal,\n });\n }\n }\n\n /**\n * Scroll to a specific position\n * @param options - ScrollToOptions with top, left, and behavior\n * @returns Promise that resolves when scroll is complete\n */\n scrollTo(options: ScrollToOptions): Promise<void> {\n return new Promise((resolve) => {\n this.viewportRef?.nativeElement?.scrollTo({\n top: options.top ?? 0,\n left: options.left ?? 0,\n behavior: options.behavior ?? 'smooth',\n });\n // Resolve after a short delay for smooth scroll\n setTimeout(resolve, options.behavior === 'instant' ? 0 : 300);\n });\n }\n\n /**\n * Manually trigger an update check\n */\n update(): void {\n this.checkScrollability();\n }\n\n /**\n * Get the viewport element for direct access if needed\n */\n get viewportElement(): HTMLElement | null {\n return this.viewportRef?.nativeElement ?? null;\n }\n\n /**\n * Get the current scroll position\n */\n get scrollTop(): number {\n return this.viewportRef?.nativeElement?.scrollTop ?? 0;\n }\n\n /**\n * Get the current horizontal scroll position\n */\n get scrollLeft(): number {\n return this.viewportRef?.nativeElement?.scrollLeft ?? 0;\n }\n}\n","/*\n * Public API Surface of ngx-zoneless-scrollbar\n */\n\nexport * from './lib/ngx-zoneless-scrollbar.component';\nexport * from './lib/ngx-zoneless-scrollbar.models';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ngx-zoneless-scrollbar",
3
- "version": "21.0.10",
3
+ "version": "21.0.12",
4
4
  "description": "A lightweight, zoneless-compatible scrollbar component for Angular. Uses native browser scrolling with CSS-styled scrollbars, designed for Angular's zoneless change detection mode.",
5
5
  "keywords": [
6
6
  "angular",