@libs-ui/components-scroll-overlay 0.2.29 → 0.2.30-6.2

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.
@@ -1,84 +1,107 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, input, output, inject, ElementRef, Renderer2, effect, untracked, Directive } from '@angular/core';
3
- import { Subject, debounceTime, fromEvent, tap, takeUntil } from 'rxjs';
2
+ import { computed, signal, input, output, inject, ElementRef, Renderer2, effect, untracked, Directive, Component } from '@angular/core';
3
+ import { getDragEventByElement, checkMouseOverInContainer } from '@libs-ui/utils';
4
+ import { Subscription, Subject, fromEvent, tap, takeUntil, switchMap, interval } from 'rxjs';
5
+ import * as i1 from '@angular/common';
6
+ import { CommonModule } from '@angular/common';
4
7
 
5
8
  /* eslint-disable @typescript-eslint/no-explicit-any */
6
9
  class LibsUiComponentsScrollOverlayDirective {
7
- /* PROPERTY */
8
- styles = signal(`
10
+ // #region PROPERTY
11
+ styles = computed(() => `
12
+ .scrollbar-track{
13
+ background-color:${this.scrollbarColor()};
14
+ }
15
+ .scrollbar-track:hover{
16
+ background-color:${this.scrollbarHoverColor()};
17
+ }
9
18
  .scrollbar-track-X {
19
+ width:100%;
10
20
  position: absolute;
11
21
  bottom: 0;
12
22
  left: 0;
13
- height: 8px;
14
- pointer-events: none;
15
23
  visibility: hidden;
24
+ cursor: pointer;
16
25
  opacity: 0;
26
+ z-index: 1;
17
27
  transition: opacity 0.3s ease, visibility 0.3s ease;
18
28
  }
19
29
 
20
- .scrollbar-thumb-X {
21
- height: 100%;
22
- background-color: #CDD0D6;
23
- border-radius: 4px;
24
- cursor: pointer;
25
- transition: background-color 0.3s;
26
- pointer-events: auto;
27
- position: absolute;
28
- }
29
-
30
30
  .scrollbar-track-Y {
31
+ height:100%;
31
32
  position: absolute;
32
33
  top: 0;
33
34
  right: 0;
34
- width: 8px;
35
- pointer-events: none;
36
35
  visibility: hidden;
36
+ cursor: pointer;
37
37
  opacity: 0;
38
+ z-index: 1;
38
39
  transition: opacity 0.3s ease, visibility 0.3s ease;
39
40
  }
40
41
 
41
- .scrollbar-thumb-Y {
42
- width: 100%;
43
- background-color: #CDD0D6;
42
+ .scrollbar-thumb{
43
+ background-color:${this.scrollThumbColor()};
44
+ }
45
+ .scrollbar-thumb:hover{
46
+ background-color:${this.scrollThumbHoverColor()};
47
+ }
48
+
49
+ .scrollbar-thumb-X {
50
+ height: calc(100% - ${this.scrollbarPadding() * 2}px);
51
+ bottom: ${this.scrollbarPadding()}px;
44
52
  border-radius: 4px;
45
- cursor: pointer;
53
+ cursor: grabbing;
46
54
  transition: background-color 0.3s;
47
- pointer-events: auto;
48
55
  position: absolute;
49
56
  }
50
57
 
51
- .scrollbar-thumb:hover {
52
- background-color: #9CA2AD;
58
+ .scrollbar-thumb-Y {
59
+ width: calc(100% - ${this.scrollbarPadding() * 2}px);
60
+ right: ${this.scrollbarPadding()}px;
61
+ border-radius: 4px;
62
+ cursor: grabbing;
63
+ transition: background-color 0.3s;
64
+ position: absolute;
53
65
  }
54
- `, {}).asReadonly();
55
- subsX;
56
- subsY;
57
- mutationObserverSubjectX = new Subject();
58
- mutationObserverSubjectY = new Subject();
59
- scrollbarWidth = computed(() => this.options()?.scrollbarWidth || 8); // Chiều rộng thanh cuộn
60
- scrollbarColor = computed(() => this.options()?.scrollbarColor || '#CDD0D6'); // Màu sắc thanh cuộn
61
- mutationObserverX = signal(undefined);
62
- mutationObserverY = signal(undefined);
66
+ `, {});
67
+ isScrollThumb = signal(false);
68
+ keepDisplayThumb = signal(false);
69
+ subsX = new Subscription();
70
+ subsY = new Subscription();
71
+ scrollbarWidth = computed(() => this.options()?.scrollbarWidth ?? 10); // Chiều rộng thanh cuộn
72
+ scrollbarPadding = computed(() => this.options()?.scrollbarPadding ?? 2); // Chiều rộng thanh cuộn
73
+ scrollbarColor = computed(() => this.options()?.scrollbarColor ?? '');
74
+ scrollbarHoverColor = computed(() => this.options()?.scrollbarColor ?? '#CDD0D640');
75
+ scrollThumbColor = computed(() => this.options()?.scrollThumbColor ?? '#CDD0D6');
76
+ scrollThumbHoverColor = computed(() => this.options()?.scrollThumbHoverColor ?? '#9CA2AD');
63
77
  divContainer = document.createElement('div');
64
78
  trackX = document.createElement('div');
65
79
  thumbX = document.createElement('div');
66
80
  trackY = document.createElement('div');
67
81
  thumbY = document.createElement('div');
68
82
  onDestroy = new Subject();
69
- /* INPUT */
83
+ // #region INPUT
84
+ debugMode = input(false);
85
+ ignoreInit = input(false);
70
86
  classContainer = input('', { transform: value => value ?? '' });
71
87
  options = input(Object.assign({}));
72
- /* OUTPUT */
88
+ elementCheckScrollX = input();
89
+ elementCheckScrollY = input();
90
+ elementScroll = input();
91
+ // #region OUTPUT
92
+ outScroll = output();
73
93
  outScrollX = output();
74
94
  outScrollY = output();
75
95
  outScrollTop = output();
76
96
  outScrollBottom = output();
77
- /* INJECT */
97
+ // #region INJECT
78
98
  element = inject(ElementRef);
79
99
  render2 = inject(Renderer2);
80
100
  constructor() {
81
101
  effect(() => {
102
+ if (this.ignoreInit()) {
103
+ return;
104
+ }
82
105
  const options = this.options();
83
106
  this.divContainer.className = '';
84
107
  this.classContainer()?.split(' ').forEach(className => {
@@ -88,30 +111,33 @@ class LibsUiComponentsScrollOverlayDirective {
88
111
  this.divContainer.classList.add(className);
89
112
  });
90
113
  untracked(() => {
114
+ this.Element.classList.toggle('overflow-hidden', options?.scrollX === 'hidden' && options?.scrollY === 'hidden');
91
115
  if (options?.scrollX !== 'hidden') {
92
- this.subsX?.unsubscribe();
116
+ this.subsX.unsubscribe();
117
+ this.trackX.className = '';
118
+ this.thumbX.className = '';
119
+ this.trackX.style.height = `${this.scrollbarWidth()}px`;
93
120
  this.createScrollbar('X', this.trackX, this.thumbX);
94
121
  this.bindEventsScrollBar('X', this.trackX);
95
- this.mutationObserverY()?.disconnect();
96
- this.subsX?.add(this.mutationObserverSubjectX.pipe(debounceTime(250)).subscribe(this.updateScrollbarSize.bind(this, 'X')));
97
- this.mutationObserverX.set(new MutationObserver(() => this.mutationObserverSubjectX.next()));
98
- this.mutationObserverX()?.observe(this.Element, { attributes: true, childList: true, subtree: true });
122
+ this.handlerDragAndDropThumb('X');
123
+ this.handlerClickTrack('X');
99
124
  }
100
125
  if (options?.scrollY !== 'hidden') {
101
- this.subsY?.unsubscribe();
126
+ this.trackY.className = '';
127
+ this.thumbY.className = '';
128
+ this.trackY.style.width = `${this.scrollbarWidth()}px`;
129
+ this.subsY.unsubscribe();
102
130
  this.createScrollbar('Y', this.trackY, this.thumbY);
103
131
  this.bindEventsScrollBar('Y', this.trackY);
104
- this.mutationObserverY()?.disconnect();
105
- this.subsY?.add(this.mutationObserverSubjectY.pipe(debounceTime(250)).subscribe(this.updateScrollbarSize.bind(this, 'Y')));
106
- this.mutationObserverY.set(new MutationObserver(() => this.mutationObserverSubjectY.next()));
107
- this.mutationObserverY()?.observe(this.Element, { attributes: true, childList: true, subtree: true });
132
+ this.handlerDragAndDropThumb('Y');
133
+ this.handlerClickTrack('Y');
108
134
  }
109
135
  });
110
136
  });
111
137
  }
112
- /* FUNCTIONS*/
138
+ // #region FUNCTIONS
113
139
  get Element() {
114
- return this.element.nativeElement;
140
+ return this.elementScroll() || this.element.nativeElement;
115
141
  }
116
142
  createScrollbar(scrollDirection, trackEl, thumbEl) {
117
143
  const idStyleTag = "#id-style-tag-custom-scroll-overlay";
@@ -133,28 +159,27 @@ class LibsUiComponentsScrollOverlayDirective {
133
159
  Object.keys(stylesProperty).forEach(key => {
134
160
  this.render2.setStyle(this.Element, key, stylesProperty[key], 1);
135
161
  });
162
+ trackEl.classList.add(`scrollbar-track`);
136
163
  trackEl.classList.add(`scrollbar-track-${scrollDirection}`);
137
- if (scrollDirection === 'X') {
138
- trackEl.style.width = `100%`;
139
- trackEl.style.height = `${this.scrollbarWidth()}px`;
140
- }
141
- if (scrollDirection === 'Y') {
142
- trackEl.style.height = `100%`;
143
- trackEl.style.width = `${this.scrollbarWidth()}px`;
144
- }
164
+ thumbEl.classList.add(`scrollbar-thumb`);
145
165
  thumbEl.classList.add(`scrollbar-thumb-${scrollDirection}`);
146
- thumbEl.style.backgroundColor = this.scrollbarColor();
147
166
  trackEl.appendChild(thumbEl);
148
167
  if (this.Element.className) {
149
168
  this.Element.className.split(' ').forEach((className) => {
150
- if (className && (['w-full', 'w-screen', 'h-full', 'h-screen'].includes(className) || /^(h|w)-\[[0-9]+px\]$/.test(className)) && !this.divContainer.classList.contains(className)) {
169
+ if (className && (['w-full', 'w-screen', 'h-full', 'h-screen', 'shrink-0'].includes(className) || className.includes('min-h-') || className.includes('min-w-') || /^(!?)(h|w)-\[[0-9]+px\]$/.test(className)) && !this.divContainer.classList.contains(className)) {
151
170
  this.divContainer.classList.add(className);
152
171
  }
153
172
  });
173
+ if (!this.Element.className.includes('min-h-')) {
174
+ this.divContainer.classList.add('min-h-0');
175
+ }
176
+ if (!this.Element.className.includes('min-w-')) {
177
+ this.divContainer.classList.add('min-w-0');
178
+ }
154
179
  }
155
180
  this.divContainer.appendChild(trackEl);
156
181
  if (!this.divContainer.style.position) {
157
- this.Element.parentElement.insertBefore(this.divContainer, this.Element);
182
+ this.Element.parentElement?.insertBefore(this.divContainer, this.Element);
158
183
  this.divContainer.style.position = 'relative';
159
184
  }
160
185
  this.divContainer.append(this.Element);
@@ -165,6 +190,7 @@ class LibsUiComponentsScrollOverlayDirective {
165
190
  let scrollTop = this.Element.scrollTop;
166
191
  const subs = fromEvent(this.Element, 'scroll').pipe(tap((event) => {
167
192
  const target = this.Element;
193
+ this.outScroll.emit(event);
168
194
  if (scrollDirection === 'X') {
169
195
  if (target.scrollLeft && (target.scrollLeft + target.offsetWidth >= target.scrollWidth)) {
170
196
  target.scrollLeft = target.scrollWidth - target.offsetWidth - (target.offsetWidth - target.clientWidth);
@@ -189,13 +215,20 @@ class LibsUiComponentsScrollOverlayDirective {
189
215
  return this.outScrollBottom.emit(event);
190
216
  }
191
217
  }), takeUntil(this.onDestroy)).subscribe();
192
- subs.add(fromEvent(this.Element, 'resize').pipe(tap(this.updateScrollbarSize.bind(this, scrollDirection)), takeUntil(this.onDestroy)).subscribe());
193
- subs.add(fromEvent(this.Element, 'mouseenter').pipe(tap(() => {
194
- trackEl.style.visibility = 'visible';
195
- trackEl.style.opacity = '1';
218
+ subs.add(fromEvent(document, 'resize').pipe(tap(this.updateScrollbarSize.bind(this, scrollDirection)), takeUntil(this.onDestroy)).subscribe());
219
+ const mouseLeave = fromEvent(this.divContainer, 'mouseleave');
220
+ const mouseenter = fromEvent(this.divContainer, 'mouseenter');
221
+ subs.add(mouseenter.pipe(tap(() => {
222
+ if (scrollDirection === 'X' && !this.options()?.scrollXOpacity0 || scrollDirection === 'Y' && !this.options()?.scrollYOpacity0) {
223
+ trackEl.style.visibility = 'visible';
224
+ trackEl.style.opacity = '1';
225
+ }
196
226
  this.updateScrollbarSize(scrollDirection);
197
- }), takeUntil(this.onDestroy)).subscribe());
198
- subs.add(fromEvent(this.Element, 'mouseleave').pipe(tap(() => {
227
+ }), switchMap(() => interval(1000).pipe(takeUntil(mouseLeave))), tap(this.updateScrollbarSize.bind(this, scrollDirection)), takeUntil(this.onDestroy)).subscribe());
228
+ subs.add(mouseLeave.pipe(tap(() => {
229
+ if (this.keepDisplayThumb()) {
230
+ return;
231
+ }
199
232
  trackEl.style.visibility = 'hidden';
200
233
  trackEl.style.opacity = '0';
201
234
  }), takeUntil(this.onDestroy)).subscribe());
@@ -208,12 +241,79 @@ class LibsUiComponentsScrollOverlayDirective {
208
241
  return;
209
242
  }
210
243
  }
244
+ handlerClickTrack(scrollDirection) {
245
+ const elementTrack = scrollDirection === 'X' ? this.trackX : this.trackY;
246
+ const elementThumb = scrollDirection === 'X' ? this.thumbX : this.thumbY;
247
+ const subs = scrollDirection === 'X' ? this.subsX : this.subsY;
248
+ subs.add(fromEvent(elementTrack, 'click').subscribe(e => {
249
+ if (this.isScrollThumb()) {
250
+ return;
251
+ }
252
+ if ((scrollDirection === 'X' && e.clientX < elementThumb.getBoundingClientRect().left) || (scrollDirection === 'Y' && e.clientY < elementThumb.getBoundingClientRect().top)) {
253
+ this.updateScrollPositionByUserAction(scrollDirection, e, 'smooth', 0);
254
+ return;
255
+ }
256
+ if (scrollDirection === 'X') {
257
+ this.updateScrollPositionByUserAction(scrollDirection, e, 'smooth', -1 * elementThumb.getBoundingClientRect().width);
258
+ return;
259
+ }
260
+ this.updateScrollPositionByUserAction(scrollDirection, e, 'smooth', -1 * elementThumb.getBoundingClientRect().height);
261
+ }));
262
+ }
263
+ handlerDragAndDropThumb(scrollDirection) {
264
+ const elementTrack = scrollDirection === 'X' ? this.trackX : this.trackY;
265
+ const elementThumb = scrollDirection === 'X' ? this.thumbX : this.thumbY;
266
+ const subs = scrollDirection === 'X' ? this.subsX : this.subsY;
267
+ let lengthThumbToPointClick = 0;
268
+ subs.add(getDragEventByElement({
269
+ elementMouseDown: elementThumb,
270
+ functionMouseDown: (mouseEvent) => {
271
+ this.isScrollThumb.set(true);
272
+ this.keepDisplayThumb.set(true);
273
+ if (scrollDirection === 'X') {
274
+ lengthThumbToPointClick = elementThumb.getBoundingClientRect().left - mouseEvent.clientX;
275
+ return;
276
+ }
277
+ lengthThumbToPointClick = elementThumb.getBoundingClientRect().top - mouseEvent.clientY;
278
+ },
279
+ functionMouseUp: (mouseEvent) => {
280
+ this.keepDisplayThumb.set(false);
281
+ lengthThumbToPointClick = 0;
282
+ if (!checkMouseOverInContainer(mouseEvent, this.Element)) {
283
+ elementTrack.style.visibility = 'hidden';
284
+ elementTrack.style.opacity = '0';
285
+ }
286
+ setTimeout(() => {
287
+ this.isScrollThumb.set(false);
288
+ }, 250);
289
+ },
290
+ onDestroy: this.onDestroy
291
+ }).subscribe((mouseEvent) => {
292
+ this.updateScrollPositionByUserAction(scrollDirection, mouseEvent, 'auto', lengthThumbToPointClick);
293
+ }));
294
+ }
295
+ updateScrollPositionByUserAction(scrollDirection, e, behavior, lengthThumbToPointClick = 0) {
296
+ e.stopPropagation();
297
+ if (scrollDirection === 'X') {
298
+ const containerWidth = this.Element.offsetWidth;
299
+ const contentWidth = (this.elementCheckScrollX() || this.Element).scrollWidth;
300
+ const thumbPosition = e.clientX - this.Element.getBoundingClientRect().left + lengthThumbToPointClick;
301
+ const scrollLeft = (thumbPosition / (containerWidth - this.thumbX.offsetWidth)) * (contentWidth - containerWidth);
302
+ this.Element.scroll({ left: scrollLeft, behavior });
303
+ return;
304
+ }
305
+ const containerHeight = this.Element.offsetHeight;
306
+ const contentHeight = (this.elementCheckScrollY() || this.Element).scrollHeight;
307
+ const thumbPosition = e.clientY - this.Element.getBoundingClientRect().top + lengthThumbToPointClick;
308
+ const scrollTop = (thumbPosition / (containerHeight - this.thumbY.offsetHeight)) * (contentHeight - containerHeight);
309
+ this.Element.scroll({ top: scrollTop, behavior });
310
+ }
211
311
  updateScrollbarSize(scrollDirection) {
212
312
  if (scrollDirection === 'X') {
213
313
  const containerWidth = this.Element.offsetWidth;
214
- const contentWidth = this.Element.scrollWidth;
314
+ const contentWidth = (this.elementCheckScrollX() || this.Element).scrollWidth;
215
315
  const thumbWidth = (containerWidth / contentWidth) * (containerWidth);
216
- this.thumbX.style.width = `${thumbWidth}px`;
316
+ this.thumbX.style.width = `${Math.max(20, thumbWidth)}px`;
217
317
  this.trackX.style.display = 'none';
218
318
  if (contentWidth > containerWidth) {
219
319
  this.trackX.style.display = 'block';
@@ -222,9 +322,9 @@ class LibsUiComponentsScrollOverlayDirective {
222
322
  return;
223
323
  }
224
324
  const containerHeight = this.Element.offsetHeight;
225
- const contentHeight = this.Element.scrollHeight;
325
+ const contentHeight = (this.elementCheckScrollY() || this.Element).scrollHeight;
226
326
  const thumbHeight = (containerHeight / contentHeight) * (containerHeight);
227
- this.thumbY.style.height = `${thumbHeight}px`;
327
+ this.thumbY.style.height = `${Math.max(20, thumbHeight)}px`;
228
328
  this.trackY.style.display = 'none';
229
329
  if (contentHeight > containerHeight) {
230
330
  this.trackY.style.display = 'block';
@@ -234,26 +334,27 @@ class LibsUiComponentsScrollOverlayDirective {
234
334
  updateScrollbarPosition(scrollDirection) {
235
335
  if (scrollDirection === 'X') {
236
336
  const containerWidth = this.Element.offsetWidth;
237
- const contentWidth = this.Element.scrollWidth;
337
+ const contentWidth = (this.elementCheckScrollX() || this.Element).scrollWidth;
238
338
  const scrollLeft = this.Element.scrollLeft;
239
339
  const thumbPosition = (scrollLeft / (contentWidth - containerWidth)) * (containerWidth - this.thumbX.offsetWidth);
240
340
  this.thumbX.style.left = `${thumbPosition}px`;
241
341
  return;
242
342
  }
243
343
  const containerHeight = this.Element.offsetHeight;
244
- const contentHeight = this.Element.scrollHeight;
344
+ const contentHeight = (this.elementCheckScrollY() || this.Element).scrollHeight;
245
345
  const scrollTop = this.Element.scrollTop;
246
346
  const thumbPosition = (scrollTop / (contentHeight - containerHeight)) * (containerHeight - this.thumbY.offsetHeight);
247
347
  this.thumbY.style.top = `${thumbPosition}px`;
248
348
  }
249
349
  ngOnDestroy() {
250
- this.mutationObserverX()?.disconnect();
251
- this.mutationObserverY()?.disconnect();
350
+ this.divContainer.remove();
351
+ this.subsX.unsubscribe();
352
+ this.subsY.unsubscribe();
252
353
  this.onDestroy.next();
253
354
  this.onDestroy.complete();
254
355
  }
255
356
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsScrollOverlayDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
256
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.13", type: LibsUiComponentsScrollOverlayDirective, isStandalone: true, selector: "[LibsUiComponentsScrollOverlayDirective]", inputs: { classContainer: { classPropertyName: "classContainer", publicName: "classContainer", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { outScrollX: "outScrollX", outScrollY: "outScrollY", outScrollTop: "outScrollTop", outScrollBottom: "outScrollBottom" }, ngImport: i0 });
357
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.13", type: LibsUiComponentsScrollOverlayDirective, isStandalone: true, selector: "[LibsUiComponentsScrollOverlayDirective]", inputs: { debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null }, ignoreInit: { classPropertyName: "ignoreInit", publicName: "ignoreInit", isSignal: true, isRequired: false, transformFunction: null }, classContainer: { classPropertyName: "classContainer", publicName: "classContainer", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, elementCheckScrollX: { classPropertyName: "elementCheckScrollX", publicName: "elementCheckScrollX", isSignal: true, isRequired: false, transformFunction: null }, elementCheckScrollY: { classPropertyName: "elementCheckScrollY", publicName: "elementCheckScrollY", isSignal: true, isRequired: false, transformFunction: null }, elementScroll: { classPropertyName: "elementScroll", publicName: "elementScroll", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { outScroll: "outScroll", outScrollX: "outScrollX", outScrollY: "outScrollY", outScrollTop: "outScrollTop", outScrollBottom: "outScrollBottom" }, ngImport: i0 });
257
358
  }
258
359
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsScrollOverlayDirective, decorators: [{
259
360
  type: Directive,
@@ -266,9 +367,82 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
266
367
 
267
368
  ;
268
369
 
370
+ class LibsUiComponentsScrollOverlayDemoComponent {
371
+ // Demo text content: generate 5000 words for vertical scrolling
372
+ longContent = signal(Array.from({ length: 5000 }, (_, i) => `Lorem${i + 1}`).join(' '));
373
+ // Demo horizontal content: generate 5000 words for horizontal scrolling
374
+ longHorizontalContent = signal(Array.from({ length: 5000 }, (_, i) => `Word${i + 1}`).join(' '));
375
+ // Scenario selection
376
+ scenarioOptions = ['default', 'customStyle', 'autoHide', 'horizontal'];
377
+ selectedScenario = 'default';
378
+ // Options signals
379
+ customStyleOptions = signal({ scrollbarWidth: 12, scrollbarColor: '#f3f4f6', scrollbarHoverColor: '#e5e7eb', scrollThumbColor: '#3b82f6', scrollThumbHoverColor: '#2563eb', scrollbarPadding: 4 });
380
+ autoHideOptions = signal({ scrollYOpacity0: true, scrollbarWidth: 8, scrollbarColor: 'transparent', scrollThumbColor: '#9ca3af', scrollThumbHoverColor: '#6b7280' });
381
+ horizontalOptions = signal({ scrollX: 'scroll', scrollY: 'hidden', scrollbarWidth: 8, scrollbarColor: '#f3f4f6', scrollThumbColor: '#3b82f6' });
382
+ // Scroll event log
383
+ lastEvent = signal('No events yet');
384
+ // Helpers
385
+ get options() {
386
+ switch (this.selectedScenario) {
387
+ case 'customStyle': return this.customStyleOptions();
388
+ case 'autoHide': return this.autoHideOptions();
389
+ case 'horizontal': return this.horizontalOptions();
390
+ default: return undefined;
391
+ }
392
+ }
393
+ get content() {
394
+ return this.selectedScenario === 'horizontal'
395
+ ? this.longHorizontalContent()
396
+ : this.longContent();
397
+ }
398
+ // Event handlers
399
+ onScroll() { this.lastEvent.set('Scroll event fired'); }
400
+ onScrollTop() { this.lastEvent.set('Reached top'); }
401
+ onScrollBottom() { this.lastEvent.set('Reached bottom'); }
402
+ // API docs
403
+ inputsDoc = [
404
+ { name: 'options', type: 'IScrollOverlayOptions', default: 'undefined', description: 'Cấu hình tuỳ chỉnh scrollbar' },
405
+ { name: 'debugMode', type: 'boolean', default: 'false', description: 'Bật chế độ debug' },
406
+ { name: 'notShowScrollBarX', type: 'boolean', default: 'false', description: 'Ẩn scrollbar ngang' },
407
+ { name: 'notShowScrollBarY', type: 'boolean', default: 'false', description: 'Ẩn scrollbar dọc' }
408
+ ];
409
+ outputsDoc = [
410
+ { name: 'outScroll', type: 'Event', description: 'Bắn ra khi scroll bất kỳ' },
411
+ { name: 'outScrollTop', type: 'Event', description: 'Bắn ra khi scroll đến top' },
412
+ { name: 'outScrollBottom', type: 'Event', description: 'Bắn ra khi scroll đến bottom' }
413
+ ];
414
+ optionsDoc = [
415
+ { name: 'scrollbarWidth', type: 'number', default: 'undefined', description: 'Chiều rộng scrollbar (px)' },
416
+ { name: 'scrollbarColor', type: 'string', default: 'undefined', description: 'Màu track scrollbar' },
417
+ { name: 'scrollbarHoverColor', type: 'string', default: 'undefined', description: 'Màu track khi hover' },
418
+ { name: 'scrollThumbColor', type: 'string', default: 'undefined', description: 'Màu thumb' },
419
+ { name: 'scrollThumbHoverColor', type: 'string', default: 'undefined', description: 'Màu thumb khi hover' },
420
+ { name: 'scrollbarPadding', type: 'number', default: 'undefined', description: 'Padding scrollbar' },
421
+ { name: 'scrollX', type: `'hidden' | 'scroll'`, default: 'undefined', description: 'Kiểu scroll X' },
422
+ { name: 'scrollXOpacity0', type: 'boolean', default: 'undefined', description: 'Ẩn track X khi không hover' },
423
+ { name: 'scrollY', type: `'hidden' | 'scroll'`, default: 'undefined', description: 'Kiểu scroll Y' },
424
+ { name: 'scrollYOpacity0', type: 'boolean', default: 'undefined', description: 'Ẩn track Y khi không hover' }
425
+ ];
426
+ interfacesDoc = [
427
+ { name: 'IScrollOverlayOptions', type: 'interface', description: 'Các tuỳ chọn cấu hình scroll-overlay' }
428
+ ];
429
+ // Installation commands
430
+ installCommandNpm = 'npm install @libs-ui/components-scroll-overlay';
431
+ installCommandYarn = 'yarn add @libs-ui/components-scroll-overlay';
432
+ copyToClipboard(text) {
433
+ navigator.clipboard.writeText(text).then(() => console.log('Copied:', text));
434
+ }
435
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsScrollOverlayDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
436
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: LibsUiComponentsScrollOverlayDemoComponent, isStandalone: true, selector: "lib-scroll-overlay-demo", ngImport: i0, template: "<div class=\"max-w-6xl mx-auto p-5 font-sans text-gray-800\">\n <header class=\"text-center py-10 bg-white rounded-lg mb-8 shadow-sm\">\n <h1 class=\"text-4xl font-bold mb-2\">Demo Scroll Overlay</h1>\n <p class=\"text-xl text-gray-500\">&#64;libs-ui/components-scroll-overlay</p>\n </header>\n\n <main>\n <!-- Gi\u1EDBi thi\u1EC7u -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">Gi\u1EDBi thi\u1EC7u</h2>\n <p><code>scroll-overlay</code> l\u00E0 m\u1ED9t directive gi\u00FAp tu\u1EF3 bi\u1EBFn scrollbar tr\u00EAn b\u1EA5t k\u1EF3 ph\u1EA7n t\u1EED n\u00E0o trong Angular, h\u1ED7 tr\u1EE3 tu\u1EF3 ch\u1EC9nh m\u00E0u, k\u00EDch th\u01B0\u1EDBc, \u1EA9n/hi\u1EC7n v\u00E0 s\u1EF1 ki\u1EC7n scroll.</p>\n </section>\n\n <!-- C\u00E0i \u0111\u1EB7t -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">C\u00E0i \u0111\u1EB7t</h2>\n <div class=\"flex items-center bg-gray-100 p-4 rounded-lg mb-4\">\n <pre class=\"flex-1 text-sm overflow-x-auto\"><code>{{ installCommandNpm }}</code></pre>\n <button class=\"ml-4 px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600\" (click)=\"copyToClipboard(installCommandNpm)\">Sao ch\u00E9p</button>\n </div>\n <div class=\"flex items-center bg-gray-100 p-4 rounded-lg\">\n <pre class=\"flex-1 text-sm overflow-x-auto\"><code>{{ installCommandYarn }}</code></pre>\n <button class=\"ml-4 px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600\" (click)=\"copyToClipboard(installCommandYarn)\">Sao ch\u00E9p</button>\n </div>\n </section>\n\n <!-- Demo Tr\u1EF1c ti\u1EBFp -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">Demo Tr\u1EF1c ti\u1EBFp</h2>\n <div class=\"grid grid-cols-4 gap-4 mb-6\">\n <button *ngFor=\"let sc of scenarioOptions\"\n (click)=\"selectedScenario = sc\"\n class=\"px-3 py-1 border rounded\"\n [class.bg-blue-500]=\"selectedScenario===sc\"\n [class.text-white]=\"selectedScenario===sc\">\n {{ sc }}\n </button>\n </div>\n <div class=\"p-4 bg-gray-50 rounded-lg h-[300px]\">\n <div LibsUiComponentsScrollOverlayDirective\n [options]=\"options\"\n (outScroll)=\"onScroll()\"\n (outScrollTop)=\"onScrollTop()\"\n (outScrollBottom)=\"onScrollBottom()\"\n class=\"w-full h-full\">\n <div class=\"p-2\"\n [innerText]=\"content\"\n [class.whitespace-pre-wrap]=\"selectedScenario!=='horizontal'\"\n [class.whitespace-nowrap]=\"selectedScenario==='horizontal'\"></div>\n </div>\n </div>\n <p class=\"mt-4 text-gray-700\">S\u1EF1 ki\u1EC7n: {{ lastEvent() }}</p>\n </section>\n\n <!-- C\u00E1ch s\u1EED d\u1EE5ng -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">C\u00E1ch s\u1EED d\u1EE5ng</h2>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-auto text-sm\">\n <code ngNonBindable>&lt;div LibsUiComponentsScrollOverlayDirective [options]=\"&#123; scrollbarWidth:12, scrollThumbColor:'#3b82f6' &#125;\" style=\"width:300px;height:200px;overflow:auto;\"&gt;\n N\u1ED9i dung d\u00E0i...\n&lt;/div&gt;</code>\n </pre>\n </section>\n\n <!-- API Reference -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">API Reference</h2>\n <h3 class=\"text-xl font-semibold mb-3\">Inputs</h3>\n <table class=\"min-w-full bg-white border border-gray-200 mb-6\">\n <thead>\n <tr>\n <th class=\"py-2 px-4 border-b bg-gray-100\">T\u00EAn</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">Ki\u1EC3u</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u1EB7c \u0111\u1ECBnh</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let input of inputsDoc\">\n <td class=\"py-2 px-4 border-b\"><code>{{ input.name }}</code></td>\n <td class=\"py-2 px-4 border-b\"><code>{{ input.type }}</code></td>\n <td class=\"py-2 px-4 border-b\">{{ input.default }}</td>\n <td class=\"py-2 px-4 border-b\">{{ input.description }}</td>\n </tr>\n </tbody>\n </table>\n <h3 class=\"text-xl font-semibold mb-3\">Outputs</h3>\n <table class=\"min-w-full bg-white border border-gray-200 mb-6\">\n <thead>\n <tr>\n <th class=\"py-2 px-4 border-b bg-gray-100\">T\u00EAn</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">Ki\u1EC3u</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let output of outputsDoc\">\n <td class=\"py-2 px-4 border-b\"><code>{{ output.name }}</code></td>\n <td class=\"py-2 px-4 border-b\"><code>{{ output.type }}</code></td>\n <td class=\"py-2 px-4 border-b\">{{ output.description }}</td>\n </tr>\n </tbody>\n </table>\n <h3 class=\"text-xl font-semibold mb-3\">Options</h3>\n <table class=\"min-w-full bg-white border border-gray-200 mb-6\">\n <thead>\n <tr>\n <th class=\"py-2 px-4 border-b bg-gray-100\">Thu\u1ED9c t\u00EDnh</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">Ki\u1EC3u</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u1EB7c \u0111\u1ECBnh</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let opt of optionsDoc\">\n <td class=\"py-2 px-4 border-b\"><code>{{ opt.name }}</code></td>\n <td class=\"py-2 px-4 border-b\"><code>{{ opt.type }}</code></td>\n <td class=\"py-2 px-4 border-b\">{{ opt.default }}</td>\n <td class=\"py-2 px-4 border-b\">{{ opt.description }}</td>\n </tr>\n </tbody>\n </table>\n <h3 class=\"text-xl font-semibold mb-3\">Interfaces</h3>\n <div class=\"space-y-6\">\n <div *ngFor=\"let intf of interfacesDoc\" class=\"bg-gray-50 p-6 rounded-lg\">\n <h4 class=\"font-semibold mb-2\">{{ intf.name }}</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-auto text-sm mb-3\"><code>{{ intf.type }}</code></pre>\n <p>{{ intf.description }}</p>\n </div>\n </div>\n </section>\n </main>\n</div>\n", styles: ["@charset \"UTF-8\";.demo-container{padding:24px;font-family:system-ui,-apple-system,sans-serif;max-width:1200px;margin:0 auto}.demo-intro{margin-bottom:32px}.demo-intro h3{color:#333;margin-bottom:16px}.demo-intro ul{list-style:none;padding:0}.demo-intro ul li{margin-bottom:8px;padding-left:20px;position:relative}.demo-intro ul li:before{content:\"\\2022\";position:absolute;left:0;color:#3b82f6}.demo-features{margin-bottom:32px}.demo-features h3{color:#333;margin-bottom:16px}.features-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:16px}.feature-item{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0}.feature-item h4{color:#1e40af;margin:0 0 8px}.feature-item p{margin:0;color:#64748b}.demo-api{margin-bottom:48px}.demo-api h3{color:#333;margin-bottom:24px}.api-section{margin-bottom:32px}.api-section h4{color:#1e40af;margin-bottom:16px}.api-table{overflow-x:auto}.api-table table{width:100%;border-collapse:collapse;background:#fff;border-radius:8px;border:1px solid #e2e8f0}.api-table table th,.api-table table td{padding:12px 16px;text-align:left;border-bottom:1px solid #e2e8f0}.api-table table th{background:#f8fafc;font-weight:600;color:#1e40af}.api-table table td{color:#64748b}.api-table table td:first-child{font-family:Fira Code,monospace;color:#334155}.api-table table td:nth-child(2){font-family:Fira Code,monospace;color:#3b82f6}.api-table table tr:last-child td{border-bottom:none}.demo-section{margin-bottom:48px}.demo-section h3{color:#333;margin-bottom:16px}.demo-description{margin-bottom:16px}.demo-description p{color:#64748b;margin-bottom:12px}.demo-description pre{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0;overflow-x:auto}.demo-description pre code{font-family:Fira Code,monospace;font-size:14px;color:#334155}.demo-box{display:flex;gap:16px;align-items:flex-start}.event-log{padding:16px;background:#f5f5f5;border-radius:4px;min-width:200px}.event-log p{margin:0;font-size:14px;color:#666}.demo-notes{margin-top:48px;padding:24px;background:#f8fafc;border-radius:8px;border:1px solid #e2e8f0}.demo-notes h3{color:#333;margin-bottom:16px}.demo-notes ul{list-style:none;padding:0;margin:0}.demo-notes ul li{color:#64748b;margin-bottom:12px;padding-left:24px;position:relative}.demo-notes ul li:before{content:\"\\2192\";position:absolute;left:0;color:#3b82f6}.demo-notes ul li:last-child{margin-bottom:0}.interface-description{margin-bottom:24px}.interface-description p{color:#64748b;margin-bottom:16px}.interface-description pre{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0;overflow-x:auto}.interface-description pre code{font-family:Fira Code,monospace;font-size:14px;color:#334155;line-height:1.6}.interface-usage{margin-bottom:24px}.interface-usage h5{color:#1e40af;margin-bottom:12px;font-size:16px}.interface-usage pre{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0;overflow-x:auto}.interface-usage pre code{font-family:Fira Code,monospace;font-size:14px;color:#334155;line-height:1.6}.interface-usage pre code .comment{color:#64748b}.interface-notes{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0}.interface-notes h5{color:#1e40af;margin-bottom:12px;font-size:16px}.interface-notes ul{list-style:none;padding:0;margin:0}.interface-notes ul li{color:#64748b;margin-bottom:8px;padding-left:20px;position:relative}.interface-notes ul li:before{content:\"\\2022\";position:absolute;left:0;color:#3b82f6}.interface-notes ul li:last-child{margin-bottom:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: LibsUiComponentsScrollOverlayDirective, selector: "[LibsUiComponentsScrollOverlayDirective]", inputs: ["debugMode", "ignoreInit", "classContainer", "options", "elementCheckScrollX", "elementCheckScrollY", "elementScroll"], outputs: ["outScroll", "outScrollX", "outScrollY", "outScrollTop", "outScrollBottom"] }] });
437
+ }
438
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsScrollOverlayDemoComponent, decorators: [{
439
+ type: Component,
440
+ args: [{ selector: 'lib-scroll-overlay-demo', standalone: true, imports: [CommonModule, LibsUiComponentsScrollOverlayDirective], template: "<div class=\"max-w-6xl mx-auto p-5 font-sans text-gray-800\">\n <header class=\"text-center py-10 bg-white rounded-lg mb-8 shadow-sm\">\n <h1 class=\"text-4xl font-bold mb-2\">Demo Scroll Overlay</h1>\n <p class=\"text-xl text-gray-500\">&#64;libs-ui/components-scroll-overlay</p>\n </header>\n\n <main>\n <!-- Gi\u1EDBi thi\u1EC7u -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">Gi\u1EDBi thi\u1EC7u</h2>\n <p><code>scroll-overlay</code> l\u00E0 m\u1ED9t directive gi\u00FAp tu\u1EF3 bi\u1EBFn scrollbar tr\u00EAn b\u1EA5t k\u1EF3 ph\u1EA7n t\u1EED n\u00E0o trong Angular, h\u1ED7 tr\u1EE3 tu\u1EF3 ch\u1EC9nh m\u00E0u, k\u00EDch th\u01B0\u1EDBc, \u1EA9n/hi\u1EC7n v\u00E0 s\u1EF1 ki\u1EC7n scroll.</p>\n </section>\n\n <!-- C\u00E0i \u0111\u1EB7t -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">C\u00E0i \u0111\u1EB7t</h2>\n <div class=\"flex items-center bg-gray-100 p-4 rounded-lg mb-4\">\n <pre class=\"flex-1 text-sm overflow-x-auto\"><code>{{ installCommandNpm }}</code></pre>\n <button class=\"ml-4 px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600\" (click)=\"copyToClipboard(installCommandNpm)\">Sao ch\u00E9p</button>\n </div>\n <div class=\"flex items-center bg-gray-100 p-4 rounded-lg\">\n <pre class=\"flex-1 text-sm overflow-x-auto\"><code>{{ installCommandYarn }}</code></pre>\n <button class=\"ml-4 px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600\" (click)=\"copyToClipboard(installCommandYarn)\">Sao ch\u00E9p</button>\n </div>\n </section>\n\n <!-- Demo Tr\u1EF1c ti\u1EBFp -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">Demo Tr\u1EF1c ti\u1EBFp</h2>\n <div class=\"grid grid-cols-4 gap-4 mb-6\">\n <button *ngFor=\"let sc of scenarioOptions\"\n (click)=\"selectedScenario = sc\"\n class=\"px-3 py-1 border rounded\"\n [class.bg-blue-500]=\"selectedScenario===sc\"\n [class.text-white]=\"selectedScenario===sc\">\n {{ sc }}\n </button>\n </div>\n <div class=\"p-4 bg-gray-50 rounded-lg h-[300px]\">\n <div LibsUiComponentsScrollOverlayDirective\n [options]=\"options\"\n (outScroll)=\"onScroll()\"\n (outScrollTop)=\"onScrollTop()\"\n (outScrollBottom)=\"onScrollBottom()\"\n class=\"w-full h-full\">\n <div class=\"p-2\"\n [innerText]=\"content\"\n [class.whitespace-pre-wrap]=\"selectedScenario!=='horizontal'\"\n [class.whitespace-nowrap]=\"selectedScenario==='horizontal'\"></div>\n </div>\n </div>\n <p class=\"mt-4 text-gray-700\">S\u1EF1 ki\u1EC7n: {{ lastEvent() }}</p>\n </section>\n\n <!-- C\u00E1ch s\u1EED d\u1EE5ng -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">C\u00E1ch s\u1EED d\u1EE5ng</h2>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-auto text-sm\">\n <code ngNonBindable>&lt;div LibsUiComponentsScrollOverlayDirective [options]=\"&#123; scrollbarWidth:12, scrollThumbColor:'#3b82f6' &#125;\" style=\"width:300px;height:200px;overflow:auto;\"&gt;\n N\u1ED9i dung d\u00E0i...\n&lt;/div&gt;</code>\n </pre>\n </section>\n\n <!-- API Reference -->\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold mb-5 pb-3 border-b border-gray-200\">API Reference</h2>\n <h3 class=\"text-xl font-semibold mb-3\">Inputs</h3>\n <table class=\"min-w-full bg-white border border-gray-200 mb-6\">\n <thead>\n <tr>\n <th class=\"py-2 px-4 border-b bg-gray-100\">T\u00EAn</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">Ki\u1EC3u</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u1EB7c \u0111\u1ECBnh</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let input of inputsDoc\">\n <td class=\"py-2 px-4 border-b\"><code>{{ input.name }}</code></td>\n <td class=\"py-2 px-4 border-b\"><code>{{ input.type }}</code></td>\n <td class=\"py-2 px-4 border-b\">{{ input.default }}</td>\n <td class=\"py-2 px-4 border-b\">{{ input.description }}</td>\n </tr>\n </tbody>\n </table>\n <h3 class=\"text-xl font-semibold mb-3\">Outputs</h3>\n <table class=\"min-w-full bg-white border border-gray-200 mb-6\">\n <thead>\n <tr>\n <th class=\"py-2 px-4 border-b bg-gray-100\">T\u00EAn</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">Ki\u1EC3u</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let output of outputsDoc\">\n <td class=\"py-2 px-4 border-b\"><code>{{ output.name }}</code></td>\n <td class=\"py-2 px-4 border-b\"><code>{{ output.type }}</code></td>\n <td class=\"py-2 px-4 border-b\">{{ output.description }}</td>\n </tr>\n </tbody>\n </table>\n <h3 class=\"text-xl font-semibold mb-3\">Options</h3>\n <table class=\"min-w-full bg-white border border-gray-200 mb-6\">\n <thead>\n <tr>\n <th class=\"py-2 px-4 border-b bg-gray-100\">Thu\u1ED9c t\u00EDnh</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">Ki\u1EC3u</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u1EB7c \u0111\u1ECBnh</th>\n <th class=\"py-2 px-4 border-b bg-gray-100\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let opt of optionsDoc\">\n <td class=\"py-2 px-4 border-b\"><code>{{ opt.name }}</code></td>\n <td class=\"py-2 px-4 border-b\"><code>{{ opt.type }}</code></td>\n <td class=\"py-2 px-4 border-b\">{{ opt.default }}</td>\n <td class=\"py-2 px-4 border-b\">{{ opt.description }}</td>\n </tr>\n </tbody>\n </table>\n <h3 class=\"text-xl font-semibold mb-3\">Interfaces</h3>\n <div class=\"space-y-6\">\n <div *ngFor=\"let intf of interfacesDoc\" class=\"bg-gray-50 p-6 rounded-lg\">\n <h4 class=\"font-semibold mb-2\">{{ intf.name }}</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-auto text-sm mb-3\"><code>{{ intf.type }}</code></pre>\n <p>{{ intf.description }}</p>\n </div>\n </div>\n </section>\n </main>\n</div>\n", styles: ["@charset \"UTF-8\";.demo-container{padding:24px;font-family:system-ui,-apple-system,sans-serif;max-width:1200px;margin:0 auto}.demo-intro{margin-bottom:32px}.demo-intro h3{color:#333;margin-bottom:16px}.demo-intro ul{list-style:none;padding:0}.demo-intro ul li{margin-bottom:8px;padding-left:20px;position:relative}.demo-intro ul li:before{content:\"\\2022\";position:absolute;left:0;color:#3b82f6}.demo-features{margin-bottom:32px}.demo-features h3{color:#333;margin-bottom:16px}.features-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:16px}.feature-item{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0}.feature-item h4{color:#1e40af;margin:0 0 8px}.feature-item p{margin:0;color:#64748b}.demo-api{margin-bottom:48px}.demo-api h3{color:#333;margin-bottom:24px}.api-section{margin-bottom:32px}.api-section h4{color:#1e40af;margin-bottom:16px}.api-table{overflow-x:auto}.api-table table{width:100%;border-collapse:collapse;background:#fff;border-radius:8px;border:1px solid #e2e8f0}.api-table table th,.api-table table td{padding:12px 16px;text-align:left;border-bottom:1px solid #e2e8f0}.api-table table th{background:#f8fafc;font-weight:600;color:#1e40af}.api-table table td{color:#64748b}.api-table table td:first-child{font-family:Fira Code,monospace;color:#334155}.api-table table td:nth-child(2){font-family:Fira Code,monospace;color:#3b82f6}.api-table table tr:last-child td{border-bottom:none}.demo-section{margin-bottom:48px}.demo-section h3{color:#333;margin-bottom:16px}.demo-description{margin-bottom:16px}.demo-description p{color:#64748b;margin-bottom:12px}.demo-description pre{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0;overflow-x:auto}.demo-description pre code{font-family:Fira Code,monospace;font-size:14px;color:#334155}.demo-box{display:flex;gap:16px;align-items:flex-start}.event-log{padding:16px;background:#f5f5f5;border-radius:4px;min-width:200px}.event-log p{margin:0;font-size:14px;color:#666}.demo-notes{margin-top:48px;padding:24px;background:#f8fafc;border-radius:8px;border:1px solid #e2e8f0}.demo-notes h3{color:#333;margin-bottom:16px}.demo-notes ul{list-style:none;padding:0;margin:0}.demo-notes ul li{color:#64748b;margin-bottom:12px;padding-left:24px;position:relative}.demo-notes ul li:before{content:\"\\2192\";position:absolute;left:0;color:#3b82f6}.demo-notes ul li:last-child{margin-bottom:0}.interface-description{margin-bottom:24px}.interface-description p{color:#64748b;margin-bottom:16px}.interface-description pre{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0;overflow-x:auto}.interface-description pre code{font-family:Fira Code,monospace;font-size:14px;color:#334155;line-height:1.6}.interface-usage{margin-bottom:24px}.interface-usage h5{color:#1e40af;margin-bottom:12px;font-size:16px}.interface-usage pre{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0;overflow-x:auto}.interface-usage pre code{font-family:Fira Code,monospace;font-size:14px;color:#334155;line-height:1.6}.interface-usage pre code .comment{color:#64748b}.interface-notes{background:#f8fafc;padding:16px;border-radius:8px;border:1px solid #e2e8f0}.interface-notes h5{color:#1e40af;margin-bottom:12px;font-size:16px}.interface-notes ul{list-style:none;padding:0;margin:0}.interface-notes ul li{color:#64748b;margin-bottom:8px;padding-left:20px;position:relative}.interface-notes ul li:before{content:\"\\2022\";position:absolute;left:0;color:#3b82f6}.interface-notes ul li:last-child{margin-bottom:0}\n"] }]
441
+ }] });
442
+
269
443
  /**
270
444
  * Generated bundle index. Do not edit.
271
445
  */
272
446
 
273
- export { LibsUiComponentsScrollOverlayDirective };
447
+ export { LibsUiComponentsScrollOverlayDemoComponent, LibsUiComponentsScrollOverlayDirective };
274
448
  //# sourceMappingURL=libs-ui-components-scroll-overlay.mjs.map