@scion/components 18.1.0 → 19.0.0

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.
Files changed (59) hide show
  1. package/dimension/dimension.module.d.ts +2 -0
  2. package/dimension/public_api.d.ts +1 -1
  3. package/fesm2022/scion-components-dimension.mjs +36 -23
  4. package/fesm2022/scion-components-dimension.mjs.map +1 -1
  5. package/fesm2022/scion-components-sashbox.mjs +73 -43
  6. package/fesm2022/scion-components-sashbox.mjs.map +1 -1
  7. package/fesm2022/scion-components-splitter.mjs +40 -33
  8. package/fesm2022/scion-components-splitter.mjs.map +1 -1
  9. package/fesm2022/scion-components-throbber.mjs +39 -39
  10. package/fesm2022/scion-components-throbber.mjs.map +1 -1
  11. package/fesm2022/scion-components-viewport.mjs +225 -277
  12. package/fesm2022/scion-components-viewport.mjs.map +1 -1
  13. package/package.json +5 -17
  14. package/sashbox/sashbox.module.d.ts +2 -0
  15. package/splitter/public_api.d.ts +1 -1
  16. package/splitter/splitter.module.d.ts +2 -0
  17. package/throbber/throbber.module.d.ts +2 -0
  18. package/viewport/native-scrollbar-track-size-provider.service.d.ts +6 -16
  19. package/viewport/public_api.d.ts +1 -1
  20. package/viewport/scrollable.directive.d.ts +9 -10
  21. package/viewport/scrollbar/scrollbar.component.d.ts +17 -32
  22. package/viewport/viewport.component.d.ts +14 -16
  23. package/viewport/viewport.module.d.ts +2 -0
  24. package/esm2022/dimension/bounding-client-rect.signal.mjs +0 -53
  25. package/esm2022/dimension/dimension.directive.mjs +0 -62
  26. package/esm2022/dimension/dimension.mjs +0 -11
  27. package/esm2022/dimension/dimension.module.mjs +0 -34
  28. package/esm2022/dimension/dimension.signal.mjs +0 -58
  29. package/esm2022/dimension/public_api.mjs +0 -19
  30. package/esm2022/dimension/scion-components-dimension.mjs +0 -5
  31. package/esm2022/public-api.mjs +0 -11
  32. package/esm2022/sashbox/element-ref.directive.mjs +0 -31
  33. package/esm2022/sashbox/public_api.mjs +0 -18
  34. package/esm2022/sashbox/sash-initializer.directive.mjs +0 -35
  35. package/esm2022/sashbox/sash.directive.mjs +0 -145
  36. package/esm2022/sashbox/sashbox-accessor.mjs +0 -6
  37. package/esm2022/sashbox/sashbox.component.mjs +0 -282
  38. package/esm2022/sashbox/sashbox.module.mjs +0 -55
  39. package/esm2022/sashbox/scion-components-sashbox.mjs +0 -5
  40. package/esm2022/scion-components.mjs +0 -5
  41. package/esm2022/splitter/public_api.mjs +0 -17
  42. package/esm2022/splitter/scion-components-splitter.mjs +0 -5
  43. package/esm2022/splitter/splitter.component.mjs +0 -226
  44. package/esm2022/splitter/splitter.module.mjs +0 -38
  45. package/esm2022/throbber/ellipsis-throbber/ellipsis-throbber.component.mjs +0 -25
  46. package/esm2022/throbber/public_api.mjs +0 -17
  47. package/esm2022/throbber/ripple-throbber/ripple-throbber.component.mjs +0 -25
  48. package/esm2022/throbber/roller-throbber/roller-throbber.component.mjs +0 -25
  49. package/esm2022/throbber/scion-components-throbber.mjs +0 -5
  50. package/esm2022/throbber/spinner-throbber/spinner-throbber.component.mjs +0 -26
  51. package/esm2022/throbber/throbber.component.mjs +0 -72
  52. package/esm2022/throbber/throbber.module.mjs +0 -32
  53. package/esm2022/viewport/native-scrollbar-track-size-provider.service.mjs +0 -115
  54. package/esm2022/viewport/public_api.mjs +0 -19
  55. package/esm2022/viewport/scion-components-viewport.mjs +0 -5
  56. package/esm2022/viewport/scrollable.directive.mjs +0 -94
  57. package/esm2022/viewport/scrollbar/scrollbar.component.mjs +0 -323
  58. package/esm2022/viewport/viewport.component.mjs +0 -288
  59. package/esm2022/viewport/viewport.module.mjs +0 -42
@@ -1,16 +1,17 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Inject, Directive, Input, NgZone, Component, ChangeDetectionStrategy, ViewChild, HostBinding, EventEmitter, ViewEncapsulation, Output, HostListener, NgModule } from '@angular/core';
2
+ import { inject, NgZone, Injectable, ElementRef, Renderer2, input, effect, untracked, Directive, DestroyRef, viewChild, computed, Component, ChangeDetectionStrategy, HostBinding, output, ViewEncapsulation, HostListener, NgModule } from '@angular/core';
3
+ import { DOCUMENT } from '@angular/common';
4
+ import { fromEvent, merge, mergeWith, timer, of } from 'rxjs';
5
+ import { debounceTime, startWith, map, distinctUntilChanged, first, withLatestFrom, takeWhile, takeUntil, switchMap } from 'rxjs/operators';
6
+ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
+ import { subscribeIn, filterArray } from '@scion/toolkit/operators';
3
8
  import { coerceElement } from '@angular/cdk/coercion';
4
- import { debounceTime, startWith, map, distinctUntilChanged, takeUntil, switchMap, first, withLatestFrom, takeWhile } from 'rxjs/operators';
5
- import { BehaviorSubject, Subject, fromEvent, merge, of, timer } from 'rxjs';
6
- import { DOCUMENT, AsyncPipe } from '@angular/common';
7
9
  import { fromResize$, fromMutation$ } from '@scion/toolkit/observable';
8
- import { subscribeInside, filterArray } from '@scion/toolkit/operators';
9
- import * as i2 from '@angular/cdk/scrolling';
10
+ import * as i1 from '@angular/cdk/scrolling';
10
11
  import { ScrollingModule } from '@angular/cdk/scrolling';
11
12
 
12
13
  /*
13
- * Copyright (c) 2018-2019 Swiss Federal Railways
14
+ * Copyright (c) 2018-2024 Swiss Federal Railways
14
15
  *
15
16
  * This program and the accompanying materials are made
16
17
  * available under the terms of the Eclipse Public License 2.0
@@ -22,26 +23,14 @@ import { ScrollingModule } from '@angular/cdk/scrolling';
22
23
  * Provides the native scrollbar tracksize.
23
24
  */
24
25
  class SciNativeScrollbarTrackSizeProvider {
25
- constructor(_document, _zone) {
26
- this._document = _document;
27
- this._zone = _zone;
28
- this._trackSize$ = new BehaviorSubject(null);
29
- this._destroy$ = new Subject();
30
- this.installNativeScrollbarTrackSizeListener();
31
- }
32
- /**
33
- * Returns an {Observable} which emits the native scrollbar track size, if any, or else `null`.
34
- *
35
- * Upon subscription, it emits the track size immediately, and then continuously emits when the track size changes.
36
- */
37
- get trackSize$() {
38
- return this._trackSize$;
39
- }
26
+ _document = inject(DOCUMENT);
27
+ _zone = inject(NgZone);
40
28
  /**
41
- * Returns the native scrollbar track size, if any, or else `null`.
29
+ * Provides the track size of the native scrollbar, or `null` if the native scrollbars sit on top of the content.
42
30
  */
43
- get trackSize() {
44
- return this._trackSize$.getValue();
31
+ trackSize;
32
+ constructor() {
33
+ this.trackSize = this.createNativeScrollbarTrackSizeSignal();
45
34
  }
46
35
  /**
47
36
  * Computes the native scrollbar track size.
@@ -49,7 +38,7 @@ class SciNativeScrollbarTrackSizeProvider {
49
38
  * @returns native track size, or `null` if the native scrollbars sit on top of the content.
50
39
  */
51
40
  computeTrackSize() {
52
- // create temporary viewport and viewport client with native scrollbars to compute scrolltrack width
41
+ // Create temporary viewport and viewport client with native scrollbars to compute the scrolltrack width.
53
42
  const viewportDiv = this._document.createElement('div');
54
43
  setStyle(viewportDiv, {
55
44
  position: 'absolute',
@@ -74,39 +63,29 @@ class SciNativeScrollbarTrackSizeProvider {
74
63
  hScrollbarTrackHeight: viewportBounds.height - viewportClientBounds.height,
75
64
  vScrollbarTrackWidth: viewportBounds.width - viewportClientBounds.width,
76
65
  };
77
- // destroy temporary viewport
66
+ // Destroy temporary viewport.
78
67
  this._document.body.removeChild(viewportDiv);
79
68
  if (trackSize.hScrollbarTrackHeight === 0 && trackSize.vScrollbarTrackWidth === 0) {
80
69
  return null;
81
70
  }
82
71
  return trackSize;
83
72
  }
84
- installNativeScrollbarTrackSizeListener() {
73
+ createNativeScrollbarTrackSizeSignal() {
85
74
  // We compute the size of the native scrollbar track when the browser fires the onresize window event.
86
75
  // This event is also fired on page zoom or when displaying a hidden document. Hidden documents do not have
87
76
  // a scrollbar track size until being displayed, e.g., after showing hidden iframes.
88
- this._zone.runOutsideAngular(() => {
89
- fromEvent(window, 'resize')
90
- .pipe(debounceTime(5), startWith(null), // trigger the initial computation
91
- map(() => this.computeTrackSize()), distinctUntilChanged((t1, t2) => JSON.stringify(t1) === JSON.stringify(t2)), takeUntil(this._destroy$))
92
- .subscribe(trackSize => {
93
- this._zone.run(() => this._trackSize$.next(trackSize));
94
- });
95
- });
96
- }
97
- ngOnDestroy() {
98
- this._destroy$.next();
77
+ const trackSize$ = fromEvent(window, 'resize')
78
+ .pipe(subscribeIn(fn => this._zone.runOutsideAngular(fn)), debounceTime(5), startWith(null), // trigger the initial computation
79
+ map(() => this.computeTrackSize()), distinctUntilChanged((t1, t2) => t1?.hScrollbarTrackHeight === t2?.hScrollbarTrackHeight && t1?.vScrollbarTrackWidth === t2?.vScrollbarTrackWidth));
80
+ return toSignal(trackSize$, { initialValue: null });
99
81
  }
100
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciNativeScrollbarTrackSizeProvider, deps: [{ token: DOCUMENT }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable }); }
101
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciNativeScrollbarTrackSizeProvider, providedIn: 'root' }); }
82
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciNativeScrollbarTrackSizeProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
83
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciNativeScrollbarTrackSizeProvider, providedIn: 'root' });
102
84
  }
103
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciNativeScrollbarTrackSizeProvider, decorators: [{
85
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciNativeScrollbarTrackSizeProvider, decorators: [{
104
86
  type: Injectable,
105
87
  args: [{ providedIn: 'root' }]
106
- }], ctorParameters: () => [{ type: Document, decorators: [{
107
- type: Inject,
108
- args: [DOCUMENT]
109
- }] }, { type: i0.NgZone }] });
88
+ }], ctorParameters: () => [] });
110
89
  /**
111
90
  * Applies the given style(s) to the given element.
112
91
  *
@@ -140,24 +119,33 @@ function setStyle(element, style) {
140
119
  * to hide the pushed out native scrollbars.
141
120
  */
142
121
  class SciScrollableDirective {
143
- constructor(_host, _renderer, nativeScrollbarTrackSizeProvider) {
144
- this._host = _host;
145
- this._renderer = _renderer;
146
- this._destroy$ = new Subject();
147
- this._inputChange$ = new Subject();
148
- /**
149
- * Controls whether to display native scrollbars.
150
- * Has no effect if the native scrollbar sits on top of the content, e.g. in OS X.
151
- */
152
- this.isDisplayNativeScrollbar = false;
153
- merge(nativeScrollbarTrackSizeProvider.trackSize$, this._inputChange$.pipe(map(() => nativeScrollbarTrackSizeProvider.trackSize)))
154
- .pipe(takeUntil(this._destroy$))
155
- .subscribe(nativeScrollbarTrackSize => {
156
- if (nativeScrollbarTrackSize === null) { // the native scrollbar sits on top of the content
157
- this.useNativeScrollbars();
122
+ _host = inject((ElementRef)).nativeElement;
123
+ _renderer = inject(Renderer2);
124
+ _nativeScrollbarTrackSizeProvider = inject(SciNativeScrollbarTrackSizeProvider);
125
+ /**
126
+ * Controls whether to display native scrollbars.
127
+ * Has no effect if the native scrollbar sits on top of the content, e.g. in OS X.
128
+ */
129
+ displayNativeScrollbar = input(false, { alias: 'sciScrollableDisplayNativeScrollbar' });
130
+ constructor() {
131
+ this.controlDisplayOfNativeScrollbar();
132
+ }
133
+ /**
134
+ * Controls the display of the native scrollbar based on this directive's configuration.
135
+ */
136
+ controlDisplayOfNativeScrollbar() {
137
+ effect(() => {
138
+ const displayNativeScrollbar = this.displayNativeScrollbar();
139
+ if (displayNativeScrollbar) {
140
+ untracked(() => this.useNativeScrollbars());
141
+ return;
142
+ }
143
+ const nativeScrollbarTrackSize = this._nativeScrollbarTrackSizeProvider.trackSize();
144
+ if (nativeScrollbarTrackSize) {
145
+ untracked(() => this.shiftNativeScrollbars(nativeScrollbarTrackSize));
158
146
  }
159
147
  else {
160
- this.isDisplayNativeScrollbar ? this.useNativeScrollbars() : this.shiftNativeScrollbars(nativeScrollbarTrackSize);
148
+ untracked(() => this.useNativeScrollbars());
161
149
  }
162
150
  });
163
151
  }
@@ -165,7 +153,7 @@ class SciScrollableDirective {
165
153
  * Uses the native scrollbars when content overflows.
166
154
  */
167
155
  useNativeScrollbars() {
168
- this.setStyle(this._host.nativeElement, {
156
+ this.setStyle(this._host, {
169
157
  overflow: 'auto',
170
158
  width: '100%',
171
159
  height: '100%',
@@ -177,7 +165,7 @@ class SciScrollableDirective {
177
165
  * Shifts the native scrollbars out of the visible viewport area.
178
166
  */
179
167
  shiftNativeScrollbars(nativeScrollbarTrackSize) {
180
- this.setStyle(this._host.nativeElement, {
168
+ this.setStyle(this._host, {
181
169
  overflow: 'scroll',
182
170
  width: `calc(100% + ${nativeScrollbarTrackSize.vScrollbarTrackWidth}px`,
183
171
  height: `calc(100% + ${nativeScrollbarTrackSize.hScrollbarTrackHeight}px`,
@@ -188,25 +176,15 @@ class SciScrollableDirective {
188
176
  setStyle(element, style) {
189
177
  Object.keys(style).forEach(key => this._renderer.setStyle(element, key, style[key]));
190
178
  }
191
- ngOnChanges(changes) {
192
- this._inputChange$.next();
193
- }
194
- ngOnDestroy() {
195
- this._destroy$.next();
196
- }
197
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciScrollableDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: SciNativeScrollbarTrackSizeProvider }], target: i0.ɵɵFactoryTarget.Directive }); }
198
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: SciScrollableDirective, isStandalone: true, selector: "[sciScrollable]", inputs: { isDisplayNativeScrollbar: ["sciScrollableDisplayNativeScrollbar", "isDisplayNativeScrollbar"] }, usesOnChanges: true, ngImport: i0 }); }
179
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciScrollableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
180
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.0", type: SciScrollableDirective, isStandalone: true, selector: "[sciScrollable]", inputs: { displayNativeScrollbar: { classPropertyName: "displayNativeScrollbar", publicName: "sciScrollableDisplayNativeScrollbar", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
199
181
  }
200
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciScrollableDirective, decorators: [{
182
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciScrollableDirective, decorators: [{
201
183
  type: Directive,
202
184
  args: [{
203
185
  selector: '[sciScrollable]',
204
- standalone: true,
205
186
  }]
206
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: SciNativeScrollbarTrackSizeProvider }], propDecorators: { isDisplayNativeScrollbar: [{
207
- type: Input,
208
- args: ['sciScrollableDisplayNativeScrollbar']
209
- }] } });
187
+ }], ctorParameters: () => [] });
210
188
 
211
189
  /*
212
190
  * Copyright (c) 2018-2019 Swiss Federal Railways
@@ -251,54 +229,38 @@ class SciScrollbarComponent {
251
229
  *
252
230
  * @internal
253
231
  */
254
- static { this.VIEWPORT_RESIZE_DEBOUNCE_TIME = 50; }
232
+ static VIEWPORT_RESIZE_DEBOUNCE_TIME = 50;
233
+ _host = inject((ElementRef)).nativeElement;
234
+ _document = inject(DOCUMENT);
235
+ _zone = inject(NgZone);
236
+ _destroyRef = inject(DestroyRef);
237
+ _lastDragPosition = null;
238
+ _overflow = false;
239
+ _thumbSizeFr = 0;
240
+ _thumbPositionFr = 0;
241
+ _thumbElement = viewChild.required('thumb_handle');
242
+ _vertical = computed(() => this.direction() === 'vscroll');
255
243
  get vertical() {
256
- return this.direction === 'vscroll';
244
+ return this._vertical();
257
245
  }
258
246
  get horizontal() {
259
- return this.direction === 'hscroll';
247
+ return !this._vertical();
260
248
  }
261
249
  get scrolling() {
262
250
  return this._lastDragPosition !== null;
263
251
  }
252
+ /**
253
+ * Specifies the direction of the scrollbar.
254
+ *
255
+ * By default, if not specified, renders a vertical scrollbar.
256
+ */
257
+ direction = input('vscroll');
264
258
  /**
265
259
  * The viewport to provide scrollbars for.
266
260
  */
267
- set setViewport(viewport) {
268
- this._viewport = viewport;
269
- this._viewportChange$.next();
270
- }
271
- constructor(_host, _document, _zone) {
272
- this._host = _host;
273
- this._document = _document;
274
- this._zone = _zone;
275
- this._destroy$ = new Subject();
276
- this._viewportChange$ = new Subject();
277
- this._lastDragPosition = null;
278
- this._overflow = false;
279
- this._thumbSizeFr = 0;
280
- this._thumbPositionFr = 0;
281
- /**
282
- * Specifies the direction of the scrollbar.
283
- *
284
- * By default, if not specified, renders a vertical scrollbar.
285
- */
286
- this.direction = 'vscroll';
287
- this._viewportChange$
288
- .pipe(switchMap(() => {
289
- if (!this._viewport) {
290
- return of(undefined);
291
- }
292
- return merge(this.viewportScrollChange$(), this.viewportDimensionChange$({ debounceTime: SciScrollbarComponent.VIEWPORT_RESIZE_DEBOUNCE_TIME }), this.viewportClientDimensionChange$({ debounceTime: SciScrollbarComponent.VIEWPORT_RESIZE_DEBOUNCE_TIME }));
293
- }), subscribeInside(continueFn => this._zone.runOutsideAngular(continueFn)), takeUntil(this._destroy$))
294
- .subscribe(() => {
295
- if (this._viewport) {
296
- this.renderScrollPosition();
297
- }
298
- else {
299
- this.unsetScrollPosition();
300
- }
301
- });
261
+ viewport = input.required();
262
+ constructor() {
263
+ this.installScrollPositionRenderer();
302
264
  }
303
265
  /**
304
266
  * Computes the scroll position and updates CSS variables to render the scroll position in the UI.
@@ -318,7 +280,7 @@ class SciScrollbarComponent {
318
280
  }
319
281
  if (overflow !== this._overflow) {
320
282
  this._overflow = overflow;
321
- overflow ? this._host.nativeElement.classList.add('overflow') : this._host.nativeElement.classList.remove('overflow');
283
+ overflow ? this._host.classList.add('overflow') : this._host.classList.remove('overflow');
322
284
  }
323
285
  }
324
286
  /**
@@ -331,14 +293,12 @@ class SciScrollbarComponent {
331
293
  this._overflow = false;
332
294
  this.setCssVariable('--ɵsci-scrollbar-thumb-position-fr', 0);
333
295
  this.setCssVariable('--ɵsci-scrollbar-thumb-size-fr', 0);
334
- this._host.nativeElement.classList.remove('overflow');
296
+ this._host.classList.remove('overflow');
335
297
  }
336
- /* @docs-private */
337
298
  onTouchStart(event) {
338
299
  event.preventDefault();
339
300
  this._lastDragPosition = this.vertical ? event.touches[0].screenY : event.touches[0].screenX;
340
301
  }
341
- /* @docs-private */
342
302
  onTouchMove(event) {
343
303
  event.preventDefault();
344
304
  const newDragPositionPx = this.vertical ? event.touches[0].screenY : event.touches[0].screenX;
@@ -347,12 +307,10 @@ class SciScrollbarComponent {
347
307
  this._lastDragPosition = newDragPositionPx;
348
308
  this.moveViewportClient(viewportPanPx);
349
309
  }
350
- /* @docs-private */
351
310
  onTouchEnd(event) {
352
311
  event.preventDefault();
353
312
  this._lastDragPosition = null;
354
313
  }
355
- /* @docs-private */
356
314
  onMouseDown(mousedownEvent) {
357
315
  if (mousedownEvent.button !== 0) {
358
316
  return;
@@ -360,32 +318,49 @@ class SciScrollbarComponent {
360
318
  mousedownEvent.preventDefault();
361
319
  this._lastDragPosition = this.vertical ? mousedownEvent.screenY : mousedownEvent.screenX;
362
320
  // Listen for 'mousemove' events
363
- this._zone.runOutsideAngular(() => {
364
- const mousemoveListener = merge(fromEvent(this._document, 'mousemove'), fromEvent(this._document, 'sci-mousemove'))
365
- .pipe(takeUntil(this._destroy$))
366
- .subscribe(mousemoveEvent => {
367
- mousemoveEvent.preventDefault();
368
- const newDragPositionPx = this.vertical ? mousemoveEvent.screenY : mousemoveEvent.screenX;
369
- const scrollbarPanPx = newDragPositionPx - this._lastDragPosition;
370
- const viewportPanPx = this.toViewportPanPx(scrollbarPanPx);
371
- this._lastDragPosition = newDragPositionPx;
372
- this.moveViewportClient(viewportPanPx);
373
- });
374
- // Listen for 'mouseup' events; use 'capture phase' and 'stop propagation' to not close overlays
375
- merge(fromEvent(this._document, 'mouseup', { capture: true }), fromEvent(this._document, 'sci-mouseup'))
376
- .pipe(first(), takeUntil(this._destroy$))
377
- .subscribe(mouseupEvent => {
378
- mouseupEvent.stopPropagation();
379
- mousemoveListener.unsubscribe();
380
- this._lastDragPosition = null;
381
- });
321
+ const mousemoveListener = merge(fromEvent(this._document, 'mousemove'), fromEvent(this._document, 'sci-mousemove'))
322
+ .pipe(subscribeIn(fn => this._zone.runOutsideAngular(fn)), takeUntilDestroyed(this._destroyRef))
323
+ .subscribe(mousemoveEvent => {
324
+ NgZone.assertNotInAngularZone();
325
+ mousemoveEvent.preventDefault();
326
+ const newDragPositionPx = this.vertical ? mousemoveEvent.screenY : mousemoveEvent.screenX;
327
+ const scrollbarPanPx = newDragPositionPx - this._lastDragPosition;
328
+ const viewportPanPx = this.toViewportPanPx(scrollbarPanPx);
329
+ this._lastDragPosition = newDragPositionPx;
330
+ this.moveViewportClient(viewportPanPx);
331
+ });
332
+ // Listen for 'mouseup' events; use 'capture phase' and 'stop propagation' to not close overlays
333
+ merge(fromEvent(this._document, 'mouseup', { capture: true }), fromEvent(this._document, 'sci-mouseup'))
334
+ .pipe(subscribeIn(fn => this._zone.runOutsideAngular(fn)), first(), takeUntilDestroyed(this._destroyRef))
335
+ .subscribe(mouseupEvent => {
336
+ NgZone.assertNotInAngularZone();
337
+ mouseupEvent.stopPropagation();
338
+ mousemoveListener.unsubscribe();
339
+ this._lastDragPosition = null;
382
340
  });
383
341
  }
384
- /* @docs-private */
385
342
  onScrollTrackMouseDown(event, direction) {
386
343
  const signum = (direction === 'up' ? -1 : +1);
387
344
  this.scrollWhileMouseDown(this.toViewportPanPx(signum * this.thumbSize), event);
388
345
  }
346
+ /**
347
+ * Renders the current scroll position when the viewport is scrolled.
348
+ */
349
+ installScrollPositionRenderer() {
350
+ effect(onCleanup => {
351
+ const viewport = this.viewport();
352
+ untracked(() => {
353
+ if (!viewport) {
354
+ this.unsetScrollPosition();
355
+ return;
356
+ }
357
+ const subscription = viewportScroll$(viewport)
358
+ .pipe(mergeWith(viewportSize$(viewport, { debounceTime: SciScrollbarComponent.VIEWPORT_RESIZE_DEBOUNCE_TIME })), mergeWith(viewportClientSize$(viewport, { debounceTime: SciScrollbarComponent.VIEWPORT_RESIZE_DEBOUNCE_TIME })), subscribeIn(fn => this._zone.runOutsideAngular(fn)))
359
+ .subscribe(() => this.renderScrollPosition());
360
+ onCleanup(() => subscription.unsubscribe());
361
+ });
362
+ });
363
+ }
389
364
  /**
390
365
  * Projects the given scrollbar scroll pixels into viewport scroll pixels.
391
366
  */
@@ -399,10 +374,10 @@ class SciScrollbarComponent {
399
374
  */
400
375
  moveViewportClient(viewportPanPx) {
401
376
  if (this.vertical) {
402
- this._viewport.scrollTop += viewportPanPx;
377
+ this.viewport().scrollTop += viewportPanPx;
403
378
  }
404
379
  else {
405
- this._viewport.scrollLeft += viewportPanPx;
380
+ this.viewport().scrollLeft += viewportPanPx;
406
381
  }
407
382
  }
408
383
  /**
@@ -434,81 +409,32 @@ class SciScrollbarComponent {
434
409
  this.moveViewportClient(viewportScrollPx);
435
410
  });
436
411
  }
437
- /**
438
- * Emits whenever the viewport scrolls.
439
- */
440
- viewportScrollChange$() {
441
- return fromEvent(this._viewport, 'scroll', { passive: true }).pipe(map(() => undefined));
442
- }
443
- /**
444
- * Emits on subscription, and then each time the size of the viewport changes.
445
- */
446
- viewportDimensionChange$(options) {
447
- return fromResize$(this._viewport)
448
- .pipe(
449
- // Debouncing is particularly important in the context of Angular animations, since they continuously
450
- // trigger resize events. Debouncing prevents the scrollbar from flickering, for example, when the user
451
- // expands a panel that contains a viewport.
452
- debounceTime(options.debounceTime), map(() => undefined));
453
- }
454
- /**
455
- * Emits on subscription, and then each time the size or style property of the viewport client changes.
456
- */
457
- viewportClientDimensionChange$(options) {
458
- return this.children$(this._viewport)
459
- .pipe(switchMap(children => merge(...children.map(child => merge(fromResize$(child),
460
- // Observe style mutations since some transformations change the scroll position without necessarily triggering a dimension change,
461
- // e.g., `scale` or `translate` used by some virtual scroll implementations
462
- fromMutation$(child, { subtree: false, childList: false, attributeFilter: ['style'] }))))),
463
- // Debouncing is particularly important in the context of Angular animations, since they continuously
464
- // trigger resize events. Debouncing prevents the scrollbar from flickering, for example, when the user
465
- // expands a panel that contains a viewport.
466
- debounceTime(options.debounceTime), map(() => undefined));
467
- }
468
- /**
469
- * Emits the children of the passed element, and then each time child elements are added or removed.
470
- */
471
- children$(element) {
472
- return fromMutation$(element, { subtree: false, childList: true })
473
- .pipe(startWith(undefined), map(() => Array.from(element.children)), filterArray((child) => child instanceof HTMLElement));
474
- }
475
412
  setCssVariable(key, value) {
476
- this._host.nativeElement.style.setProperty(key, value);
413
+ this._host.style.setProperty(key, value);
477
414
  }
478
415
  get viewportSize() {
479
- return this._viewport ? (this.vertical ? this._viewport.clientHeight : this._viewport.clientWidth) : 0;
416
+ return this.viewport() ? (this.vertical ? this.viewport().clientHeight : this.viewport().clientWidth) : 0;
480
417
  }
481
418
  get viewportClientSize() {
482
- return this._viewport ? (this.vertical ? this._viewport.scrollHeight : this._viewport.scrollWidth) : 0;
419
+ return this.viewport() ? (this.vertical ? this.viewport().scrollHeight : this.viewport().scrollWidth) : 0;
483
420
  }
484
421
  get scrollPosition() {
485
- return this._viewport ? (this.vertical ? this._viewport.scrollTop : this._viewport.scrollLeft) : 0;
422
+ return this.viewport() ? (this.vertical ? this.viewport().scrollTop : this.viewport().scrollLeft) : 0;
486
423
  }
487
424
  get thumbSize() {
488
- const thumbElement = this.thumbElement.nativeElement;
425
+ const thumbElement = this._thumbElement().nativeElement;
489
426
  return this.vertical ? thumbElement.clientHeight : thumbElement.clientWidth;
490
427
  }
491
428
  get trackSize() {
492
- const trackElement = this._host.nativeElement;
493
- return this.vertical ? trackElement.clientHeight : trackElement.clientWidth;
494
- }
495
- /* @docs-private */
496
- ngOnDestroy() {
497
- this._destroy$.next();
429
+ return this.vertical ? this._host.clientHeight : this._host.clientWidth;
498
430
  }
499
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciScrollbarComponent, deps: [{ token: i0.ElementRef }, { token: DOCUMENT }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
500
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", type: SciScrollbarComponent, isStandalone: true, selector: "sci-scrollbar", inputs: { direction: "direction", setViewport: ["viewport", "setViewport"] }, host: { properties: { "class.vertical": "this.vertical", "class.horizontal": "this.horizontal", "class.scrolling": "this.scrolling" } }, viewQueries: [{ propertyName: "thumbElement", first: true, predicate: ["thumb_handle"], descendants: true, static: true }], ngImport: i0, template: "<!-- scrolltrack to move the viewport client upwards (paging) -->\n<div class=\"scrolltrack-start\" (mousedown)=\"onScrollTrackMouseDown($event, 'up')\"></div>\n\n<!-- scrollthumb -->\n<div #thumb_handle\n class=\"thumb-handle\"\n [class.vertical]=\"vertical\"\n [class.horizontal]=\"horizontal\"\n (mousedown)=\"onMouseDown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (touchmove)=\"onTouchMove($event)\"\n (touchend)=\"onTouchEnd($event)\"\n (touchcancel)=\"onTouchEnd($event)\">\n <div class=\"thumb\"\n [class.vertical]=\"vertical\"\n [class.horizontal]=\"horizontal\">\n </div>\n</div>\n\n<!-- scrolltrack to move the viewport client downwards (paging) -->\n<div class=\"scrolltrack-end\" (mousedown)=\"onScrollTrackMouseDown($event, 'down')\"></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;border:2px solid transparent;box-sizing:border-box;--\\275sci-scrollbar-thumb-position-fr: 0;--\\275sci-scrollbar-thumb-size-fr: 0}:host.vertical{flex-direction:column}:host.horizontal{flex-direction:row}:host>div.scrolltrack-start{flex-grow:var(--\\275sci-scrollbar-thumb-position-fr)}:host>div.thumb-handle{flex-basis:20px;display:flex;flex-grow:var(--\\275sci-scrollbar-thumb-size-fr);align-items:center}:host>div.thumb-handle.vertical{flex-direction:column}:host>div.thumb-handle.horizontal{flex-direction:row}:host>div.thumb-handle>div.thumb{flex:auto;border-radius:4px;background-color:var(--sci-scrollbar-color);opacity:.4;transition-duration:125ms;transition-property:width,height;transition-timing-function:ease-out}:host>div.thumb-handle>div.thumb.vertical{width:60%}:host>div.thumb-handle>div.thumb.horizontal{height:60%}:host>div.scrolltrack-end{flex-grow:calc(1 - (var(--\\275sci-scrollbar-thumb-position-fr) + var(--\\275sci-scrollbar-thumb-size-fr)))}:host-context:not(.overflow){display:none}:host-context:hover>div.thumb-handle>div.thumb,:host-context(.scrolling)>div.thumb-handle>div.thumb{background-color:var(--sci-scrollbar-color);opacity:.75}:host-context:hover>div.thumb-handle>div.thumb.vertical,:host-context(.scrolling)>div.thumb-handle>div.thumb.vertical{width:100%}:host-context:hover>div.thumb-handle>div.thumb.horizontal,:host-context(.scrolling)>div.thumb-handle>div.thumb.horizontal{height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
431
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciScrollbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
432
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.0.0", type: SciScrollbarComponent, isStandalone: true, selector: "sci-scrollbar", inputs: { direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, viewport: { classPropertyName: "viewport", publicName: "viewport", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.vertical": "this.vertical", "class.horizontal": "this.horizontal", "class.scrolling": "this.scrolling" } }, viewQueries: [{ propertyName: "_thumbElement", first: true, predicate: ["thumb_handle"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- scrolltrack to move the viewport client upwards (paging) -->\n<div class=\"scrolltrack-start\" (mousedown)=\"onScrollTrackMouseDown($event, 'up')\"></div>\n\n<!-- scrollthumb -->\n<div #thumb_handle\n class=\"thumb-handle\"\n [class.vertical]=\"vertical\"\n [class.horizontal]=\"horizontal\"\n (mousedown)=\"onMouseDown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (touchmove)=\"onTouchMove($event)\"\n (touchend)=\"onTouchEnd($event)\"\n (touchcancel)=\"onTouchEnd($event)\">\n <div class=\"thumb\"\n [class.vertical]=\"vertical\"\n [class.horizontal]=\"horizontal\">\n </div>\n</div>\n\n<!-- scrolltrack to move the viewport client downwards (paging) -->\n<div class=\"scrolltrack-end\" (mousedown)=\"onScrollTrackMouseDown($event, 'down')\"></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;border:2px solid transparent;box-sizing:border-box;--\\275sci-scrollbar-thumb-position-fr: 0;--\\275sci-scrollbar-thumb-size-fr: 0}:host.vertical{flex-direction:column}:host.horizontal{flex-direction:row}:host>div.scrolltrack-start{flex-grow:var(--\\275sci-scrollbar-thumb-position-fr)}:host>div.thumb-handle{flex-basis:20px;display:flex;flex-grow:var(--\\275sci-scrollbar-thumb-size-fr);align-items:center}:host>div.thumb-handle.vertical{flex-direction:column}:host>div.thumb-handle.horizontal{flex-direction:row}:host>div.thumb-handle>div.thumb{flex:auto;border-radius:4px;background-color:var(--sci-scrollbar-color);opacity:.4;transition-duration:125ms;transition-property:width,height;transition-timing-function:ease-out}:host>div.thumb-handle>div.thumb.vertical{width:60%}:host>div.thumb-handle>div.thumb.horizontal{height:60%}:host>div.scrolltrack-end{flex-grow:calc(1 - (var(--\\275sci-scrollbar-thumb-position-fr) + var(--\\275sci-scrollbar-thumb-size-fr)))}:host-context:not(.overflow){display:none}:host-context:hover>div.thumb-handle>div.thumb,:host-context(.scrolling)>div.thumb-handle>div.thumb{background-color:var(--sci-scrollbar-color);opacity:.75}:host-context:hover>div.thumb-handle>div.thumb.vertical,:host-context(.scrolling)>div.thumb-handle>div.thumb.vertical{width:100%}:host-context:hover>div.thumb-handle>div.thumb.horizontal,:host-context(.scrolling)>div.thumb-handle>div.thumb.horizontal{height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
501
433
  }
502
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciScrollbarComponent, decorators: [{
434
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciScrollbarComponent, decorators: [{
503
435
  type: Component,
504
- args: [{ selector: 'sci-scrollbar', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "<!-- scrolltrack to move the viewport client upwards (paging) -->\n<div class=\"scrolltrack-start\" (mousedown)=\"onScrollTrackMouseDown($event, 'up')\"></div>\n\n<!-- scrollthumb -->\n<div #thumb_handle\n class=\"thumb-handle\"\n [class.vertical]=\"vertical\"\n [class.horizontal]=\"horizontal\"\n (mousedown)=\"onMouseDown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (touchmove)=\"onTouchMove($event)\"\n (touchend)=\"onTouchEnd($event)\"\n (touchcancel)=\"onTouchEnd($event)\">\n <div class=\"thumb\"\n [class.vertical]=\"vertical\"\n [class.horizontal]=\"horizontal\">\n </div>\n</div>\n\n<!-- scrolltrack to move the viewport client downwards (paging) -->\n<div class=\"scrolltrack-end\" (mousedown)=\"onScrollTrackMouseDown($event, 'down')\"></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;border:2px solid transparent;box-sizing:border-box;--\\275sci-scrollbar-thumb-position-fr: 0;--\\275sci-scrollbar-thumb-size-fr: 0}:host.vertical{flex-direction:column}:host.horizontal{flex-direction:row}:host>div.scrolltrack-start{flex-grow:var(--\\275sci-scrollbar-thumb-position-fr)}:host>div.thumb-handle{flex-basis:20px;display:flex;flex-grow:var(--\\275sci-scrollbar-thumb-size-fr);align-items:center}:host>div.thumb-handle.vertical{flex-direction:column}:host>div.thumb-handle.horizontal{flex-direction:row}:host>div.thumb-handle>div.thumb{flex:auto;border-radius:4px;background-color:var(--sci-scrollbar-color);opacity:.4;transition-duration:125ms;transition-property:width,height;transition-timing-function:ease-out}:host>div.thumb-handle>div.thumb.vertical{width:60%}:host>div.thumb-handle>div.thumb.horizontal{height:60%}:host>div.scrolltrack-end{flex-grow:calc(1 - (var(--\\275sci-scrollbar-thumb-position-fr) + var(--\\275sci-scrollbar-thumb-size-fr)))}:host-context:not(.overflow){display:none}:host-context:hover>div.thumb-handle>div.thumb,:host-context(.scrolling)>div.thumb-handle>div.thumb{background-color:var(--sci-scrollbar-color);opacity:.75}:host-context:hover>div.thumb-handle>div.thumb.vertical,:host-context(.scrolling)>div.thumb-handle>div.thumb.vertical{width:100%}:host-context:hover>div.thumb-handle>div.thumb.horizontal,:host-context(.scrolling)>div.thumb-handle>div.thumb.horizontal{height:100%}\n"] }]
505
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: Document, decorators: [{
506
- type: Inject,
507
- args: [DOCUMENT]
508
- }] }, { type: i0.NgZone }], propDecorators: { thumbElement: [{
509
- type: ViewChild,
510
- args: ['thumb_handle', { static: true }]
511
- }], vertical: [{
436
+ args: [{ selector: 'sci-scrollbar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- scrolltrack to move the viewport client upwards (paging) -->\n<div class=\"scrolltrack-start\" (mousedown)=\"onScrollTrackMouseDown($event, 'up')\"></div>\n\n<!-- scrollthumb -->\n<div #thumb_handle\n class=\"thumb-handle\"\n [class.vertical]=\"vertical\"\n [class.horizontal]=\"horizontal\"\n (mousedown)=\"onMouseDown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (touchmove)=\"onTouchMove($event)\"\n (touchend)=\"onTouchEnd($event)\"\n (touchcancel)=\"onTouchEnd($event)\">\n <div class=\"thumb\"\n [class.vertical]=\"vertical\"\n [class.horizontal]=\"horizontal\">\n </div>\n</div>\n\n<!-- scrolltrack to move the viewport client downwards (paging) -->\n<div class=\"scrolltrack-end\" (mousedown)=\"onScrollTrackMouseDown($event, 'down')\"></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;border:2px solid transparent;box-sizing:border-box;--\\275sci-scrollbar-thumb-position-fr: 0;--\\275sci-scrollbar-thumb-size-fr: 0}:host.vertical{flex-direction:column}:host.horizontal{flex-direction:row}:host>div.scrolltrack-start{flex-grow:var(--\\275sci-scrollbar-thumb-position-fr)}:host>div.thumb-handle{flex-basis:20px;display:flex;flex-grow:var(--\\275sci-scrollbar-thumb-size-fr);align-items:center}:host>div.thumb-handle.vertical{flex-direction:column}:host>div.thumb-handle.horizontal{flex-direction:row}:host>div.thumb-handle>div.thumb{flex:auto;border-radius:4px;background-color:var(--sci-scrollbar-color);opacity:.4;transition-duration:125ms;transition-property:width,height;transition-timing-function:ease-out}:host>div.thumb-handle>div.thumb.vertical{width:60%}:host>div.thumb-handle>div.thumb.horizontal{height:60%}:host>div.scrolltrack-end{flex-grow:calc(1 - (var(--\\275sci-scrollbar-thumb-position-fr) + var(--\\275sci-scrollbar-thumb-size-fr)))}:host-context:not(.overflow){display:none}:host-context:hover>div.thumb-handle>div.thumb,:host-context(.scrolling)>div.thumb-handle>div.thumb{background-color:var(--sci-scrollbar-color);opacity:.75}:host-context:hover>div.thumb-handle>div.thumb.vertical,:host-context(.scrolling)>div.thumb-handle>div.thumb.vertical{width:100%}:host-context:hover>div.thumb-handle>div.thumb.horizontal,:host-context(.scrolling)>div.thumb-handle>div.thumb.horizontal{height:100%}\n"] }]
437
+ }], ctorParameters: () => [], propDecorators: { vertical: [{
512
438
  type: HostBinding,
513
439
  args: ['class.vertical']
514
440
  }], horizontal: [{
@@ -517,12 +443,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImpor
517
443
  }], scrolling: [{
518
444
  type: HostBinding,
519
445
  args: ['class.scrolling']
520
- }], direction: [{
521
- type: Input
522
- }], setViewport: [{
523
- type: Input,
524
- args: [{ alias: 'viewport', required: true }]
525
446
  }] } });
447
+ /**
448
+ * Emits whenever the viewport scrolls.
449
+ */
450
+ function viewportScroll$(viewport) {
451
+ return fromEvent(viewport, 'scroll', { passive: true }).pipe(map(() => undefined));
452
+ }
453
+ /**
454
+ * Emits on subscription, and then each time the size of the viewport changes.
455
+ */
456
+ function viewportSize$(viewport, options) {
457
+ return fromResize$(viewport)
458
+ .pipe(
459
+ // Debouncing is particularly important in the context of Angular animations, since they continuously
460
+ // trigger resize events. Debouncing prevents the scrollbar from flickering, for example, when the user
461
+ // expands a panel that contains a viewport.
462
+ debounceTime(options.debounceTime), map(() => undefined));
463
+ }
464
+ /**
465
+ * Emits on subscription, and then each time the size or style property of the viewport client changes.
466
+ */
467
+ function viewportClientSize$(viewport, options) {
468
+ return children$(viewport)
469
+ .pipe(switchMap(children => merge(...children.map(child => merge(fromResize$(child),
470
+ // Observe style mutations since some transformations change the scroll position without necessarily triggering a dimension change,
471
+ // e.g., `scale` or `translate` used by some virtual scroll implementations
472
+ fromMutation$(child, { subtree: false, childList: false, attributeFilter: ['style'] }))))),
473
+ // Debouncing is particularly important in the context of Angular animations, since they continuously
474
+ // trigger resize events. Debouncing prevents the scrollbar from flickering, for example, when the user
475
+ // expands a panel that contains a viewport.
476
+ debounceTime(options.debounceTime), map(() => undefined));
477
+ }
478
+ /**
479
+ * Emits the children of the passed element, and then each time child elements are added or removed.
480
+ */
481
+ function children$(element) {
482
+ return fromMutation$(element, { subtree: false, childList: true })
483
+ .pipe(startWith(undefined), map(() => Array.from(element.children)), filterArray((child) => child instanceof HTMLElement));
484
+ }
526
485
 
527
486
  /*
528
487
  * Copyright (c) 2018-2019 Swiss Federal Railways
@@ -601,40 +560,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImpor
601
560
  * ```
602
561
  */
603
562
  class SciViewportComponent {
604
- /** @internal */
605
- set setViewport(viewport) {
606
- this._viewportElement = coerceElement(viewport);
607
- }
608
- /** @internal */
609
- set setViewportClient(viewportClient) {
610
- this._viewportClient = coerceElement(viewportClient);
611
- }
563
+ _viewport = viewChild.required('viewport');
564
+ _viewportClient = viewChild.required('viewport_client');
565
+ _host = inject((ElementRef)).nativeElement;
566
+ nativeScrollbarTrackSizeProvider = inject(SciNativeScrollbarTrackSizeProvider);
612
567
  /**
613
- * Controls whether to use native scrollbars or, which is by default, emulated scrollbars that sit on top of the viewport client.
614
- * In the latter, the viewport client remains natively scrollable.
568
+ * Controls if to use the native scrollbar or a scrollbar that sits on top of the viewport. Defaults to `on-top`.
615
569
  */
616
- set scrollbarStyle(scrollbarStyle) {
617
- this._scrollbarStyle = scrollbarStyle || 'on-top';
618
- }
619
- get scrollbarStyle() {
620
- return this._scrollbarStyle;
621
- }
622
- constructor(host, nativeScrollbarTrackSizeProvider) {
623
- this.host = host;
624
- this.nativeScrollbarTrackSizeProvider = nativeScrollbarTrackSizeProvider;
625
- this._scrollbarStyle = 'on-top';
626
- /**
627
- * Emits upon a scroll event.
628
- *
629
- * You can add [sciDimension] directive to the viewport or viewport client to be notified about layout changes.
630
- */
631
- this.scroll = new EventEmitter(); // eslint-disable-line @angular-eslint/no-output-native
570
+ scrollbarStyle = input('on-top');
571
+ /**
572
+ * Emits when the viewport is scrolled. The event is emitted outside the Angular zone to avoid unnecessary change detection cycles.
573
+ */
574
+ scroll = output();
575
+ constructor() {
576
+ this.installScrollEmitter();
632
577
  }
633
578
  focus() {
634
- this._viewportElement?.focus();
635
- }
636
- onScroll(event) {
637
- this.scroll.emit(event);
579
+ this.viewportElement.focus();
638
580
  }
639
581
  /**
640
582
  * Returns the number of pixels that the viewport client is scrolled vertically.
@@ -642,7 +584,7 @@ class SciViewportComponent {
642
584
  * @see Element.scrollTop
643
585
  */
644
586
  get scrollTop() {
645
- return this._viewportElement.scrollTop;
587
+ return this.viewportElement.scrollTop;
646
588
  }
647
589
  /**
648
590
  * Sets the number of pixels that the viewport client is scrolled vertically.
@@ -650,7 +592,7 @@ class SciViewportComponent {
650
592
  * @see Element.scrollTop
651
593
  */
652
594
  set scrollTop(scrollTop) {
653
- this._viewportElement.scrollTop = scrollTop;
595
+ this.viewportElement.scrollTop = scrollTop;
654
596
  }
655
597
  /**
656
598
  * Returns the number of pixels that the viewport client is scrolled horizontally.
@@ -658,7 +600,7 @@ class SciViewportComponent {
658
600
  * @see Element.scrollLeft
659
601
  */
660
602
  get scrollLeft() {
661
- return this._viewportElement.scrollLeft;
603
+ return this.viewportElement.scrollLeft;
662
604
  }
663
605
  /**
664
606
  * Sets the number of pixels that the viewport client is scrolled horizontally.
@@ -666,7 +608,7 @@ class SciViewportComponent {
666
608
  * @see Element.scrollLeft
667
609
  */
668
610
  set scrollLeft(scrollLeft) {
669
- this._viewportElement.scrollLeft = scrollLeft;
611
+ this.viewportElement.scrollLeft = scrollLeft;
670
612
  }
671
613
  /**
672
614
  * Returns the height of the viewport client.
@@ -674,7 +616,7 @@ class SciViewportComponent {
674
616
  * @see Element.scrollHeight
675
617
  */
676
618
  get scrollHeight() {
677
- return this._viewportElement.scrollHeight;
619
+ return this.viewportElement.scrollHeight;
678
620
  }
679
621
  /**
680
622
  * Returns the width of the viewport client.
@@ -682,19 +624,19 @@ class SciViewportComponent {
682
624
  * @see Element.scrollWidth
683
625
  */
684
626
  get scrollWidth() {
685
- return this._viewportElement.scrollWidth;
627
+ return this.viewportElement.scrollWidth;
686
628
  }
687
629
  /**
688
630
  * Returns the viewport {HTMLElement}.
689
631
  */
690
632
  get viewportElement() {
691
- return this._viewportElement;
633
+ return this._viewport().nativeElement;
692
634
  }
693
635
  /**
694
636
  * Returns the viewport client {HTMLElement}.
695
637
  */
696
638
  get viewportClientElement() {
697
- return this._viewportClient;
639
+ return this._viewportClient().nativeElement;
698
640
  }
699
641
  /**
700
642
  * Checks if the specified element is scrolled into the viewport.
@@ -716,15 +658,15 @@ class SciViewportComponent {
716
658
  // The calculation of whether an element is scrolled into view may be wrong by a few pixels if the viewport contains elements with decimal sizes.
717
659
  // This can happen because `offsetLeft` and `offsetTop` operate on an integer (not a decimal), losing precision that can accumulate.
718
660
  // To avoid incorrect calculation when there is no viewport overflow, we consider all contained elements as scrolled into the view.
719
- if (this._viewportElement.scrollWidth <= this._viewportElement.clientWidth && this._viewportElement.scrollHeight <= this._viewportElement.clientHeight) {
661
+ if (this.viewportElement.scrollWidth <= this.viewportElement.clientWidth && this.viewportElement.scrollHeight <= this.viewportElement.clientHeight) {
720
662
  return true;
721
663
  }
722
664
  const elBottom = elTop + coerceElement(element).offsetHeight;
723
665
  const elRight = elLeft + coerceElement(element).offsetWidth;
724
- const vpTop = this._viewportElement.scrollTop;
725
- const vpLeft = this._viewportElement.scrollLeft;
726
- const vpBottom = vpTop + this._viewportElement.clientHeight;
727
- const vpRight = vpLeft + this._viewportElement.clientWidth;
666
+ const vpTop = this.viewportElement.scrollTop;
667
+ const vpLeft = this.viewportElement.scrollLeft;
668
+ const vpBottom = vpTop + this.viewportElement.clientHeight;
669
+ const vpRight = vpLeft + this.viewportElement.clientWidth;
728
670
  if (fit === 'full') {
729
671
  return (elTop >= vpTop && elBottom <= vpBottom) && (elLeft >= vpLeft && elRight <= vpRight);
730
672
  }
@@ -747,8 +689,8 @@ class SciViewportComponent {
747
689
  if (left === null) {
748
690
  return;
749
691
  }
750
- this._viewportElement.scrollTop = top - offset;
751
- this._viewportElement.scrollLeft = left - offset;
692
+ this.viewportElement.scrollTop = top - offset;
693
+ this.viewportElement.scrollLeft = left - offset;
752
694
  }
753
695
  /**
754
696
  * Computes the distance of the element to the viewport's left or top border.
@@ -774,31 +716,35 @@ class SciViewportComponent {
774
716
  return null;
775
717
  }
776
718
  el = offsetParent;
777
- } while (el !== this.host.nativeElement);
719
+ } while (el !== this._host);
778
720
  return offset;
779
721
  }
780
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciViewportComponent, deps: [{ token: i0.ElementRef }, { token: SciNativeScrollbarTrackSizeProvider }], target: i0.ɵɵFactoryTarget.Component }); }
781
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.1", type: SciViewportComponent, isStandalone: true, selector: "sci-viewport", inputs: { scrollbarStyle: "scrollbarStyle" }, outputs: { scroll: "scroll" }, host: { listeners: { "focus": "focus()" } }, viewQueries: [{ propertyName: "setViewport", first: true, predicate: ["viewport"], descendants: true, static: true }, { propertyName: "setViewportClient", first: true, predicate: ["viewport_client"], descendants: true, static: true }], ngImport: i0, template: "<!-- viewport and viewport client -->\n<div #viewport\n class=\"viewport\"\n tabindex=\"-1\"\n sciScrollable [sciScrollableDisplayNativeScrollbar]=\"scrollbarStyle === 'native'\"\n cdkScrollable\n (scroll)=\"onScroll($event)\">\n <div #viewport_client class=\"viewport-client\" part=\"content\">\n <slot></slot>\n </div>\n</div>\n\n<!-- render emulated scrollbars which sit on-top -->\n@if (scrollbarStyle === 'on-top' && (nativeScrollbarTrackSizeProvider.trackSize$ | async) !== null) {\n <sci-scrollbar [direction]=\"'vscroll'\" [viewport]=\"viewport\" class=\"e2e-vertical\"/>\n <sci-scrollbar [direction]=\"'hscroll'\" [viewport]=\"viewport\" class=\"e2e-horizontal\"/>\n}\n", styles: [":host{display:grid;grid-template-columns:100%;grid-template-rows:100%;position:relative;overflow:hidden;outline:none}:host:host-context(:not(:hover):not(:active)) sci-scrollbar:not(.scrolling){opacity:0}:host>div.viewport{display:grid;position:relative;outline:none;-webkit-overflow-scrolling:touch}:host>div.viewport>div.viewport-client{display:grid}:host>sci-scrollbar{--sci-scrollbar-color: var(--sci-viewport-scrollbar-color);position:absolute;transition-duration:1s;transition-property:opacity;transition-timing-function:ease-out}:host>sci-scrollbar.vertical{top:0;right:0;bottom:0;left:unset;width:12px}:host>sci-scrollbar.horizontal{top:unset;right:0;bottom:0;left:0;height:12px}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i2.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "directive", type: SciScrollableDirective, selector: "[sciScrollable]", inputs: ["sciScrollableDisplayNativeScrollbar"] }, { kind: "component", type: SciScrollbarComponent, selector: "sci-scrollbar", inputs: ["direction", "viewport"] }], encapsulation: i0.ViewEncapsulation.ShadowDom }); }
722
+ /**
723
+ * Emits when the scroll position changes.
724
+ */
725
+ installScrollEmitter() {
726
+ const zone = inject(NgZone);
727
+ effect(onCleanup => {
728
+ const viewport = this._viewport();
729
+ untracked(() => {
730
+ const subscription = fromEvent(viewport.nativeElement, 'scroll')
731
+ .pipe(subscribeIn(fn => zone.runOutsideAngular(fn)))
732
+ .subscribe(event => this.scroll.emit(event));
733
+ onCleanup(() => subscription.unsubscribe());
734
+ });
735
+ });
736
+ }
737
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciViewportComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
738
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.0", type: SciViewportComponent, isStandalone: true, selector: "sci-viewport", inputs: { scrollbarStyle: { classPropertyName: "scrollbarStyle", publicName: "scrollbarStyle", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { scroll: "scroll" }, host: { listeners: { "focus": "focus()" } }, viewQueries: [{ propertyName: "_viewport", first: true, predicate: ["viewport"], descendants: true, isSignal: true }, { propertyName: "_viewportClient", first: true, predicate: ["viewport_client"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- viewport and viewport client -->\n<div #viewport\n class=\"viewport\"\n tabindex=\"-1\"\n sciScrollable [sciScrollableDisplayNativeScrollbar]=\"scrollbarStyle() === 'native'\"\n cdkScrollable>\n <div #viewport_client class=\"viewport-client\" part=\"content\">\n <slot></slot>\n </div>\n</div>\n\n<!-- render emulated scrollbars which sit on-top -->\n@if (scrollbarStyle() === 'on-top' && nativeScrollbarTrackSizeProvider.trackSize() !== null) {\n <sci-scrollbar [direction]=\"'vscroll'\" [viewport]=\"viewport\" class=\"e2e-vertical\"/>\n <sci-scrollbar [direction]=\"'hscroll'\" [viewport]=\"viewport\" class=\"e2e-horizontal\"/>\n}\n", styles: [":host{display:grid;grid-template-columns:100%;grid-template-rows:100%;position:relative;overflow:hidden;outline:none}:host:host-context(:not(:hover):not(:active)) sci-scrollbar:not(.scrolling){opacity:0}:host>div.viewport{display:grid;position:relative;outline:none;-webkit-overflow-scrolling:touch}:host>div.viewport>div.viewport-client{display:grid}:host>sci-scrollbar{--sci-scrollbar-color: var(--sci-viewport-scrollbar-color);position:absolute;transition-duration:1s;transition-property:opacity;transition-timing-function:ease-out}:host>sci-scrollbar.vertical{top:0;right:0;bottom:0;left:unset;width:12px}:host>sci-scrollbar.horizontal{top:unset;right:0;bottom:0;left:0;height:12px}\n"], dependencies: [{ kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "directive", type: SciScrollableDirective, selector: "[sciScrollable]", inputs: ["sciScrollableDisplayNativeScrollbar"] }, { kind: "component", type: SciScrollbarComponent, selector: "sci-scrollbar", inputs: ["direction", "viewport"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
782
739
  }
783
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciViewportComponent, decorators: [{
740
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciViewportComponent, decorators: [{
784
741
  type: Component,
785
- args: [{ selector: 'sci-viewport', encapsulation: ViewEncapsulation.ShadowDom, standalone: true, imports: [
786
- AsyncPipe,
742
+ args: [{ selector: 'sci-viewport', encapsulation: ViewEncapsulation.ShadowDom, imports: [
787
743
  ScrollingModule,
788
744
  SciScrollableDirective,
789
745
  SciScrollbarComponent,
790
- ], template: "<!-- viewport and viewport client -->\n<div #viewport\n class=\"viewport\"\n tabindex=\"-1\"\n sciScrollable [sciScrollableDisplayNativeScrollbar]=\"scrollbarStyle === 'native'\"\n cdkScrollable\n (scroll)=\"onScroll($event)\">\n <div #viewport_client class=\"viewport-client\" part=\"content\">\n <slot></slot>\n </div>\n</div>\n\n<!-- render emulated scrollbars which sit on-top -->\n@if (scrollbarStyle === 'on-top' && (nativeScrollbarTrackSizeProvider.trackSize$ | async) !== null) {\n <sci-scrollbar [direction]=\"'vscroll'\" [viewport]=\"viewport\" class=\"e2e-vertical\"/>\n <sci-scrollbar [direction]=\"'hscroll'\" [viewport]=\"viewport\" class=\"e2e-horizontal\"/>\n}\n", styles: [":host{display:grid;grid-template-columns:100%;grid-template-rows:100%;position:relative;overflow:hidden;outline:none}:host:host-context(:not(:hover):not(:active)) sci-scrollbar:not(.scrolling){opacity:0}:host>div.viewport{display:grid;position:relative;outline:none;-webkit-overflow-scrolling:touch}:host>div.viewport>div.viewport-client{display:grid}:host>sci-scrollbar{--sci-scrollbar-color: var(--sci-viewport-scrollbar-color);position:absolute;transition-duration:1s;transition-property:opacity;transition-timing-function:ease-out}:host>sci-scrollbar.vertical{top:0;right:0;bottom:0;left:unset;width:12px}:host>sci-scrollbar.horizontal{top:unset;right:0;bottom:0;left:0;height:12px}\n"] }]
791
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: SciNativeScrollbarTrackSizeProvider }], propDecorators: { setViewport: [{
792
- type: ViewChild,
793
- args: ['viewport', { static: true }]
794
- }], setViewportClient: [{
795
- type: ViewChild,
796
- args: ['viewport_client', { static: true }]
797
- }], scrollbarStyle: [{
798
- type: Input
799
- }], scroll: [{
800
- type: Output
801
- }], focus: [{
746
+ ], template: "<!-- viewport and viewport client -->\n<div #viewport\n class=\"viewport\"\n tabindex=\"-1\"\n sciScrollable [sciScrollableDisplayNativeScrollbar]=\"scrollbarStyle() === 'native'\"\n cdkScrollable>\n <div #viewport_client class=\"viewport-client\" part=\"content\">\n <slot></slot>\n </div>\n</div>\n\n<!-- render emulated scrollbars which sit on-top -->\n@if (scrollbarStyle() === 'on-top' && nativeScrollbarTrackSizeProvider.trackSize() !== null) {\n <sci-scrollbar [direction]=\"'vscroll'\" [viewport]=\"viewport\" class=\"e2e-vertical\"/>\n <sci-scrollbar [direction]=\"'hscroll'\" [viewport]=\"viewport\" class=\"e2e-horizontal\"/>\n}\n", styles: [":host{display:grid;grid-template-columns:100%;grid-template-rows:100%;position:relative;overflow:hidden;outline:none}:host:host-context(:not(:hover):not(:active)) sci-scrollbar:not(.scrolling){opacity:0}:host>div.viewport{display:grid;position:relative;outline:none;-webkit-overflow-scrolling:touch}:host>div.viewport>div.viewport-client{display:grid}:host>sci-scrollbar{--sci-scrollbar-color: var(--sci-viewport-scrollbar-color);position:absolute;transition-duration:1s;transition-property:opacity;transition-timing-function:ease-out}:host>sci-scrollbar.vertical{top:0;right:0;bottom:0;left:unset;width:12px}:host>sci-scrollbar.horizontal{top:unset;right:0;bottom:0;left:0;height:12px}\n"] }]
747
+ }], ctorParameters: () => [], propDecorators: { focus: [{
802
748
  type: HostListener,
803
749
  args: ['focus']
804
750
  }] } });
@@ -814,17 +760,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImpor
814
760
  */
815
761
  /**
816
762
  * Provides a viewport component with scrollbars that sit on top of the viewport client.
763
+ *
764
+ * @deprecated since version 19.0.0; Import standalone components and directive instead; API will be removed in a future release.
817
765
  */
818
766
  class SciViewportModule {
819
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciViewportModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
820
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.1", ngImport: i0, type: SciViewportModule, imports: [SciViewportComponent,
767
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciViewportModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
768
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.0", ngImport: i0, type: SciViewportModule, imports: [SciViewportComponent,
821
769
  SciScrollbarComponent,
822
770
  SciScrollableDirective], exports: [SciViewportComponent,
823
771
  SciScrollbarComponent,
824
- SciScrollableDirective] }); }
825
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciViewportModule, imports: [SciViewportComponent] }); }
772
+ SciScrollableDirective] });
773
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciViewportModule, imports: [SciViewportComponent] });
826
774
  }
827
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: SciViewportModule, decorators: [{
775
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SciViewportModule, decorators: [{
828
776
  type: NgModule,
829
777
  args: [{
830
778
  imports: [