From 2cabb0a3ed5d2c920c809d35f65a0b1ec5663ccc Mon Sep 17 00:00:00 2001 From: Artem Belik Date: Wed, 29 Jul 2026 18:07:16 +0300 Subject: [PATCH 01/13] feat(scrollbar): independent scrollbar implimentation (#DS-3176) --- packages/components-dev/scrollbar/module.ts | 146 +-- packages/components-dev/scrollbar/styles.scss | 29 +- .../components-dev/scrollbar/template.html | 60 +- .../private/__screenshots__/01-dark.png | Bin 0 -> 77129 bytes .../private/__screenshots__/01-light.png | Bin 0 -> 74388 bytes .../scrollbar/private/e2e.playwright-spec.ts | 17 + packages/components/scrollbar/private/e2e.ts | 59 ++ .../private/examples.scrollbar.en.md | 5 + .../private/examples.scrollbar.ru.md | 5 + .../components/scrollbar/private/index.ts | 1 + .../scrollbar/private/ng-package.json | 5 + .../scrollbar/private/public-api.ts | 2 + .../scrollbar/private/scrollbar.en.md | 5 + .../scrollbar/private/scrollbar.module.ts | 13 + .../scrollbar/private/scrollbar.ru.md | 11 + .../scrollbar/private/scrollbar.scss | 171 ++++ .../scrollbar/private/scrollbar.spec.ts | 866 +++++++++++++++++ .../components/scrollbar/private/scrollbar.ts | 886 ++++++++++++++++++ .../components/scrollbar/index.ts | 17 +- .../private-scrollbar-scroll-to-example.ts | 102 ++ ...rivate-scrollbar-virtual-scroll-example.ts | 54 ++ .../private-scrollbar-visibility-example.ts | 78 ++ packages/e2e/routes.ts | 4 +- tools/api-extractor/api-extractor.ts | 8 +- tools/api-extractor/config.json | 1 + tools/cspell-locales/ru.json | 3 + .../components/scrollbar-private.api.md | 125 +++ tsconfig.json | 1 + 28 files changed, 2465 insertions(+), 209 deletions(-) create mode 100644 packages/components/scrollbar/private/__screenshots__/01-dark.png create mode 100644 packages/components/scrollbar/private/__screenshots__/01-light.png create mode 100644 packages/components/scrollbar/private/e2e.playwright-spec.ts create mode 100644 packages/components/scrollbar/private/e2e.ts create mode 100644 packages/components/scrollbar/private/examples.scrollbar.en.md create mode 100644 packages/components/scrollbar/private/examples.scrollbar.ru.md create mode 100644 packages/components/scrollbar/private/index.ts create mode 100644 packages/components/scrollbar/private/ng-package.json create mode 100644 packages/components/scrollbar/private/public-api.ts create mode 100644 packages/components/scrollbar/private/scrollbar.en.md create mode 100644 packages/components/scrollbar/private/scrollbar.module.ts create mode 100644 packages/components/scrollbar/private/scrollbar.ru.md create mode 100644 packages/components/scrollbar/private/scrollbar.scss create mode 100644 packages/components/scrollbar/private/scrollbar.spec.ts create mode 100644 packages/components/scrollbar/private/scrollbar.ts create mode 100644 packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts create mode 100644 packages/docs-examples/components/scrollbar/private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example.ts create mode 100644 packages/docs-examples/components/scrollbar/private-scrollbar-visibility/private-scrollbar-visibility-example.ts create mode 100644 tools/public_api_guard/components/scrollbar-private.api.md diff --git a/packages/components-dev/scrollbar/module.ts b/packages/components-dev/scrollbar/module.ts index 1de4924ec9..ea6ada0f4c 100644 --- a/packages/components-dev/scrollbar/module.ts +++ b/packages/components-dev/scrollbar/module.ts @@ -1,105 +1,45 @@ import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; -import { KbqButtonModule } from '@koobiq/components/button'; import { - KBQ_SCROLLBAR_CONFIG, - KbqScrollbarEvents, - KbqScrollbarModule, - KbqScrollbarOptions -} from '@koobiq/components/scrollbar'; + PrivateScrollbarScrollToExample, + PrivateScrollbarVirtualScrollExample, + PrivateScrollbarVisibilityExample, + ScrollbarOverviewExample, + ScrollbarScrollToTopExample, + ScrollbarWithCustomConfigExample +} from 'packages/docs-examples/components/scrollbar'; import { DevThemeToggle } from '../theme-toggle'; @Component({ - selector: 'dev-scrollbar-with-options', - imports: [KbqScrollbarModule], - template: ` -

ScrollbarWithOptions:

- - @for (item of items; track item) { -
{{ item }}
-
- } -
- `, - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class DevScrollbarWithOptions { - readonly options: KbqScrollbarOptions = { - scrollbars: { - autoHide: 'never' - } - }; - readonly items = Array.from({ length: 1000 }).map((_, i) => `Item #${i}`); -} - -@Component({ - selector: 'dev-scrollbar-with-custom-config', - imports: [KbqScrollbarModule], - template: ` -

ScrollbarWithCustomConfig:

-
- @for (item of items; track item) { -
{{ item }}
-
- } -
- `, - providers: [ - { - provide: KBQ_SCROLLBAR_CONFIG, - useValue: { - scrollbars: { - autoHide: 'never' - } - } satisfies KbqScrollbarOptions - } - ], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class DevScrollbarWithCustomConfig { - readonly items = Array.from({ length: 1000 }).map((_, i) => `Item #${i}`); -} - -@Component({ - selector: 'dev-scrollbar-scroll-to-top', + selector: 'dev-examples', imports: [ - KbqScrollbarModule, - KbqButtonModule + PrivateScrollbarVisibilityExample, + PrivateScrollbarVirtualScrollExample, + PrivateScrollbarScrollToExample, + ScrollbarOverviewExample, + ScrollbarScrollToTopExample, + ScrollbarWithCustomConfigExample ], template: ` -

ScrollbarScrollToTop:

- - @for (item of items; track item) { -
{{ item }}
-
- } -
- + +
+ +
+ +
+ +
+ +
+ `, changeDetection: ChangeDetectionStrategy.OnPush }) -export class DevScrollbarScrollToTop { - readonly items = Array.from({ length: 1000 }).map((_, i) => `Item #${i}`); - - onScroll(event): void { - console.log('onScroll', event); - } -} +export class DevDocsExamples {} @Component({ selector: 'dev-app', imports: [ - KbqScrollbarModule, - KbqButtonModule, - // components - DevScrollbarWithOptions, - DevScrollbarWithCustomConfig, - DevScrollbarScrollToTop, + DevDocsExamples, DevThemeToggle ], templateUrl: './template.html', @@ -107,34 +47,4 @@ export class DevScrollbarScrollToTop { changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None }) -export class DevApp { - options: KbqScrollbarOptions; - events: KbqScrollbarEvents = { - initialized: (...args) => this.onInitialize(args) - }; - - onScroll([instance, args]) { - console.log('onScroll', instance, args); - } - - onInitialize($event) { - console.log($event); - } - - longText: string = `Vivamus suscipit tortor eget felis porttitor volutpat. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Pellentesque in ipsum id orci porta dapibus. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Donec sollicitudin molestie malesuada. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. - Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Nulla porttitor accumsan tincidunt. Nulla quis lorem ut libero malesuada feugiat. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Pellentesque in ipsum id orci porta dapibus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Donec rutrum congue leo eget malesuada. - Proin eget tortor risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Donec sollicitudin molestie malesuada. Pellentesque in ipsum id orci porta dapibus. Curabitur aliquet quam id dui posuere blandit. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed porttitor lectus nibh. - Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vivamus suscipit tortor eget felis porttitor volutpat. Nulla quis lorem ut libero malesuada feugiat. Curabitur aliquet quam id dui posuere blandit. Donec sollicitudin molestie malesuada. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Donec sollicitudin molestie malesuada. - Proin eget tortor risus. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Cras ultricies ligula sed magna dictum porta. Cras ultricies ligula sed magna dictum porta. Cras ultricies ligula sed magna dictum porta. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eget tortor risus. Proin eget tortor risus. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. - Vivamus suscipit tortor eget felis porttitor volutpat. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Pellentesque in ipsum id orci porta dapibus. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Donec sollicitudin molestie malesuada. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. - Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Nulla porttitor accumsan tincidunt. Nulla quis lorem ut libero malesuada feugiat. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Pellentesque in ipsum id orci porta dapibus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Donec rutrum congue leo eget malesuada. - Proin eget tortor risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Donec sollicitudin molestie malesuada. Pellentesque in ipsum id orci porta dapibus. Curabitur aliquet quam id dui posuere blandit. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed porttitor lectus nibh. - Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vivamus suscipit tortor eget felis porttitor volutpat. Nulla quis lorem ut libero malesuada feugiat. Curabitur aliquet quam id dui posuere blandit. Donec sollicitudin molestie malesuada. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Donec sollicitudin molestie malesuada. - Proin eget tortor risus. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Cras ultricies ligula sed magna dictum porta. Cras ultricies ligula sed magna dictum porta. Cras ultricies ligula sed magna dictum porta. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eget tortor risus. Proin eget tortor risus. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. - Vivamus suscipit tortor eget felis porttitor volutpat. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Pellentesque in ipsum id orci porta dapibus. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Donec sollicitudin molestie malesuada. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. - Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Nulla porttitor accumsan tincidunt. Nulla quis lorem ut libero malesuada feugiat. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Pellentesque in ipsum id orci porta dapibus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Donec rutrum congue leo eget malesuada. - Proin eget tortor risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Donec sollicitudin molestie malesuada. Pellentesque in ipsum id orci porta dapibus. Curabitur aliquet quam id dui posuere blandit. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed porttitor lectus nibh. - Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vivamus suscipit tortor eget felis porttitor volutpat. Nulla quis lorem ut libero malesuada feugiat. Curabitur aliquet quam id dui posuere blandit. Donec sollicitudin molestie malesuada. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Donec sollicitudin molestie malesuada. - Proin eget tortor risus. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Cras ultricies ligula sed magna dictum porta. Cras ultricies ligula sed magna dictum porta. Cras ultricies ligula sed magna dictum porta. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eget tortor risus. Proin eget tortor risus. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. - `; -} +export class DevApp {} diff --git a/packages/components-dev/scrollbar/styles.scss b/packages/components-dev/scrollbar/styles.scss index 75e6e33886..e7f3e5945c 100644 --- a/packages/components-dev/scrollbar/styles.scss +++ b/packages/components-dev/scrollbar/styles.scss @@ -1,29 +1,4 @@ -.dev-container { - padding: 24px; -} - -textarea { - margin: 10px; +dev-app { + max-width: 608px; display: block; } - -.dev-with-horizontal { - white-space: nowrap; - overflow-x: visible; -} - -.dev-with-buttons { - white-space: nowrap; - overflow: visible; -} - -.dev-nested-scroll { - width: 200px; - height: 200px; - overflow: auto; - margin: 10px; -} - -.dev-nested-scroll__content { - width: 800px; -} diff --git a/packages/components-dev/scrollbar/template.html b/packages/components-dev/scrollbar/template.html index 1236796d25..9eff7f844b 100644 --- a/packages/components-dev/scrollbar/template.html +++ b/packages/components-dev/scrollbar/template.html @@ -1,63 +1,5 @@ -
- -
- - -
- -
-
-
-

Simple Scrollbar Component

- -

Simple example

-
-
{{ longText }}
-
- -
-

Bidi support

-
-
{{ longText }}
-
-
-
- -
-

Scrollbar Via CSS

- -

Horizontal

- - -

Buttons & Resize corner

- - -

Nested scroll — class on wrapper

-
-
-
{{ longText }}
-
-
- -

Nested scroll — class on scroller (reference)

-
-
{{ longText }}
-
-
-
-
+ diff --git a/packages/components/scrollbar/private/__screenshots__/01-dark.png b/packages/components/scrollbar/private/__screenshots__/01-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..b6f51bf31e1f0bbcf69d6b032f2cbfef90dd3208 GIT binary patch literal 77129 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

W*Z^hpN26xw`oy&^+j|?yt z!K@zqnewH%`IgTE{B)xOfNPiMlt8$D3ioLhU}Y5&GBEQ-0ZZ}^vxYy#Z1o0?%elRSwaNd)&-gs3(-t^-stE= z5ePJZNz>YTAKV>=tP`W}22eLP?bJJK6VY$vfpB+@Yu>Cz#qQ*f(v-6`k=2xTr!^xwN z_bmvbBjn>vO-+rhI^?h8;o=GG1BNYV_rronIT2l1Jwusx!&=WI??5<^=KO<)Q&ZEp zJ<wzdcR`<{P#f>D0YVwSR< z5B+Ff7CsS>e=qgT!)N5NXe5wR@V=^Z^TQw5;8}Zd;lV|q_i|B2A_3$}{TfH|+JgA_ zsdU%hc89iUX+Z8pwth$kgz#h@A9)rynpqT;5MsbWow6%OWQ7*fy(pg-!d=y`+>-2Xz#iXQk zR-^5Yh_x&A-~ZlL@;_z2cu~^yqv+%7D#Oe%e?tGBjm=FLuqW+D!eK=c;OFb9P>`2j zXl=e3{VcO%^LsZo2ui_Zyz{B&5atL~J2E2TG=p7BRHDcXe{TJ5AYh%ou)bqFvEoNi z+@4VUXxj~v=wnf*c=qo8e!zjS)#;`j<||ed!~&)qE0#BHO-i4+SLpLgXvg2i84~c@ zHpvE?^bN*Au@FDB9ULA$>9j_Z)Tu&VE7aRu9f9p=Ag2oZnt8DV2KwMkL}Y|juvpQg z_ySbb%MuE}&o4S7* zw3yN##R*k_g-XbMo#67z&91_3f3mn-zdl_y7L+FhT-SrTF4kz0?|pj5eNEOHjdaja zge=C)lJz^Bur-EV*ipQCQss7>EB&doIbs`lW} zV(wyZcNa0z^V{LIKgZK8fEEL`X*(b(L1BD%W`UNNh-ERB4y%r9H3q6!RGb!FUEP~H z9N?ZpY_L=z0A<;YyMTa}SddF_hRaxD(8^ugrs(|^sO<)9n3=M_MglAo--+UocI-m2 zrzfKJ3v1!oozD#rTS)TX4*@3B#2V@5=H`3`C}E+lj<7q@&!P1^%?>%^G=F~N1Q6h& z1_(;T+q43)^p$pZ(z!=iWk}b6pE$DcIJme^t-hpky@OO5c2J!52eL0^P)Xly_nzt2 zzhlC$bA;X2n=f@P)^To=1u{9hr@JV|L4nU1eKAIw9g|QTv2_g)#Ih;yTx5Q1HrhwO zpC|J$v$EJ8h;7lg%Fut_k8CR~wYcmZ90|XhE=gfAjD;duYDr5ZcrOcKxSW#*Te}=~LM|RNmlj)HTuAvpIO~j-OQ-BDe9w=Qpp5sq zKY5=LP2B2!2(?wLn6B7|6>GDEmjJ!hRTz#WM{H zcmxpLg8Z%oTwZuXipt9VRVVoTma$DEM_V_Hkf4-0Q-);UIJ+EWw8K#N?Gg3p2wJ|% zjhWU`_33e9v_t44v54CzGXi{k6g=M8c#LX>+~V-})}Np$a;vFsL#aONtS$`A6e% zAB^B0;WU#Wd5&kR;3n6r{JgotY=@<~^H>%pUgAGL*k1ctNqpG0x&6wsL^MgXL!Z~z zX8`pu_Z}Dn$(;X86&m*xe}KN29!dy=M?TqI#Fr+U4$3gMG@GkULAa1h_h-UncTJb2 z|1A`NN+uTSw>l8B=5jp(iWl~Wx_Q<;uG^y*7`%~!iLtS~|;{ zsjNS!9lgkysxO*CCc13rGCMHy!5MFVn%4;{vP(H6~WOK@Bk-^%j zj?ZyEUaD9ni(*-Tbh$HoZ|_A7dw&9ir2+{A#jCUch;A&OyP4^k+oXv1pLy&8bP(cr zPR}lca9xslUH!g)1Twk4HwfH(*w|X?aGG%+ua361wnRRh7(WU+Ed1c7XJ7B~_mx`! z(?MAqr{!GiAT>y72O`^{i(Gj!_T?s@hqIjzv8c7q!?hkbcnklBwzmq)@?E!h=@tYe zMe?H?leIa9bvpw@gW!~z*r+JLp~L3JDw%~v zuou4oUMeBH5KD9=$;2T{iJwztkV<-e-hEhXJiJYAD?&5cVUdi{RTu7?wVp~t;D^uQ z?)*YBdM#kTiDk(CTH^g&mdn&udmx(U)iQLm!)N%#&+kgLV$Z-M4;<9M3@>}Ure1)& zKfbQpWCeq5j!-AtH(^d*SaNALHc<${yytLBujkwk#(3ukFR%#FS!!!L3OQ==Xla4N z^&%21-LDP$PZcOtk$&LrZgM*64YAQM68P zXIDkL=yH_&ChT2BO0Ajh@jojATv2G(NW1D3hUd-RL+dS!1m57E6b!&`+LpcA*RHjr zl)@l+m>z5Q#Cboz(c*fK;58w>2=65UPgFiWJ}&s9H^w10&!hOUIWdtE70+pR%r!dd z84h~1e@G#Lnl1{;RZMJMJnl?Ul1V_ulAdUgk3(X7vh-Kf+4;p>OLdb6U?rAX-A}1E zqSpgQ#GbK?v@}@f7Y)Dc1oaY$e8kQzCWR0)b6}~SJMAxurmA(^@H!3{HT^)Up?KGE zu@7SfH+r|$9}BV48jgf)88@#cpKc88vN=;RF>N$=y0|!)Zusdhlq(!^SDTD&tl6FQ zXZaz5^%UJ{$^Xj5_hd#x10u}Z#KP?gwkivM=v)EAOfvTAJ}eHKS(m-7N-$FWY(n`- zasa%nW6sH30J2t9`$rHHUtZT9F8`VEFx=OO^t$3Le`ho6X$x8lY7XD{F6xS+Ev+HVH)@{^J2a$%#j z8l`>nZENeRw%FaBn=H36v9K^OHntfCFut7ltN_>BjW>y7{dS8Tt?r%HZmT`EEbnpG z@DypD(%TD#8$RdbF<^`z&OpU#oUi-r38N`?e{+EwkNce}&*^->?=@<#)+m`r?xXem z^S;W;6`S98XZ_eKCoFt0bvD^zN^QQ31lzoF(gT;o+}x)blG1i3(}U$*@Me!B&?l#- zy>jdV=w9=mFL}_2UMF5mZZFg`+iv)>QrWq0Yz~VAus=4t#)_%IoQvw}>KZXH+e!** zE)NHMf?CI5J(Lv550r=h=NueUNQdH=hpUGTl|nMUNA1%>)Eh_W3xDd#1Z+Lt@WmC9 zg`b5KR@rHpm~el=$+#X0c=9)0j&QP&qW647lEj&-vrc1+*foP@L;3y2b<}Rd*u+Gj z!P#bMv2_q(03~tG0NwYh^9iAn*#C-%5S!h~OSrRDx!b5z^G>`N9FtKY-^VKJ>l5A@ zC@Lv&u(DE_kIa7~&-VXG6JO?-E+9I#Dx;&*h6Z;k$vVnvWQ)3x`aLqzNQ1hX8|!zI zxnc2CSq}y3O_phS`VlnQ9#9^DfZghPnzF3hCcw)2<`rl>$j-*zHlodUA(9u=|0Jvpl%1qFZfB4S}$+1DhkS^S;~G?_+Si zEkF?(CaW=@i-#aQdnJN(dEkLvt~QzDXoa|)6oUyb9$?%C3dln+>t$cyv=O9#AzB$G z8yOcGQo#HW97tFyjDN<%oFobP z)0<~5eU6JW+IfsPOQVXEcKxENswPPeELmwJ5ZF_QSg*2vzw^#mz-oX$d30f>btZZU zA3Z;#oRPqSU{R4VM!GZ+<72|##nqci%{Y*Znm6n>8)%+oY{ABky*z9?4Y|~5`+V&P z`YJ(JMU=jFTp&E^Qx5);M^awi^=``moJGpBzs1DH3RiGZy(m><1EZSg>rX=leu(Cl zmdya4JKxirm;S6W+0Sg9+i)YybokHeKw}s?{(`VTfo^GXQ&Ur8fH)YXbt63M&uVqQ z80p;t1hI*l^W7=w27T3>ocpds2$cu1r8Q9TLowrq`zxMWX!h#GBAaeWz)AfoN?=O6de z{rR#UmLzA)R}11UX=)v^!fdy#&AxU8pl$}8e1n{C&FpqHp4OnC*z>N|#a7qbke+p;p8yoF6?o{rl41RjT3yke$oW9oL&wzF!rIL;AB=NTJaLB^Y#kd`#L?P+ZKY z*%F8T;l02|8H%!0e&DWX7d(x!)iGQ-0S9#m8BtPtZ;X1ZS&h7cf*_!jc#wiidbj-s zgChPib-O~lS67&IE~4xqB;6#V?H&Ym#HVB+FyZ&5s~@hzJ_zSWw+&OCPDf>t8f${q z#e8m%;88E{O1%AvyS7Px^zl0-QREq?=hcxfz0IYy*)PA_OG$fuufQ21T30MA7!t_| z5OLXN(_3wx%*52oim6r5taVzKsAQnQXLTs>VNSZZtTr${pO*kkBXxbVwzbj?22=s2rXMU2n9YB_yR+g%-&>+Sco9_>;EVzH zCJ@ahCI$50kXiqAtIGplYsJ_|MP!EtLp&Nr7)NXKYsc-yRz#@`e$Ql$jafA>C~_-Cx!6PJ zdP>>TRMxw4%rnh?w*LNpN(1ja`E;?CvaEezCR0&S&;IG(x^}5nPt5OMuCFrev=T!( zl+2ls@K{!PE1f;{+I(kqG>xQ&dTY>J8YOcHI1X0$0VkG#J@Fb`mdkYz z&g@)9o?pc5&QH|bifh58r0cxp)Ah;BDIQ3oNL?I6#k91vOt;`%XJ$Ab&cyPK%+K>7 zWAlRj9MAf&Wae&BQdSmAKwNjFd8T*Z6=T=k)<3HSgBqwAbUSCWg@31}Hbc;$C8P^y zl_egA3)-V}^bry1)egZ7szQK)=VBQr$#NQ;`9aubFVzANug(pM?)*tTFu)y>y zg6PE5D9#oxGQ@7^a2_6u)P$SIHk$^wnj0MbKg9R{8t@v{I)VOhKef*b2br*5aPMee zp?Ki&*h?&_lWM4%E=dymRjgY&yHUEd*4rI)#cCzgko3l#^#%sxCfWlman&2r~$rBJg_++HyfAZGX1bl-Oc#OAL#YHs-ln)rFKKi?^j! z^V;H*wD&K?q1($=btyizr>At zo7=tx&MEK$5YCm9Fdxjon2_{&cAYGG0@FDGrx%RW{KH|M|H;6>qTw$? zeH^|J2@(VUvq+K5c%GA!lMw;zYq|ml`jEjvSu8&srrO74VVXgc>L(xgSNK8d@Ql7U z;@Guxd+Ra=Ap`b1HGUG21%-to>z5>4R~thqfSud94{ikphK8a89k0g6jdg7ZF^Mbb zN%i+!4kDp}P-wq-U9wurfsBeeSM7e;r<6^)&}gYHVY&%M2x4!=K!&`@c~9)-d2;)M zyISaKZyA|s^aiM=6_JK!WnuzB0cR5vZOj47MZPZw3&etMswxV@GmRm>WP;y!tMe6p zPE%u$%>Rk|R5~%sGb^Exio|12#mkGBo~?qtX;<=M^AlIKiOY@6EDYYnbh+pD&SZtU z0n0X=it_%#{Pr&m`U&o@ktJzVSQ<+dwu5J9d=6(ce3&k%k!b?=EBDPmFi1M#ftgsA z;}G^jsx>)aQ@i?;}zh#$>@I?{TV7(~g7H728UOU&2(&_3g&R@95|T6l0xU&2|R~ z0KSC$Pa0XFJm1@x^%ii6I73&|Yxf%Z`LkH97`K|GFEbOBo7CujwDl1q-5saEo5i+=0eOIU@JO)=Ds;+j$r$pD9;pgF;?=S!Nx(0STD#m&k`E-l zvT0N?b}bU*U_h9};xw3kf14$$50!YVkK5q1PdCV_TxAca%x-0{4v_w4%0PdAz!5Dj zE{=V3|GHPa*SOcvD}Gt6ZPJrXd;yf!SPW0LX*L7bAK zQ*3m)jEszSx5)wS>MJshhl_IxyHA>KfgK;DM|iC7ox^hS6)qtHe0yU)XGBWT@SXY! zSPt_yJ^ucku(zlZVy?$`z3YjdDaxB zLooIPxr`)e`hMCG@yuPGE{sgb&>Xwb7f}?3~zlb|fA>B2) zE{vAO<;7?Aq6?3ifx+Vyj@oQ0gWE#zYab}iOq->xEqq^(=jR9WZTt=+LcX*wl5XJM zF2@h0{08f9)~}A9ZMvmPM~m62dO0 zC*kEP77TW~9=$WaJOKGR*_~dxAcK}lyJL)TtdTU6vE4phf8o8`eWLU9XA*||M}ib| z=h0f~=$QTfR$YRVl@*!_Hzz@Z9uJ7!n^eCXcL;ykF>(m6Pbu+0LI=PE0W+f$P;pTXcggDa|xB|Uxd=dQcupfA1qaIzFC z8TbSM<--{ro8W%E3jaE&h~=mOFd6|MBo+(1TQA_N#vF-1Ses<87b&8*wsvM`W+o@b zVcoBH`}$DYkVd+F`8Xj_6}-+4c-I$cJnr#Vrw+g^;vJmHnap8n^1O2OCJ`h?6*+i+{pzQj2vtcA~6eQj$l zU5B|Tur~$jQZ9l%&d#TNC?;xD?H7Eg4QdA}Fbfwi@hHE#Lp?y*rgnRr@ zV3l*htp965xzhXh_;@etsacLRw&i~#DGP8>?v<2$6c-Nwymv52xGvYD=H>#Pf$+*o zTVyq5RaHsQZU|}(k(t~81n63{Q~XJz4}QIj_8*kZ|I+aP3uW{Ezu+AV|3%8d{hxY7 z%>Sn-O9?u+**)&~c1L|>n09084v7USLw zAR^!&!HcBm4RsTU0CqDUe^8QkZg0E$I~uaKPBYC>mcXP7(W7kfTt#neXo&Jp*Oe0& z@BQ|T{I8zwrJ`t1K$H&Feok|k5Ku&~vi_m-TQ8|o97qmh26Sd?Imh(<{JGTRiajwf zG7@}qURbK<9zRVa9cS>qi)`(3`KS{%lijBdD*NW9*3zNPzg}S;#48o#llcg+qN9}x zo|reJ?VJTHs_0;*ONdX1gM*uslcU%UfAMTjYV{;HtmpYH6IGx{lwJ}5Je0j!U@T>z z=%OeHwcY6n(R#ekFDr{Ib_36J)Dp2OK3&Asu;GuyRZ}uBn=bP#Z}WVTtc!K;K`mm2;E#Re`FkEZWg`Rl_^xGxZp~Wj-0bY` z541)mMq=XP2fy2Nu7X+~eV}{%T-Q(5D>DQlUJ15^$fz(fmIBouxB+w^}MUgM#gTjT(pT1m=13*x;k1bIa*?SG+flwa3_0 zn!@gu6F$vCv3h*oZO+ciAaj`5yE9!b=(!;)nTo;Vd@;EFe+>@@X}ka(k!zG?Ts#FN z%rdoR+~Gfbg4~qbYsc7}HXimA=Ae!az&4i2V#?jz-bktEL#XxIo#f`TTo)J5aD+lW ze~?Rg6V$1pqruI|xe02P-JK6ZLSNAP47^SZ7#*27(rT+}vU8LaZ1DD0&?ogoYI45o zks29JPEHPhhqpbpy*!-fbzN=fz}fOb=ds6i%T`!wE7jK8eTo0%6@JNAFo+__eznQ0 zZS=vSYvog(Y%0xOYBj$;wTv$g2}!$4CyZ^q*<4*(Wntkr7*xp7qqVl6-PQAyATaeQ&Q zE7KN0Z99FAEIR<;M5~?2@sSZ4209PyYq!S-PZs(3ht|Q4J{DeHcW{e|6YDDt3k#)= zBtgvnu;ZYxg$NH1pLhxo|DuwgSbA?!Sv)tw5uUy5L>e9*_P(WZJMm}zyLiQkPb%Xt zUw#Av_|EQho;|RHEEmVds;H{QZN?SCte`$ai-5V@qiWxRd-Fz3ltT3RGtRP0L1(65 zjo&~gQc+Qn{5?^9ywo6-MC~7~g-=N6urqJIFZehTMv6>FR|Mo&Xo56X&np5@;54+Y zl3(|67UE$G%m#T~%m3I5TzOvq$W00hlDOoqHU-o0i2@D3R7p{9HI;EN>kPuQP7~^y0;%Cnyu&}UuUwyNGvh{H+U_+EPbc8H|Izd&C zr-AwVsZNIQqN3S!&s$*<9|tdCX!}o2yNia&n`buAB@?QS4vvJsIq_LOEqB4ACZZrO z3*?2rzbglr@)IWdcctWE-bk78;Y9py z%gbacRB}d5HzTD!i@TG}7z@Qap*x^e<@6v+6z2_#ymWuD6rcoigZA|I@LLl5F~A&^>Dj9;P;w;Z-AEva0cM& zOQ7@qtFo^krd%=yxJLZy9K{SI(2k-*y`eqZnz0eKYo-m3+W{4{6h zOZtoyTR3j|+$t+^2-=S*P0;>g&txctUFLxQlMilUCd>QaZBhL}gsg~|K7c~i)biN@ z)BLeFHMQ0EbJG}D08dAX+7%=a=CST|a!QlFZMz!@rKv^3z%Vd0L}8w2k=U*;45k`| zRZ(r_F)=Z4JEH2ewA@lBIyV5v(fWha%2z`zv{*(KkyiSu1Z??YN1ru2X&t;Q)u#*7 z(^WNmwA-9o1xpz_#6gj3sLcL2DG8u_gyk4CL4kjV`B7a{v&dg`jlM6?zGY)<7N$Az znR6B*bpdCW+E;THR9Jgy_si2-_^7TQ5yY{HQ9|h4T(3HTIa4F0?wdF{{q zyQr4Flu<~Adnx58pm2FP0m0qf-EEn5Nz8@c8E$W4YywN5cXTx5jLtq-gkK_@P}~xA zWb*8(@3wTV!WYZWzaC@b0Med_q{fy_ zq>g+gBQK9Y;5-&byU=7$apA+=Vp_fkz&+nwK78mX6C!`>=TYH(xM(jd4Avk<%{n+# zV=%sSZJ(j1iXfqGQC0V)#K^yLacy-wM``=~qWSEweWjjAep~Ru>m50LGQ~UPI*z^)p+4 zx6h5E*(%gmZreL*Sm6O4x!dFU7?_>oW9%&^LwCTSl@k#jK2@TJaY8aNN zqIqp6>FhzN@K}t;(#hM~*C)NGMGR-p(7r1qi;W>8hkyn_2n^G(Y#RogM#E14r~xAr z8dZK*v-7u)&+wR24Xe3p&(AqO&gK*sqXx|{@LHc-CU`8h-+P4M!(om20@3$yPUhtH z{7}$qRV>uh9HH`MO)W(bdM9F&^oSP?iYvDu~4Loa`>E)x@55TEq3P?%1M2F1w$`6yF6gD8~svV_9Za5{0>EF zqmM^*ujrupJV3S2At51gVsrvmxzHAw1CSYFsoy)F zL4L;*$chRuSN@0~NPA9#VQ~byI)o(x?y|ZAJ0HvSB!&_>Ug^GWn3ymh*Q&8VPQu;5 zU8N(v-jBWeAdTR53#>b*Ev@}pN7H@L-GLbS4GQln4L9XM)RU|?Q-;%K0i_%&M;IP7 zChpeKt8-JP0tsFrNkB(SMkX?bpka2V+O*&7h`ApvejkfgUfp^$;pS^DX^S;0``rl ze5oeaFkIDMs1QL84qPgs?~%lEmZ&e7d2m@p`~=>rtAYIoDmWi_f+@bB<*`x8rFJ-c zNZS1)y7mztR)ty)vMBM%xM438=cg1w#r!nxI?UN+3?+yPGplH2((zs8BI?4GYg7wq z`J92%9uRwZ-2=;mCaO#G^O!yq83F!@5oHIEdSBj8OGunD!mz~T%O}e|Jr%3ssQtRX zjtB|w&VoA>eJiuyj@fhqU(mtQ`XTgEg%U;6?+SzN{u&%KfKbTq5cL5p z^z{@(DEROx9fOiz>Bm;|9Xh;qe3acd<;Ip`}Ncq@uewvo|%?9=s za3XHAyT)SVcH9}PzwkYJg^T;00j+D?Z6`1yhR9rkJ6Ou&?z#(BhuD=LNFOc|hTa^Cpj;4^7X=7=+ zJDayvHF*_z>i*^|egg$(n|_;Y?4^f4Ms5zoF9yNH&`pgMJE{vl-T*W?ct?g&%Fw2! z=|B!~5hZ$Ye3**VhY_$Mh=}0c<@XJMBcHjTBQyX(6si9yLIDV|m~C1OM0RXk8L#OB z{+=PUHSv*JxV*ldnF)#z+E^--RMZ77qb6^EOdJ81eagehj+UfBqe_c`iw98x2)HhXNx zbTsW}OJKwjV6fZU^HhJed>O-KzmwD90@ZrZm*r=)Ax34*a*>efQcaT?5v4Hz5K^od zqt%6fcl4y9Z!EumA6H)}3{5br_CX)_=md;4hc$R`Fyg!z0i{~lf zV0RBbQG+w{G>!WzS8&}3t~74Cptt-6|Hu80!#XW+`~2Qa!RZ!JfuUz=jo?a9QEvlP zh1pkr@$4lQUb0P?0|5jQ9Q+cntHoX*0L+NAh0qLW;;N{tW94{L8%SnsD0))N`i@SW z=y~SubUo;qW8q+}Ew+&uUZhEAO8Lo?XrAfqv^Q&IW+d4LAWb;_`XKP@L1E&%fuf4Nueg^3?m1C-r0a=kdQ(sY* z*tb|lWrwDAhX*>oshmnSk|N^bUw=)EZZPw=pJxP*z+)UN0kZPZR#xV;9&*Yv``}ED z5v6G)z3R5V10`~JAW4mktv-GNhzuze-AYv$B=>V^Bj4~rH1d9e$wCTJIBE|YS4t!q z5s(0PIonbKHRZJFKjiI+^=TDkCE-LDDk&r&Hisy9^L389apv=r@T5qZni@C22&;73 zA$GI8*a})VQShaED;>wbzbn-V5pArpJ5O(Q>q1nGc0OEu1*C#xt1S94^{TNF*V9hX zKut|eY36Ua`615*|@w<4hQZAF)>$h*0v@LNNv}sn9^YTn!xY_pKYIk-rQx@up*?c(Sp$nx!Z(bt7w1_L7cMf(OFu$>h*ev zHWCNBNrxDf25=Q+mF3BQO@J2wNQAviX7Ph!n@mBwi305W;rH(~r3LQ)ZFTV2WGgEy zoN2V27X1(mcY}DxL00yU4x(Q=WCJ6(LRCiJwTnoBF5-lM0PvK`LR!y*OU0rbS;G9A zccz^ih4Lgbn4%@IVb$4ii0;OOhM|yzric|=dY2|_o)J{e_ep-svnEwZ_9=RL_fE14 z>DbY@XEb_(b%&=8r21mne&SVg>tDqs{9$6Dq?d*-Q1T-&WoKpeErJ3YukV+nMprOG z0D2aci3ubpq^M{>&~o~Gdq%V>_AT%>bR%GlO-(r+{vK@%36tZ0S@j*$;^03v2B%Vl zxi#Sa8u-#I_1Q9|?it~07M9nf-vTQukH?I?fIUQ-@80xG4X_J&omR_NfimKKcQrb` z9MxCQr$8bof|u6rQ0SI>13y}#bEpviTv3~|qQ0~9i(ab-CJxTmw6FXweNjq=bOw4w zz}4@1do|)VT|JWD`Gr{GZD9AlP}p39YglO-V|7c3Uy_uvva*ODUE z_~NOhJLn;V>N*!1BD(fExkUBTkxFiJdPp{v;Vv50)RadClcxQZ4w-HkON|gTcDjO( zR4ECkVc=hny^7k4KxSIK;NhkSNB(H|t=c~SyB2_iE2FQ-&W`md=kO)UHF8+M{n4C= zf3>%k#P>rXc_JH;6REYSDLE}|fu7Y%Kz3|=JY-M&_@eeal^eCk`8oW}*>1hTz1w#2 zF$4l95LKX2y)kCo2Ki*__x2fO#<~4Xk=(5lIP@>W3m_zQ9nA@b+eeTjnTbkN~(NGHKpnVF5CDqEn4q-C+soc=jgK9sm@yGjP zaK}#(b<(n2Yze678W9_mXZ!nn#XiX~(`Cnh9=SMd5kwn0l;|yY>8Hy{cx~ zy<9iOUlRM|V|k-k*Uw+SKwC}LK!6mgzGN_49UT=nnDysqy4;>l!1^XJ ztjnSbqBRAfWaG<-IMI+~&*{ZT`=`bM+=j?D{Ufd6IXX40}pf~04u_F-v%5M`|5F!bw@Uukx+=q(-5 z-6fQik1v6kuE{lUi#d>v?k-8-$Ay4C(2IldAETqTi(TS_9wTsZGOv0L=NlOh&VXH@ z^U%{T@R@qKM)x2ha`@st8RSY!%-gN-o1ds5kBZ(BlnEA3L!;yFVyjN`#o+F1r!|@G z#zxl|3g&zNwr>pwLLTE)n7?_UVH<^uMH%g8OjcH_yOOq7n}Oa+aq_ ze>-7AQSuX#RHA2l3_h{3(6Y6Gk}^0|qR}AeR-44+sg=L8I-Rmn`dStjIQ5F%ny7US zHp_aUDK=luFRRM&2Rf~KU$AIir-Ee;EaI~d2fXO9=2o*c(H_|b-9fC~;UWoLw<5!v z{sNxT_bD@9WzGgGkPN*Cv3B z48%+5Y&OO!vG#~XP>ZIf`zxKBSAQ8w67V>tkkQaw?-ILd&G#mL&v%8o*yZQv=I7>x z@_LRVct_2VW)&B^uU#ESEiYdlE;YZlvM@E3Xt~bf77lNcFcBs#AdU1@HYt82nuoyLK=PS%l zOSL$^#uaf|Og!%?A;Fu#+B*h*8xVAY!92a{R{?kH$gnVA!gF@8gD~-T`2?hMMRrTh zc^{GT;c=w2Ce>pHG>8Jpo+5fr_z#cUs@O;pJIhPh;nBhn_G4;HA@J9W2|fzefrd8x z@g?7-GkW9KsYa~_d=6}#ji1)sWW661vu4#AXkyD!$be8$aeN%90CWN|SKmNwF6Zz!}kjLQ@+A)v zRq47G-Wc=QpW9^CMEl0A-3~UQK~d6)F?ymzZ9pL6Lyt1+B*qBoNaL02A+`nD701by1){hz>qcTw`T(liju|%WQZewsp&>I{1N#4WEENXyYS}(LT z0>!;t5568d^slV^{k-@y`jF+ja^SBB7m{>|`VJ!LtWFa&& zPOOhKsf*>#hr82d$YLD+pIOvd3cTAlhcl$w8bHtiq@Q1u>1w)t2egbxBA%W>@4KxP zpl>He3#Dl{$Ph2Jc=7*B2S@SKcGPO zaK8RCAq4yT=H@2q3zLL{nvRuVB%*$%R&!eo=XTHCsR^lAJ3G6iB*mcdeEI#Q<^=Ds zLuMG*-4pVw_GRp=(lR=%m%i}u@|bSADt zlhC#&dfn-vdC>)Vl&ZN`A1zPmD z7jY7u^NY{cWd^QTv*DYN+#Da*~E-M(`i7WGS|Lc{bmix0`r@pEIOSs7w%sB?w%0MC5e#L1w_9TvrNP7%7k-^=o`G z-JNXjtIyHPYx83Ay6FBViCbpUC`N>XY+0aQ+S1b0!derk$po0l@m@QKq~ps$+b=*m zH$1%D_*Vvt?{%sPW3v5k%EJ%ey1Tm{zkDkNpV>(k30%vGdYQem3EbD7RvoP!#Hd)1 zaQ>T*RuTUj)fR-dvf3AyNg(F=L<-@0zpVK4U1`EyYuqY4$JSyk>N%{?-{uq$o?8cD zbflBb<@uccl|R%Vn3uq)H`=+7=Gq6e(#6m4kXP6gliIjXlKCC!r$Destv&kXG5vIB9R@SmJ{yMeVqC72ku&NV@S zofsUK$F)4wdHxZ!$2ydiCK;-uVwB%JX{d!C$HvDZi3G&@`@uJ*p{1>S&k$N~uA{7c z61%KFY_1X;gH=fKR>=VRuQ+M!%l?#*SAz$kdGw$mlSk0sz}5Z4{+EQcMh3r}vfA_C zw4h%DPPho>{}%=QuMOepm;HYc=p`q+`ez#D7kD;*!QoVg(ZXSNb&XZYsAB+v3@{G# zT=n?)?2`z3WIm1d;I0ia0oDuaIetM_R`l1eZ@`LEM8`TgUM=y3IBIL!dwCi9{wISV zfZEHa@rr)9pLGPAYmOI`;0i7|@XGQzr(;0CRIbwghZD-mDCpV+wT?gg`=iT2A1 zGRG#@)4;4O%=6o^TCI9A(IAn3`3e3WFs}sxeLy*Y%W<`*^zlJOU0s42TMYje;R0fN zJ=QCO$1Jj{Ba8O;IqAbdz(L5d0L5x3Bq+lEQ3(muA&8d;|6*P+fGA?cKe{IlLG4Ax z3n?j$$a&Li2-!{-oym+;Zm!h1^`iqwy$3& z3n~qFCkjC-S~n^qzSFEy}-q3iwvD@ zc6UCTtB-TZIV7U`I*9@5H!=k5sn#C<(PQWSN%$8a)0X6`+ixB$kRQT;xADIvd^CJ^ z(2VE>>_wE(ByOme@~_-4_H?RE53(*CbB{x24Q`uhPTu}i0s zr0UJjzq+z*{-ZS4)cudr?1~Ef742`s&1PEyA|p9G0@Nu$nTY;vU00Wg^pl4JTstf$ zo4!UyqPMgp5fCUTMFS?cDP2TtzP#1ive0onS8OU+3%#ymU_$9LU!|H{!^L&SfPX;5g%243I1VDQqzJ62#RUb{ z^HuOU-X;c7BC|DC_BRuSj42$RdL%y`A6^1gdzTfsqOy7|Tn|NNx8&=W={3Tg!>*&F z!>rp-0VWL3&uO-Obm3nGYV_msCD)rZ8m)+|oQ!=TZ>k5eabW$o-=vb;5@jiS8%fOn z=_u-HnA{vhkdnBE^c$J(b(*Md#6Qvc<5gFwSHIxS20QqhabzSI#C4r0L`PORx_)Z$ zs5V49?M@h^1K>FVNhq(GT2A9=jPXy+x{p%c0Rp4b<y339GCR%5s-$~H@w*O6;4wR#%c(v?-pSZ&3qexs4Pn1iR9JZ?06{b zJvlnL&_v<$AZ)Bl;O}>hiaSxJlaio^gdiS+fuOn3s@YhVHC|>l$A;m^$au|hlTt~t z#-5jo$o4udFyu4PQfZU`0RX5n+r_IN4ldvU)4*x|J$wUi9@nFir&BNoi{tPN7t(d%?}lRACLP{DoNl)8_`(k3;M zhzf9;{Ej!ksUs8z=_2P7!w+&0knBirxrU}%e<=?9F(908GM?E=`pnYYf)%wpZgit` z&J2^FsjR^PS42w6cB4dtbWd3sYI=tFxsWC5+4EeN*4I{n)H=Dq6MLeVm>-*(vzEhV z^V(^iE3d9&Pqg{Q=_Q8^)i&(o0kzHZJ7ky{%_|=C2qMYVzLjnV?151nU_#1yLIT`V zqZ2C%>62FX$0@qU#tvAQOX^c0Qr&E^B6AM?`bBFZQ(XHrlJ(|NlQ$rR5wo)!=(;{# zn5#YC8gu=*F?43LsZB!iuCSumMxVuN3=$(RDMVA3_X?(*m%ps{6PQnj z1_nO-`W{zqFQ!a;PulT6`T1*MqWRWS`_4zb#=GR z@62<>W#T`(NK3(?!0n+xP`J-5Kv)-i&;h#)IxUGk7y-IEbsq4T^R1hMWEkszetcOw zOG;0Nm~Patsh0nZ`ID?SW|D3_+v)~to^VE#ECmHc;+TIAPytD#HQQ({v$e%X2`F^qxrP#3u69T!IKggjlRb_)pkusGZ=Q`U_J-jqudmt& z>akJ7QW!igdLGq(Gv)jukG6pIbZCVru7iny>7P_C>pfr!Abs|(7IYUgY4lz81Y~T= z>oi3<$je_`XOdo7$;-*zTW&p{eM5ATFP9`cCR-U^w-W#-m}gjTYHB=ud3=5JAbK{p zh5h5pXn}F^qgRex5AjMt!?^vUgxWf00Gz)KcjSR#~4WB0Saz>yx1vOo^gTG zzVanhU%5uCkd6laP_H z1$1@Gv&pwpfjm7P9Iwv@Ig8z%lGpPQWO z6AxOJajLQAx&IVjj+U3e9r1UaX8`ymgCCJrJ;jL| zK-&Q4{%D8%Upi7S44;ACe7QUBF0|AJL%aeSsd@yYnN4!>t;s;4&xa8QXPRFh!|XRh9%7%72!X#f#v%?gVf7D=z4y? zQgPdY!G0bSdMPUIa=TI5@nJQE)B5=rRF;ye_g`6l<+g}>r#BKJfh>QUB1;B*%TNmR zlM=}zL`;clVg_Ub!5_y#VzGI5r3Z9ep?vxBn0+%~HhE8Yv9k~andfba73;X%jiF69 zu{(k2nb(w>N=W6XY|29b`Rlx*f@OmYKlHS9Go~gfKs+fH2dkp+$6@T=9?=LcTqM)w zDW$#GWU(5BA+fH-3Aqruy?79?JOzBkkvg1fPJqyj;rQ}8Bd^WVPL91w%dy*v!5J~l zQ%Hjuml!K&d3pn%Cl}$rJ3p|()B@!_pW11M_o(H^#{NpJ`X<)sxYoCU&q7$Fd5>Y8 zt(OjFit&2cZ-I-P8&F-Jr-^0+hH87=++Eva#lWvs*yAzlG|)`X)wKY*svOr@X(FUHF{AVkhxP5aH|vg+9l(M`HKgJwJ_vI9S{r z-A@BI0+?Nvlv;($$$>ZJ?bE9usiDdp;Hx4Mf);ecaKRavRCw7x8hSrc%vPeRT@s?Z998)@yqSG}wR& zxfD(tmU54aGg@*QPQbG{PE7&hG5C-Mg^cB7(1{4r%8t87t?xd5-1(Z`TYnaNJ}$JE z|ApL=_+m$`{XzQ+lAN3{t%}iv3_QG-x>zVm(9KD&Oworlib#|TQE|!VIK`?|R{2%d zS%tmcZ!;NKODAA_pft1JKC_MXP8#_|C)+pXM1?~j4isKX6_BhB?#j#_O89^yvsAUs z&PRK^wG&TvJ~oZ~ByB@0`h^U~D5deNzr#1H6%!RT>=cn&e7v~05Hs*Wh664HF!zL_ zI#f?6yVH=H#f&ZzQb#H=p?ZPaj~iJ>7sYk$nBTqM;K$F#3f?eW z+c>8GZwnc&5=1@i>*N10x^wB7uONe5!zpEt^KH-0xK!0u@(03-l8fRfrGk}1H>)LTxD-?R z?P#s6tVBg&m9q7+=?>`%MD;?jszAQFRHo*UDb=iCbk>vm9n49UUV+cT$fxT{Dq!%9}3Gm;U*?od_O*2qZ;neDV zZsDPJ0fwRfQMkqbZfh@8d*7frIh|(+dQ(NsWJ2rB_YbVzn%0{SU%q?*JlLyI!^9~E z0E*1Dn4~>!2@CNZmQUYyxXY2^cl*Rips221U0v-?xkmB76y zO3th%XD4G!!-s$UBI8&9DFdvak3rwj8@U^EJb)Z@)@WOqAt^F{f~Li*6_feLTsp5J zQ^?n}-TBj}21pLeYqJr>lWjJ~NGP1`pe4Tv=0c-FsIcB|6gzit;8fAWSitgzog!rI zh1b>N=Q|JhMb)>+K`PNx>znI^|4(D*8Pw#qM&XDckVuEnREpGGnskufn-Gd1(hm@% zca+{iiX7yC^d6}aqzOn-!2nW26BMNxLsdYeDZ<^sTYud8$zto|dHE#ZZ9#5Rks|DjK&b(*`6CnEz|cpDj~aPm^=A>! zKA+K(o!uHkd6b{|6oc7iDfgj*dRpaME*3c{zilE#wH%SdkdHns24^xBJsk6^+l;*zm!|8M!zs6G``RwZ-(8`}aEsw`X9l zI#SPCcbHHk91>J3wS*o0t*CBzO5G@Qn;sOs;LN=1*u|cAUD&!^xPZ>1p(~$VyBZ~J zB&aiFx~Z7iF`^byl)<;f*m|XxNh)=<2M7@#L%$D*5M&-ORD4_c70SB1MCsCQtJ?+H&ao`s@lbelk(T=}q=zSu!yF zi9zxpkc)h}8meaQmWYJ!NocnCZ_}hA*sk~4YJZzIt{PUhq7fe{7P*mMy`J}15gybT z%l)ewt8|n>v^V9B>Q)nGknU0+6 z<5TSiRh^yIl!2JnR#E7D%c|iF7vt%vpv&C>fYnZzw&!7IW04M6lMRTOc}um?jj9Mw zO)~gtGWsPHbeDDN*XU2D`a~Gs3Hc_Q!>Z)|~?(pwAA3Bl86vc z_L@6on}1=%fhrKlF}U?+Zpmv?_9P;{^|V9KXJ>F7nzXkfihug75iU#0W^t7qlT85o z8;tdH4N>IU@{+3ec{K()ICU(o{5gW>)YNn%R%nM<0H^ge({X@QSJ|pF>sux2*=ek- zt~z--0#&%astWCGkF1+AVdvl|&V9ukTUNf1w|#LsaO3TlsrI2nnk`GA`1dXD8yg!b z&Fetwn69_upy%u3%ljA_c5;b4rl32zJ1q^PP=7Aix=nmxrru691V^aT^&GFs%4)f3 zZCwCLbnCmZeZJMt^;4p$wG z$~$0}6}zfPBsy7Hkw3{7f_uZHYe?hx4U^0qB9;2F6)tFZci#0P(SEoS0*SXyzrC;|cT_*!?7QgB&qB@G7ytfQ4~jN4X$;4^E6%~@LuUR^b&g<3+N zI93`(-a7@5`N|3ueDBK!P@giM2ZshHSwm?i@cV@wVA%`4!Ui?f1t6z87KfLuTF0?H zJw5xI^Ea%I0mo;3b@QUX2Eg7|penwCh9Y+@$I^>5>6us?_{DtitK*E`MhPJL5%Wt) zMy7xKO%nA1BjBIy_EDK(w+rOIEIz!7<43msBB$;S-X(xm%t%eK;knGdWGYH(_a8p+-uTezIS2A{j%TrbM*N2^f0x;5W3eFF1;CyATOWVJmm39M^psTFlx#`_DyFp|)=+z5+sQ7knv^5}btsSPu0Vtd{4F`1UIM9hlSil8^68ka; zG%HRo28YWCtvTOZve9DL`gkq#l%=8BUWt-;-AQM}9-F}pT21}-kU)P4{~xpgY{Trh zQVUA3tU7_mVi`0C=lUCW3BifiYjqO0yxe%TOV5aRo+PemuJy#q~Qc^O8 zrU`NR($&Ga?_}iiuD&(E9IWL6dAg;l2e{+FGji*WprI15*V%epK)*ehe z-~cC;oAz(=!MH-U7hkoPGz}NTo}R$A)Lm@R#`o5U_8@_eCb7#&1EB|-bnR)DQL*II ztL!WpZ%6|JTb_OC_DBEITH#>7?EGQXIP_EzLa#8~Ks*(@%TWLvV0UB+g)r0wj`KM$o!=vJ z?_yxH-Qg!?SR{Slg(OVMZ!ccN>3exT^DK-oQX&6)wJASnd`l|s=;&xvD9gq9lB`g1 z7i2l;*E={%ODq80(N-3zn6m-|z#m_2QdN%VF}U;y&CYTKpf(u~~&%FFkP zQAtZn%Z-`3u8JkCXJRr{sG5b*tQ|2*Rq2p5U`lT)DYw0(_`+4JTT z|7)AhOEz5MRldHiDYDoFDzvL9-;0P}!jB$;`nqCx$oFlooX4f-Zy75@HUT9O_@va+ z?!pKI?f)`Z3_YPF>f#jtA_B0H$bl{ttwCF+?L|7hr);XeGGYjMzg13bp__%c^fioa zS4hb15EGLOCo9pM1Gn`xCOk7(tMnQAk?QK@aPO~5g4c{y4Cx_!JAnc2pse2b4qNQ_ zDW?_|K|45z0PJ)yi^JjYiU{420i8+&5t-Bs0!f^=c>4HtK27n2uN;R-wlv$bkxrFz zSTqcF#pgW_dB7&Mn2U08;pO9jCoL4i;#%{W|1|f>h}P1!m>C_VwoNYB+ypB|N~+Wp zw$~99%o)e{s7&~y18^|TTH`H^&k|UZ)&~bma9VNkDFHzUhV&?K zv>Fi>TC(|hEGMEMWIee3s*z;kSzV!x=dbr**A1Lh=1y`gVp$VR%Sd~{9LpSwT619$ zY4dwVUOAFan;VA*4iTNv&0;vpk^EMqsYU2dg6C|lMQ*MMnJqEva^f>sEhgL6e66Lq z9Km!BsMjZw^Dn3(_YdKFO|<4L>B^wunwygF8 zi5Q&dZtX z{>@^U3sKwGLqlr^2~RB-vWE?fsxfxiHD+LX{fx(Vqe_j0za^5?Gwm_x8tCZgf5rqq zNQ%(sioCK3_5Kx2G){2ow5;Z0{{XCSXTsFoJ6_H!D&W6^J1HaK`gW%bM5hLet_aA(OE9c(UIC6LNjco2Ks@{rrUB{%ihkDsu#agC7~*2$z$*$)(dS zAh@(XZ}ipflRYvF?_z9b_EI&Hhj@eBoRBQDSdT|BF`dV)x|Cw3y!?H9{9!Sq$Hz|@ zGZ`7t(2h7Dgn%GiuB)K#PJ^hLS7aD-{5g?_#TW8ztkaA66H!;rE`(0bc&%Zn`Jh^Y zI3K=%dQe|imPtI@Kv||+eh0B#LDdD|+e){iI-Img&;DI8O&xe5mg%GC=ilgHl280` z1l_Er=5{4};R5)O?uQ)ZB@b?wgC-~I=}nI-Qi3~;=w0%G!eGfMP~VTl)_o>Q;5mCq zL(|bFTQ6YMa9auo*a%2Z@K{yf&dl^lj*21COZyGT7nEUwx$$U&?ET9`tVz_L3i?F{ z`J_$MheJd=P1BOk-JvC|x=&B#Jc6z*++{qyLUiXamHU;IMErC<8wwaIUWTOg#{c6U zAwIUm*ZTRrlpq-KzbN z>{@&7x#k#i%`tx1k&5z?h;X=YARr)!(o$l|ARrJZARwTvFkrwt&JMxBARs6p(qh7@ z9$Dwvkh-c9*dvU@5imhUfdxh_U~ca2d>uJ48|k;Zuv0a0-mOCHFpw5dMAeNr8!_e|mIzbd)V6GBUF6%9=zA800&QgBFM|h$AsM zZlM^*=@w~WVSzCOFoFK7fGjE72U@-{a(G~#28r>kC*?JA^}ax)W{}hRaMGL3JX|* zBI0k~e*8J6fOk>H>Hd5Ig%vJTfej*7#u}E8I6N#Kj&zWY{?Ggp(3n|RWC}azW^85F zkwf_-$$^CuixOQK!cGK9garPfPA2ji1KZ$3_>P*e)sqwg%!!GPw6I{HpRAzJUrc;N zo{~up@(11A&&+fgb3QygJguLZu#v$gpg?dyZ|GTQ?yNmjRlk|2CZq6Lg8tMjmP=aw z#)BZOp_LchkMhOaJ7YxxSSX=9MC4~&LNwfbr<#;sZECA*iK94X#b2GoMZEuWPdK>o zkbMRsJwC81ZJc^M@R=^DELwR3_!hR9Q22N3=b8UvhJ&Cq%S;q3TF5@k$}VS`xH+th zMnP?h%lf{1mO%G!?resGo3&N!shR)oN`15pXD z>3Q-Okn>cxnZ_hMU?IAQ{}W9MA4K>#`O#}UOw5=8a=a${xh6Fn3vogqpuu2YDO|}C z$hVoK@v7aEFqVU^jDCqm$V{i0azn^c|Bk3t~&y%~rFLUgS|RU>5BdM!qw z_rU*LKJ$cI983oTn^KOY&2g?xjlfJ!{x1c7lu{9BA0AtrxUlnkeg03vAZANMNsJ8_ zTCLPtV^RfU-2hiIu@xA*;1G}tK6U&>X2>G9mEFrc)v!+}Z7%ve%D zv>{5zxnlgUg%xzMoa~t}2~0)oNQDdjw~lY}$Fr}L|L*xZGv9Qe`g4{4zdPvd;s3?< z`2TqK{r_xuX5!HA!{_4RaXE=LS%Th9>2vZy`A(lZg&W>T;y%$9n?=IUPQM^FJhz`) z?LQL|V^U~SvYAagg-`~G#-gz|-T(4?eDtpk4ssc6uFK&qu`)BGe&8q<&`CK-v7tba z?DSwFLKQLjAR@lvVtrkCqQAkDU2pewKl%$!7n7AC;F*<>kZ_fYhK4rx>lGwb!1Lv8 z7M!=fO^N>w%atx8m++sgUxAhq*A|G;mROa(V0Y7~EtBSw9GpF46iUoV%jY~GUi z{_9)+E|(!5^3(Zp^$H2h9R;J?MLqb`%3c5BrrH2iln?U8G{}?6%Hg;#q;Iju^thYd z-h%c!aO6=~49jhM24L6bq6IrGJA&AV)Miq1XPkk@$+vE}k~mum z6-A#EA|fNNecmu&@vP7Akyb(s`cE3Lhe$!$N;S^noEXU?G_izCKC0n@H+`Pb`fs3V zps5kSBB9U_r6-AKaS>F%Qz^F!Rh`zg7aB7R&~N?Hl@ZZ(BvU+)Ie^$0%bE03?8yo0 zM{s!j5DlD3({U$6__7l=xSSBI-C{$EBDM8VR zK}BNi_?T)3`K$P7(0+iJsIqWDpp^E9=B!~JoM@zxc)Ob&Ee=PSO6I%$-pzY>oTQ|< z@WQ|&x(lUq@sD#kASTi@KqgFOcOYvPDAX`$u{4mx9`+M`i;9OF1C0dEE3PnNTq+sm zg3I-mjX(@e@9oe~XrL_`P@h@v1;Ykr4;h2{1+zZ(cw00Y}xT`{Fr zX*bE%UYMLzso;CcKS}~}wA1J0?BXJ6xSurBcQFX^X7`EBQnl3LiK)eI>+$B{`sU{P z;bGz@wJ70;Zfqfi}h;jN5vB8D-yov&cXYIj>*`rcr-Sx?)a~wvNAiO z_l;NNaHQNTuM>2fCxPIqm|)T7j0GBOYF(iL-%I|hPOpnY*SIyMS*41!sV<+3;S1hJ z$8S}*DSBD5Ds8a!sRpv-NRX>8~z#i9GHkBhKCRpM=5^AD{R4w>P=T-{%+g z1$?Sh8rm#qN~(2H(&0pEJtk-Et0@t{et`-@P+z%wM3!s6Oi9cAi2CbUXd=e3$pOJT=0pa zFL)eJ@!?3&v>HtQ&XvVHRR;9Bml81g`fPR~iqIx=DpXsk(d>_>h|VM|ShoP1RbqD` z6XfmnTIH&;O?J83%hkGfd@2qB9$wIG_jho3+*T)S#EV_f-0`_?fekgK@d{p*eg`$r zAuk~;;zl8*g8f$a=l(UBI)zeP$QI1}AO2UQJp#c_WE$t{<%E^q@+{XJw6}13pEA<5{Q=uwL$_U z($Y?|#CGyZPtLrD7K%O1uHVO?E*r%)AZ^P$ivI zRsl(;TB*pvOmB|&~Q|<@#}ccC2V+Hwr;o8-W(+B z&7ms>QDmontH)&Ez{oy!I;;P4-UmNLFz$8W2IaK+3D}gKJ*Q(o40Z|H`Sf(IsM}jZ z+@-h{1}#E&_lN%@;g6b$mi2DzH5LY)rf#>%^1?7=loo60(ZvgIh@lyt+xf%7jpCb& zB|f_ygLSXrWXDy7WZ|Oww z8uqF(p~fTAwcxAG>Ku8csW!6jUhGQzn(#v_t)}K@SaQTFE72b7owt`C1H0hMZSnrP zG3Xml8zeYeD^U&DttQYO|nmrE3RE`uy=Cm~( zLU-Q7kdA)tN3TVgnjnRrDOZjHz*PdBbyYrQzV z&kPcwfoTtJb~;=i%8AQ~;v`i_t<>c7sipPW4`Rq0$&*7Oe}W~}U6DmR-ICRZ)C3Lt z`eWqw`WjoqmZVxt7$>^n%%AroyTfp0Iv9fjE^f8U`t`3KZz-EZDLCYlx%Jm}vJUD` zhtXw$cgZW3FYo*h^hku<=^FxPSz1YRYnmfHhf2cCEpP=cE8~b#B`mQ`O8Fx7;6KM` zevw&P+HbbSJ&?p1Pu*8w|B|J|oPOe|1gAO7^$%USO`XhSFSGe#-aPcVVD0#9xmc7V zHaB46aJWv9D#057Y~EVwdlv#4y9a)|4O7_Uw`PC#JxB#ofeeo7CHol*KT;lL5#^+i z0%1MpD7eigmCO&{j@vVf>J+aCDD~)sqpA$fznl)qHJ`M8OkVaL0g-#*g?NL_ zPUrJnk6#2oyBYC$9o-%`MOjN?x0-JaY+#ghW9C$R~aOXr@lo|zGgN%oQ*0qQxw@=Gvjej4~-eGD>MFXq0i z?V4H-k_Ps`2<2WrjX5X2_J_>aUs-Ij4%{|C!RWjo;$KiEDx(=9Z(pFwOWT?a&z*7D zphrV!-nUZ2u+&@zBv4h{RP_Dq!xOtcE1do8I3i9dK_Dnt!{7cd7Qh`C3GtI}sARj& zp!3S?L;}&eVX$vtRY4?%3xrm&kSAvmCppvx0h)BlS(^E5|>Z)7tu6or3(kya3{*`!|i-@zgZX|doU z?$C_MnqYym2bgoPvzuDJiv@EeDuG3Y8Ia*(F=$aNV$C=j!D48)NH@e1F6|INUWkFW zTN&B*F-#~JO#Yf!(x}>~ycz$OXGN;W!8I9#cml9AXg1>K)AXOOl9hsas@y4bP`Tji z-9BWi*vCF)%0p(MViS_Q$GfWQYa>L(rrnOuQ88!gj41QL+Oriksy2Bu9@JU+2y6^j zUU6TzrKAV}fMytBsN$ErEmVs8KrsPRIt*N>>l4Zn#AI%%D_uPV&U%kV_Uc0V2ntfA zl5q*KMsANso9|I~&IWJPv{6@jU^V~tcZ}EK*>%>Yr@Iw+Bf-kNQMd>81o2RKX-S^C zVbdvuM>td2zO$FV@8JWI@(lXjSa`GJOEo%$MxiYJ?zVA6r$5>ian{@~Hr{&tA;ha3 z@OUdlxUrYvcT^62yJz=GOd6YZzE(#>*awHj7*jyguLgYZM`O@{FjDG!o2G2 z?`DnYeQl2k0FBh4NU)?`wAoKzsq>{Z0~J9A7l%}a(D1vus`JsKd3bsE`rK%g?)vt{ zwkOQ5>(tSDH<{Z5!~_>VnxqFRF!bY1Ac(#w}}d7)Cv`( z(l-bm&=L;6zFa|#8gy%eaWLXeZ1e%W`g>9b(@?#A1Hmm zI+0igng8NcgG<??-=qe;a^S*{l}L4n<>z^1}P}1LdtDfqUtJ z=|roAU@y7wEXtBv^u6}mMw=ec_pzGSzvDjKKWu~py$!<0+ribW$|zB54!MTfun6{g+sM~WfwNh1u1SN<5wH)eS9G&kLhI%b=tg0v zRg+DD`jKQlHK8!*ezN#0yXZ}rt8VZkSh-`U4&7X{ASENfJ!xHHzFa9ZrhO`lnv7wy zUW=^R`s`stL?k36#KHM8IkNCR6vxw<%({4xa8hVZo_53T=_^( zhP;c-5}YYvSM@=(nPUPm)vzZkE9{HJ?Ck8Sfnpqfynj$pXd0CTJmTp>P%|-=m!QK< zY)k_fos9A;Ki^5EnlNE|41qRKultChC#eGAC*<^Ea%T5fZ*_h@BzSHEfBAUVs3_iL zvfGup#tW!%*qQH6lKcpyq1gQUd<-j_SIA`kG5siB0!p#pfAX#|ML@`s$GL?B%j526 z!->j5s7KmOP^{~L`IKue)_`qDAx?r<;Hv<8rH~!V4BO zd@`Hc@$>KGF$xBwx|9?L8X4|9dr2!24cMwx^I6a+`G}GhEjk&lnS#4JjnWVH5(|f! zJ@Z9fpAk{VTdX?|Y{Y^c|26jpJM}ge2F2<;dYGs=@s$Oj6j5vEvz8$_V~3E!{KtP9;2YtEmAINtS}c$+%h|1!iMEhdgHYB6e^u$` zpV#(P+hD)>&EW4Jm7iBx691w>m`OFHsSU_tQ$Wm=o9@|LX=5~qMAVFKZ!tRvT3eDQ z5Nve5hG89q_>xPMY+U`J=NppuI_b6{ZoA=dw;rDRyMlO{)b6A?2EB4TH{$HW^V8Gy#b`CZcZ%`1S^Wux zkiZ^kvJ>qvpojfV$oBK4+Xohv&FQ2bS~jm;=4*T4CVzV^bg*!!AddTeEdJMQtDrE6 z27}QyF~{e|h9AvehyyvQbilw9P15vI>FBPdVpMT3DLJ^Pt}rKPt0Xz?qwqb)_j6~Gw4!!4*ns16)WiX#^oTjlAXcwJ=w&G2LBKE zu8k$On(MsPVkyl^KkblxZ>v)9HKyXB(nJ{Y3GF82A3E8rSiyPk{aU};jb3vYf*R+M zo>puT#rcFsq9BN&0;ye<*23+zmeGL`h3kvSOb#I;cp_XgzV15z+`sGOKXiU$tPtn7 z_Xg092KZVX%yF|>tN2&bEa`8MpJ5fU zZWET&W&QXZy7$0tvq14bkVcn~IfUJYcS~fyG}5F_v65r3Ss-Pay$^X0h<&gXS%Y5H@p%%j+`dLD~bJ0eb9 zgjK0S*m@Mr;i*Z6#2f-XXz)%|4p zz&s)ExwtVg(ZGDg*q}>K1q41u6tYe6dVE#BBMJNMnjWm_WknfuGL2SjW=6xSiRze(W{VUp1lEw`p<02XxwQPEiwlE@ zXqt=6z}SWlMzLEywnsU4G?pva!smuxgV6(eb$QFiLwd|m8w3jAiS>hpBiHS^Xs^cx z^qEo6Wdh!Zp{s%27c4S3ZrlNBY8u01WWp zrbUiN(&m~$QNtODftZo`N3~43EIO+&U;UI47Y=*8#>jYG(Y};Ov4I_43qV(F8qX7E z#?-$EOPQ`s1pY=t6Y>6E54`n)omupQI|K|u#P*hD)A@EicUQ73?CIRpZ86kMHCp|~ z_;H+k3Rx_EHPh+Ug9 z-?$Eu(a4rj>SJbA>|E$wIhl4hxlp==7yrN#RL}THj9DAEd!3pFIa?^J{=S}VM#%5CI^o*N z=YLfU0G@x?F}~Fw2KYsUH&8m5wBC%SPU5DxQJ3BP1j8jz|8WIl>iauh|3Z~J`B&|g z=Je^~Pn^Aw7k{3V&8>hM1OM(CS03vzecB3-7zB5eg)+SuT7)1{F#e1(&KHpINN}SG>n$0ebt>cCVIfAQ z{lF!j%_CZEsrZhH`t3Uot5aTkTV>*xqw*Sbce{3%@{SC3$F+fhCQKb%sWLe5$9vY9 z?g&|(7lgWdd_SMaX2p6BrP2w-A-MgCwY^%RvHxNL<-YDI8etbl%YE5byaw$~?^9jj zH_s9PPFto|k>Np@i6d?Crctw%>@-WMd+je9)*3c9q7V)m3OiRR4K4*KT$h7X)E_E4 zzN}KaU&iX#=Ud~{(Ne>*kC91K0J?@C(-5^)u|yN2>@#B$hA?gqUBkcLU;4)MEvh7n z#gJ^asi(>L5tIQ9;JBc|8Gjo(7-(u~x^K8)QEJnKqDm==LL^`bbJErAuc`VbqD@O{m^#HMP#v*L|!|Q=h_2tqcHtueCsJ zDpY~9$|uF8gXJiB$q%=c zU{orUM4&oQRo+V&H~(Y*!WHhmpx*_n>M10Y=cH+txGy22hC|5bL#~eVmp|fYbKC-N zZOr?IZIf#wMfUKDDm;S{7Z(rjVDfCVb68Q?eygp=asml?c4oo*k#B(-A)R&P%wV(Q za;0)?+5+!5t?^{$&*ml)3;2ad8_U!f_9zAORSU}W6l&7;m0}7&fn==E$L8|!`b3)=m}Bke8+(#8EYm;i`&4Qm#5@IV?5PXc4d(^wHyv zEZ6fG7&p^*IbVUk#A;4z+Ak$A${=M*&3-j)o@(_IP!93Ku-b~LUi4% zkt)j3y2M3yDGJR#sAhXT9`(i9zyyrt@H`-fj*l;UB-pO1Gq?-`a0Gjn=n|M6^&ll= zViMVW7fA{Gsp#l@UMfR(yX58TZXr55{oT$LXNH(()u3R7V>=Xb{&dT3DPx|_TRts` zPHR#d+2yp0JkRY7BVZDhk>JzdCa@ILK{FAqcfYc#o_z$5bawi#1oZ(pgnaA)9{|sa z!`_y80xrj_M7e4MKYyC z+-@{(b+I!wB4~lt$d@y{DB({o=`7q^Q{dJiQ$!(2ZCwJlHP<78*+M z``u)5IMyLxO-`PyxnI&QS+Lk7zf#i&VMNY~vYoGZYudeUH18Rtma6|McuN+Gg5opj zYCYiks*pP!jUwoG-%cO&k9NO{|G{%D83_f@VUVGOFgl4Q*tnjElrmzXZBFAm&gN6P z$?h?=2mWi1$GAM|teq8Zw+~(BEWVfze|@2oKWMQ_$ba%@Dq})nZ1t=@{sfqUH*s3+ z#*?o|0h2m!oo)W|%i=2{!#lkBdI4NSX99YDlRv(&=Ak(?x&D4CXD z_bDRJ$PDSx9?|hN+l~i1R|Y}nzjFn@DoTF;oNfVlTw++gc4>GXDn5kgV@DNoxVgis zj=YEUZg(Pi6TQs1u=`{rF2Z$f{7YRVfjh32lil`knhOr;HadwU3D*?_ZW1@9Fc!n0 z5_7%*@SF5W=tSA3gItARPxi=Aj%in{&k@-BYY?#X?lb!pw zjWOJj48<%eO7}d%y1^x6+HoxyhTEobxLkBMhHaa2FXD+O_rfMb4aAqkI?vuhX-oD7 zZP}HeHNZQS-$;_F)WzPqYlH7jIDJM0Ul%LaR=buA9qVk?+S^*?WRr$Fwf1rmF_1GL zsL8%quU3vJq=B8fZUq*`67t4Cys+`VAc+@(DhvT!ltf%EptzEes*xYw+?D(M(aah!)NC)+rR-DtMift>`8ZVj(>}}F08hlPf`K8K46Cpy8 zu6nqfgMKoTLDymB;(>ihDxMrt7=Wawr{`_9A4;1ILnGA(%?YkHNs&2V*M)vK0YK&D zjOZYz>{x!AYe8mTZ#PVYpP<$JxqO~}w^P3RqJ@V9r;FA4?aAmgy#T|oVyLpc+L2kE zHkrAE%Kk68*jdcGAkuN|GP&3LB|7y2LXP-QjJE5 z#2A`*SJ%6I7SBY3-Jqfd$?SZYo-M#82a(3s=E1y?+M5H-xI6da2^-;BrXS6R(HRB{rjeftygZ`{`njSq#k-9Nzxqnmec6%9#A+v%6%9(&t^F z*Fsb)@fk^z)fPdjJVtr1d!I)2#{PtyJPzk?eYQCXAc0~0q2gg^Q>RYmtG3o!f7$cd zZQMr?5vwWQX?3_;Gp;O`3fn$ssUF*-=|~qsxyIu257oeqRLV5WZ+~>5ajWn5h?_cV zw<><*#SxM0Y1Uv@nXYk6x^&eO7Jy>CzakSZl&ZsS6i;Z_JA?}XVAw8+gftcI@~)Rn z$|L=n+a4uS07>jX1w|w(qPC1g*Q^Aa&2qgnfIUjNT+Ev(e(K{^n~fIq4YiT$T)p=0 z7z=d!xmu|OKX~sg>bWLOEkr_5Sw>_3KAxY%Tze$qIlcUBmpO1cj_~`qIarQl)qOl( z>!^r#sjXgJ$V3L)>^|ExXYwiug~#@Ir`eucINOYJlu`Ui*%mivEYY2XQO1-f5aIn` z#N%!;xhFJ$!c}UZkkVJBSf}!fB_T74x6w9`P92+BPmzsCg))Yjl@*h1u1dCpF+U%V z^HR~hH>7T0p5D}uc$?s8Ivv3lr!8a-xrE+t7kqsf;Q%$mwLtt2)SumTE(>;;d_~v3jyne$*IY zp|{0SU}%M=x(!BzxQ>ffx6OERn+2YKhC8Nbq`#M{72X7jj_gDaYE{A>)?N{x%D+{Z zCf#D!6r?rS9`!s$x+CJvb4rWcX_v@`KP03*A5P+q$6eoR#0H0W{PAIL{5b&KQD2R= zDjDf1flp>2`*L?&|Ja*AJ_hPCpURkVz(eJBErwy~$SlPC>i)UYQXh)p?RI%AK;fq6 zOojQarc|k`zRZ%wMNyHr{qai_AzC|3t#$Pl#--SuMk_N*+3{M1wc)n6dVgFtSG2+1 zV=hacL9QJDz$|`PRRS4_kj}DNt=E<(`}lDhRG0TvK7&cbE=}A3!Zy0dhOLyAy-CFX zC-bE3{7-4l%Tj{Pu*2#LgUL{gSOEH(^IWKJdx4yrm1bkASZ9yZp^Kt|zIZNzB^%O7 zRA=iYhFKYbhIbGS=4z7m`Tam+RB1AA_+H28XX$ziKXbXlt0nU8=k*a|*`z+idz zx!P>U;rMo?x3*aR!@s|$dmPrkj=RmI*~11LTyFDjY)@1BV4GnL;Y+ivKDDO}Fn#*O z6k&gXfN}+pdoH`0s14vQ4^B?i)aY3IDOFQw3wZ|fe*#*jYCUF!o3X3k4sxwrm>L2S zB7aDq&z4t=>I`<^3R=8lR5PUYIMz1p-mz zM7QPIqOvTFb|%2R(d7uz3pWHosSeu?dbh<8zsTTo6(*A$)bOdQm2I9Dxx?FmQtwvT~4#jhQSNx zW=H>+v{IjA+V{;t09~WuNeW@uF<1A1JxPqM)LNc|uGwB|NY69KttqiE$>MmX8q=z} zh@tVd;`#7i_CWT4yvicroVVVecN{@i8AYNp6STx-_y^5lij`gZJr?4s?63(YA|C|I zofQcQkb*)G2t;!V>nZLGNx$GGPrDrB&dM4h)P`L zYs^kGk8>XTWcG=^ker7)e+E9;7!_?wNZ5OnL-QWx?`>o7nO8;?9=BMhOm#VS1rB7N zR{<`r>%_R!o1=>@DN|!9-+X) zI|EE(WaQFUsvjNfXH6IwzD?ktc1l{|J6YeW^Vuhk8tVF=X7u$p)lcXeR@ScLO=$YU zUs3LuX=re7bUv!Cxd99_EL>7kM@RT#(fB$U;A%RO;|T1c&vFHtB}<&IZ2(yIa?^kG zCo_0dK|2@WBcX@e1+${!&xDzH*r@!1xZF-5*p`L~Fg%Gl26QCe#duUZsYbh5lk-yH z8}Rl=+l4?VGO;00;=I++=OOeENi3^$fcZJhe; zOH8BnYlCSDQdnK%jO_)XXe#6?I_fo|YrLF8NHfS%lC&~oaBt>20IvgJ_uSqKwFq17 zA;z<%aW;bvM~4`LdR2^t0|8f5jA5JKHI9o@18BL0TCG)+2@haolVv@DwV$LrCM7a`o!pVbrcMoATeZ$ z9}ix$O<8MF1kAQ>{J?{vSCmrgE5Go}Ag@V_!kb7{=>Tr|^(!*b$Ma)=;c_oKoqNjh zWR5>_omd4{OjFI8wx5OY^$xiJ1#^ZVG_+jrFg-8sd{sJan#;9(_h7q8nj}TzJns_XQ)8R7iY)7 zb4m1*gWyN{fyC|1v;D|(Re04=*hM!(h_MHVkkN>kguu0%S#hOM3rMbOs3nkRLGYp; z=&ceXpnjsdpTD%&*Vs8-L?D~amf7x17w1~c=>N}3zBI8cf1^+nwq!RPEdb3`qwLZU ztrKoxl^zbWRLEen#CJYKNsLLxrHGWu@5LP)(dDqxlw^|f@p%IpwPv&TMFF7EC>ZY- z8Y<~O;|v{`0TfUec&>=yWir2B0JW}7mR;x#4?+SZOwddY1_>_EgfG`>y#f^zG6A~U9x4jEKsz)`1$=O_RrM!;pn2r3~wG(Z@F};y|%r z&_qt4#Kk(v7^yO=Y6ql>U+wKihy?w5TY8VqQ|a{Co;&+`KrY0=GuOfp#%j!x`K~j+ z3&Dv3EX_6g2h$AtM*H`+s}ASOZb~9;VyZ(k>0*j67*|pZ8Jw=?Pi4Qmovv@#-v*(D z$iL;XFo=gscKQs7;)B-OPgkG%4CV^@kHv2e1JATVW0n^l*q~eUaWGhNT!i>6Zh};X zjGaS)TR`JlibyG+%}bJnPBhalj_$f~mK+4AFu6fy6-(5c$g}Dda9F542%tzI10RZh z$d1+Q?0Di6=w(H-TPaF9qX)aSNi;40MA$V{?@0s%Crg#O4m)#A1Ck0}o}LZT;h}K- ziHRK+31IXY?B8y*5{>58mTQ+3x&_+hybe|yL*Wi9^qUf)a#`@{+NUnQ0wIRd6AxC2 zG-5j5#z9Z|EKZ6tbDo|;^hU<-c6(n0ECw_9hDMC>jP@UIZ4+si=lgDCquYRg!XvqI)(7&Hv|gNyIeP%WyaL zH3f}C`R8_uw4@;*Z9H~i`sZ;{W@26V-Wo3t9sM7#7OA+ARe5y3q0B!M*PwkwdO8DK z@R$EMz5LOz4$GQ!nc>2B4F6-!x~OfGDy*Vo;u!7=(*KWrC4p2rWt=?wR~v_FP1ALDFdI>$kqCWT{D_be~5+MaY!PcGXd>HL~<~&M|ou7 zZ|BXv@1V}k3M!(9YSw@;)k_&yy9@o;LQ8}kLmUhGgh+%8$%}_Psj8Ye5BEcii;9Z; z5rt-CObUwrRG>onV*PTB)pm26W^=U_8xYZ9XgOG!nv)Ci&>tz7(BTZZX@ zpn@_hP6f+H{l8}uHs((XpNCTk(*010at_fgz0qj9TQJwaTX-PHQt#ETXupJ*tc)rq(o4} z#zTQ-kZNCRU~IU z+oGY7Y=3!Sq^@7Lk6ir6&rmG~KQ^OW`??jZ&*=LjCI%Y$m>$+E)V$UA zNI?JSKsJ|7tA2mt?08oLrl6pp z#&g~0W`8KjeEQn3)i`Ip&1#|4D&GthJ^N;VB#wy6mGg4__SXCU+SsA^TQlVCQI$bW zYUjbb`E+2~X7~Td>LbA%p36suWS4+i>I-+J0bm|^{{HkrDW5yC5G3f^xA8xce78lq zbhggC2S}Z%&d`a%LSv`>(apa#hr1W;W*IV#m6EVGf8U{fP%*Z9dmq2p*y7{YFI1|x zN$r5qZ2d9}>;xQFk~z9vZs_b3G(1-Kb8Trg8-~Fy)(fTQySmDL(Jhyo_UoPCkw8VY z(b0sx>t8c7GxNus(M!PV@ZcbcStShY`G(C_d5c5T%kSkH-5e+(J^t%5abQ3*mEqN> z(M}*L_O(RqnfQO&VaTIou8l%v+mcnkNGiOZ;c=0}57tQ62bh44fk6RVR16De#|f4Y z1NTAhi6U=w&>VHBNi6G-dJe%a9C3-YJOU6?Exo}Fc!wH=*$RkWCA8dQLo9H zXTaeE=-2)Ic40s2I$+*#^q2`$MMjjHwNoCB}fxW&ZW=i2q0G&#?w-p*tKf~_Eikk_<%%PgFB-t(`WgwhPEQfidg^$HTcxH(ZJ*MasZ4} z5aPlLSFTo^7L(mgPp+yrAEGm(%t=at6l>Q2tAO{sRE1I#nzs|P$H*6zVQG^p`K1=1%zrjdgfx=E8U;+d^+FWn z@Ho`kSU8JCN1?LnkrZNtBUyBTVZF9Cb?5pbC0A$tlbTFNybHGj_+}0W5Mk}}t$W!T*0LkcPk%%l-OW3ZBR*T~r zpf`u?3Sc+7d!qg(vdvqq$r7z$Y_c(fitb@L0B<%{$(GtH7NS0_fvN^lGeCJ&=0l*H zr}IC7RuiQ?h=61VNj($S?yU9$UBd_LU< zpC-CIK?JKH0611YC}R~&3)qn9^qVEhC&m_Jjcwq8b=Nzo<+3mc_V(= z8HS^;Caz9FFPYl#zoL>gZ@dn1`L);KUMcA6;(ke(y#hLl z^fxJVCtoMcjgH^?uTd(O_s31Ta~g#BB7(z3{td%u4dA-S5o|&Z`(lyKUan@e2JmMS zae?*SMw1e$gzG&^leCl^m0BTtapzl8QD0Zg-7?!u_4Ac7gb2_{H2>WZ$f7I&Q>h1(q$35GJIg~?4?(njRNsKg)Z_%f zMs)eQh-y3!)^}F^P}au0A`yndE6kuSqYbwyNDg6P3T^@)#Q_lnR9JjQ=2+M>wGCEqSI$ZC` zV@Hm#Fb*7UKR-}0AYBhtIWcCd@KOsywxyG`9FLEycprg$C^kF`k^i53prW_Pdv%15ARmW`xtM>m2MH~V* zK^n5nusp@`d{@Wsf$AgBY(4dMP}@s#?Y{I-nqN9^!)voyIn``m=<@eWLwh;g0gv@= zHMFpmkucBTvS)<>r*1Gv{c_Gi5hyRTdSgknh~w!{fHdhzfeb5P5)wV2*#@M;dRZxZ zb=$vA0rYQGQuunI#qLv!!Q1`v_iuTN9}EW95fM)u0CfeIL>a4m#HZKc)D3h2za7DO zM=%~|zX^-|LHY7!D0a-u9{Q8uEoT7g{$j2S@$qKabdt}>!#`ER6YUN#>)@$l?YN0e zj8@0_&wl%X$KEPE;AMfToS6aJ4!#Fh-k3K^E4_zQ=KQg%MluyK#Nh-E`*%4{pC0Qw zxK?-=$U)|XQI6e3KS-)b$(~f5?8Nhf{s=NrG z*aPYZDU+$>1Wz-yZX|S(-6f@w(C8)20?C%x^-D`*+Y1gen47I84bJNTZN1wf;|_KD z435EQp4H85Xh zoC(P%6xYLh_2&Qgp5=Op6aU|y2P)D3JgY-KRk3iQ`W+5Vg{6s&ZJAp%=6)qPa@GSZ;ub=oSG7IOF)^mLmLtkty&8*D24KAK&PUHu42*zZD z8I^Jt-jhEUm#gzX%(?BNiT5U1vVS2Cd^|TRyf*!lV+w8`(`$wE@$6*TFJ)pnDQW2re@N))PSkib zSO2^TOE82N5EAUCm8+F@{k^0?cC60378&cl{uPA$c|YELG#N5THB`>f;QSsT#Y4Um zNg>(wkutBEyd9xnxDrl_+iuO02$*wb8geb8gL*3!r}q=HZ>><;YA=$>F-Za& zvov~(AohG53KQyCT7vDB7%wLB1$o2_E7|kAfcURVkmsgE*CU+_4p{z3lUv(z+m3HR zR(NK#q%r%&QhlXQ_^O+cIS(uhvqJM%-3hq;-XWFicetE3zx$b445*-aMHAVy1VB~b zQ+w)W@)mFY8Z^t(h4M-dp7jtPU=M^$4mNJZ6_hadcAgURx6K7l6NGR#Ucc06a{a&! zKXY;JlpPZ8{$iEk#kjKWVhyVqgwp!4x)-x*|A302oTq7~!^E8lPsNSRtSrvyzCqfL z7q+-^Wdx0CoYzhe@lpNLUFkW9dz23FQW1(n9rGdYB7 za;wD3J?CSO-0-)xOa47CxGc`F#4Amv!*Nk^-UbxGYpu=W^qpS1RXOa_+}cueU+k>X zatyt}IGbU&Rb_9Me)xpJLrG^gO@x+x=8vI)#)g-)VE6m8%XCH_n{h0T!`bfX1h4~* zsFg~5BPISSS?Qr4S|!(RDruSNl9Nv;y7yIak%|U0iAW_5rkb8*7o2<72prn_^FA){ z+G8RoCR_dWc^m`d-!6L*3o84oY+3|KOo~L?$Int#pHRU!fTx7#spD~?!C%hPj!Ov( z*VUn!#G8*2hC`ngEh8Et+->Duv{Ebikb9q%0EFo^M!ySffXcXE|E@iiFZZ0Pm&Vc} zEGsYP)B2^DS@uOC=$nkSC7VvtC&P=`uU^3Q<_zLSJ_8B~*^pEDE3IRGSEf~E)hHJb zQb7zaTV{bKc+3r{+iw7}4g0cNey4mgnaafW=gg|3!!$u@*kC9?rIbMyu#o?^__$ko?=G_jVD=u$Y_dN|7W0;tXAi5;;3Mogqxgp-_z1Ne(C=H10k@`vfwXhfXCzV{NlUTp$ z7kN<2=*BqTJwS|+MDx4vN$5_XG|vXRP<>bJ@H@AP z6S6p9`k_0(wlM?7S7=ebq49|OMhLKAOybKz$T0QNYG5*m)CK{ybGIUAF*dLHix{Tdq*rt_MksH5^ zwL`^}$dpva98IG#HQz9ss=D2TbAdd!?&BTsVU%5cnc;A_8w{Ef_3vOF*cR(XXmrCR z+U1+3>JN6j9gp*Yj7Zz>?akt|%E{-SamL*v@ zes*c~Xtl;ZnXV8*Lwv-+pLu zMY04i1GV$c1!1M}&CC#sm1px^y)@Sk*M%!Jr!DcdRea!i^L*gQRzvjdOq2Q7*Zb@z z+_KrElwW68so7s{nhBRwu{FYN5tNq5GX-|SLLV;R@w=WQDvj(4GNRi)1V*{XX5ic( zK;n2O+SqgdzGRrh)G#7y@$ZBC_pSj?ua}O;716{cyr7nQzB_zagUj(F=m&{RJAnml zn?`lHXb11q(|rH5XLTp#NCZW{=rydBsyWeZlmP6n6bG`_EiNc7`!z>fXe)C0D=*6lpT{ z!txah5%1TkSx=wRg*rA=R9tOfwlv+>^UNSW{;ZjmC0B2C+y;W{ulzy_7HW&GGTCoq zc7_r=_gEU&I7sRd7u037q0va+bqdyKrbUj#H@%(Cls{|$^C7ob^_ zZ4Uj^?CrhkDWgx*x-UXdaC3KC0jd0kg<4{UgFKv@cH#2mYonn43Hd3(d|0<0GP$bz z6hvi9#Wu}{W4`lF`C^}Vfpcmi#!tG5pR9|7DYO`|l^ic>$s?zD#u)4!?lZ>RrIeEI z6|tQsZ-VFgje5jcpKN7OS@e zd~eeGgEF(cbm0_#rYlZQU7^3C<#l)M{h5J@>EQ9z2ryRV5AIt3F1$IoY58<X3K+3v5)<;3v){i#|~HR#5t@R**}w`*E%M$==CkG z&B6FyQzv(U2uuUvgNVkv$Z+N?x$b-1A8Yp*vKuBYo;fU0tOl3agTqLdkE)i-;; z+~{Jr5u1DvpWaZBT^Hq>s zCW|4%zI$}OyepEiplmcB%QjBg>`SKPcf@qYVERVbEnzfW>xuzu#k5|UVEC>u9jR6( zj_T)JnX!f{h&o3}rEnDh{@glvW}=eM30GbvIj96hMK z)CQA{{7dsw;&GOIe!o}OHcSr4M>ETJfaTlQj(@O}y6Tt0m~;Q0jS4_J$$}fuzZkfX zN_BJpy0o-jv0Lfs2Y~phaCUeTldCyf8>kSd81ibeE7g#SGY zFe3w8C<88en&ej#?_m%p1s;gr)L!j%cZcxK;BtTc?T*80M3;K!`-#PMaMX8b9u_83 zunR_ab@romu6#)Un|IiLGni8C+a?yj%Pps$mb}SPo zALJI0H6W>9F+f7S$Osap4PA&&FQtU7pU5!uGPGb}WXexe=*8Hb*FHU~dgiT=E~fx^ z$zOYu^f3l&9xSv6)4pOF5swIwgJA@?v7U6psWku9&cgLUP(K#~{ozEfO&E>k2cu{i z-(4@pUJl$8V(i)$9u_FqHR?dX6Aek}UwESrnhm_t&HIVi5%Ph<1I4H-a&8Di+~pep zjwJwS*{0^}y{o;gbD-7t@l&R8UGFWEPh%#@v%3$9AeU?$7az_PDTkA~`s@DmWSy@v zHNCwtu44M3h&tu&V1sVrK0k8(?GpXeyOgNR8W<9sO}U{DbQ6x{;9%}5MYU0$(_r%n zSPmSK#mNh2CG7UI_RTadQ9_q`QmJ@hE5SMX$K055G%cPpYa2h(JI`YbDqkLZsveHY zQyfY+X+{2Iw`uG&$&pD(g9IKUgjnv=2(rEfmLhYDZ=d?Rprot}k&z>Kr02()!cBT+ z7MpcH?s>+>MWP-+zIWqQW0VZVxfGem2KDDwf>)%w<0PM)fS_r{hQGq8$vn}^gqd_T zO6wn;Q;4ls)ee8YS>F@Ad9GlMb6FmwDu)ZH$Ezw0>`|j?i|827L+;d8UOdl!|0)Ah zaRf?;sTw4(!IkJR=omRqVzGCpTc9a)Zw(JGq3Z*bAhmm23PZr5JUu;i130!V%XjulD@;RHM|OKK0dJL=6&={U^|cZ7zMz+c5URgx9%#7c1sTMg ze2KM+iPGW^{J9*^j}LSE*MSW zm;(C08+{7jmVTWoocShA#b>>`LQP?uy5IVB(9h}5Iz5v>FT9yqOKZ)siRr{=or;;p zVia|(B3@Zprn^w2gv8BkLC;t)I2WPbh3JC-3L|761U#6+f{sBvQW^ylqJ^fz7N$G4UTcwx|Prn5Qd`#gd@7r!f9yXy@e)%^(XSv@n9)$ zb>F5#^!}8V48+=$p-`ixU(lj&2~JztI{lznfky)n7FWSfMZ``7Tj`Dbx;<^K+4#7v zgOsn?yA;Y%^V?m6m5K-61u+A|Uda`w38T)cjG>y7O;jPe1fq93Z@IgOvIxp93^|-P zuhse{yT!o{x=hdSvJ6sQ^P{Efi%YjI2oSvJE5}}_0pvG26Tom3h3$h-1EM&cf#sI9 ze_*JXzOG!J=3`FC{uXe!BMK1Ev4p}SpEGlCgk@$1S(Sk(j6q8Ho@|CmvtssE!5;QtE98qV}^ zbGotoN=iJa(&79QOv`)Ff-TiLfLb^=B4TXrpaS?~dVm1uf%lCHsPDL(j@t*a9FiDT z_!c*JcW!TCD3mhd!5hJY2m`(`fk04}7l*fpGf5=XnU6H{iv(k{=h+O9w41d7;2aR5 z+~8q1R4AfpCHV;0(O9({Wv#837ekvIt&v>q{<~7|vpNk(K0*Yb;|3$+_=E(QJIjmN z-_NIam&#z`HVL9PcnAf)<0+jC{E#f?Q3!@2AZN2(hZFzF>83OaOG}#yN-2;Ik)VU) zf^<1x)bv*5)2XQ$h-0k@ZwDWmmutiCitk>nZzuw=-cq;_@~IwPwibKEbscYsYpl&! z-VeLP-rSJi^i}LQH~7xy{+I#DNGgl%xte2HkMx$yc>UZn1 zJPs*t1S{A-*jrcy&z}waY~E|&4Mq^r5@9Hu$o}9;`2Blxg+MyrEtKbtnmZKPDUzqw zOBESqSP#5)egI&F!;OTh{|=P<4bqV>Ke>)~t|@LNP(L?N0ZpDMPvf(3tONx@Xh1-n z^JVvxF)K*r-=e&apjN2{9SKvOip=9_vC|@#9lovf?-jKL{`VX=}ih?b#XdgTpTXcgof6gZ4b%SeRmLL-#|5+$q{_#XR@y5`d9*D za$#|x#UvGvr2=!Idcd6mgUtm@B&2fZDIucOz4jK3j&{Ho^7IA|^@|xC=QCGX*$d{d zB&n1}`?CWFdwWQlNK(6EW#(VXidpX~g_0USxvn38KvKlb&dMv3flBmqtDRvVfrxe} z4CcJ3RW$;FW>EZI{br=qXi=zzRj;$oM*@YLsBwb4^K6oPjml#UY`}cguTODQp%Pq{ z7szKYX5QT51b#56h}LVjL2>%S>Sxz(e?H-jc9W*~txZoejHCt{J)la4|!|4H$eDOF6@;Zo-oljK-A88a@0ePjdz3EMu zBYX(@4rI+9sEfkPyFjdxUnq!z#5LpFWZNgEIWp;N#bWJ<=o9>=N`} zOvUyo*=E6^Jvq%`cH4)+u7MfQWedi(S1kT=(6-gk(0JM;o#(D?{B53l9vlgpyxUGpfbMz@CO;c+<%74+(nFJEPgB7{ncJ!&uzHFR^ zGjJJ@bvoZK{}BcmHB#qGaKS_V9Lgu(I2qlwT@V zds68bN8n5y{Mmp8AN5eW=B2hpo%Y3NF5g7*VP7@l20;66gqRY+fe+XVpVMK+e@uCiD9G`0~N0hDbrB#(=?g zGL2vOORZ!gaUC+uq0aT!y>ey#!-d-xW{^&jtqYQqI8`~ZC!wO0&Sfc4nXhwl>wClJ zqt>Dqv1od@wKV}yg?(+cz2#kuAZQvASTba8`}lFXNSV&Om#uH=-Ql0qI)^;~%5+?; zr(Aw~Xd~uD1!JgGP{xRfpqPuG_-lmdc!Yn~_0SR&Rc|_^Wn-myGVXz7Ehvv{8Jq3!*aBl6FN!6zFNClBOUrc@XC|F!exlb9H11_I3;#Zhfwz`KJHWu~Gq48a1-GwA|iM(wv zYv`dpa*>3W-I2od)I{D^c5N)?SU`IOMO#vYhIKs(B(Z@oY;1K|S+|OR0hv?DgM+2< zY}ccATfWrOd)4RrwL{H*4WQ=g9=_UuBS}*ji_;Y*3ef?}>e;d&{&se`6A3~$UgLJK zPTb`V-p@Kh!~AldT9t2AQYIx;uRP$bDwM4_rLe}0;4(O<#Wkx6|?So1@bCN%{L7ye5Evnd3Iz(YW+F&MbsxUy~C z&>hLFPisU}}7i#ZOhHIUL>pae%L?OWSF& zcH@%)G7F7bU9sT#>zi1KIx~=nWqGNQv9EDi+m)zRyAw`LbzyU|Gh%Qry>mLq>?ln6 z?^%FK_2KkE#yEM&YGx0D@cLb02wSOs5I^7gOF(({6bi}4oG#!rvge-8OK-#Ytc8I= zySUZ)+seh#?!8XxE3+n@wF{)Kz}DNh39P`G0j6V`sNTW=5X!t_w_m+fxJyV%3g~cB z;ike|*GJq~&}eUld93v+6Sv`+OF=39U24x1l@XNQ0$UO znWGoJK%K;lG1>BFz(|M0sU5(+m)%bC0G-not=xLF)FKZ;By)j2&20pjDAQ0@OS37b zJEmw@u}Iz)v$%{a%42{~A_|p~&wgor@U_p}XS*ZCwbO&T8Q?91^74LG1|p&0BB6+2 z;e>&_?e|jhRWwd)3WJ3;7#S&UuRF2kDHumCyalkUj8&LG68D1Mirfam2}wyAbaeD| zCV&|XM`sLIN~%5xUx8h5>RMNl;CrDn7o5MLh*2!$ItvOi#SH$1*v0vm3IHZ$`e-x< zi~zBP^m2Yr1+2|5A&5irz^E8c2=|mhkHKnW4#*5Sk{D6*OfQGjPy7FI6F8@1VHz9P z(DR!s0fbSfHo9D7eB(HdjLd|V*ZsN{@FX(mu9Gdp@mi#^xw468I_7cM9roXzNLQo> zvROnfh~y}C)FWbJ7u_}kdHZO&ic@*~){4Xm`elE#Egxq8_(+pFOhD#81_gmoY7HiE zZt#vtFm}Asp>vZ}gRSsZOy@FWx!0l7C{nB9F*%yAcVRVFNn$SM2Eo8>i|&{)>*0jo z!=XwQt_5N$8@|jzCpJjYhMsk#DNXd_x=Ug@M`s7{y%nY6zTk_yR*;ro+AzY6=W@V`LVQt!#`)b{ z(>|O^`b{6i&^7V1M@1Uc7!lyWk>-GlqoGu2cIVS*G7{`lM}~rhh0TrZvZw4TeD?yr zxJeD=w5@k5KdPI_Xqnd^Tgbuj#Ckn?zbqbfjZ0(GTMhkldG15*j2chgNaj2>6Rcx7}JS_CpmnIAD%39R9JiP7`!lhFQ~aWu@-(wgH$v*H!tXHg$Pw~ZdyVvh#skWAle^s3 z%fopF8PpmT8wnz4!|;on1j~7_pPgj>2!CBAjG{EKaDrO)SM5Q>6w#E#D@hb`6n*7u z2plmH=>yTWvA769?+4*VNg|kU=#yh)vZQ%$$wTWbc|_9iy$A-@zaocX{(0@i+o6Z8 zso+k@E*yFg>6sS_qDk#Ut#B`Z(gcVn3c0VD*c*e=V1d(s0d*Y2%K%Ab)(Z= z`+jSiw#j`>FB$%C_o+9uAJJ~!UDpAxY0a#l5V>&Bo^m|Lgd7U>Va%mO$#+?~~0!z931+?{%)F_7I zX-53ACnBHW;-R5BfOhl==WOIYAN?ZurJ0WDGt2Qr zHW$<1>7Juz$b%E0p&RP*_FHpW|88Icqb{;FX*Mmh*x$zy$y>(*D-!C7x+RK=E}9*# z@rQ~reO@Ro0;i~2sCk39y?}QbZSv>GD$Bl~mb$8)*PQm7_D}fQj+fZ)UiofMsQnZ> zSZS)$YMNgUk&Xj`h~fCJbI?h9qO>M3n@wkx5xxo7sq3hG5%CNx`CWlx!SDIWRZ`u< zd}!D_k-rCRq`z0IG+1P2FtAY|MdL%@c~AqQ@_#84$k<##Q}7IJWyz zU-d!Qgj0=zsdo$*p}fy}tE5Rrk!bol+pz6;Y99oGkT_D{8GX!%iHHQYI+NpQT@(qL zs;a`@_QzUuBlNEAdB4wgoKnf`BY~(KlcldN50<}n{06lm z4z~k~VWMRQCsrE#UXOAkpmV0f3!;Ds)-MJloAm?^Qyfedm`_z^TL-f3^tqr|OIZ8v zb>FbE>OGSw8os-`t6tnuS7^GDL;TZsu4191%wMWP=e8cy>H%RhRdhjy(lvSjPmntj z3jb6C%nX}az|7!W5@K+1eq_4Oq}oH`k`c&B2p%8l+5_f;n51SjuIBzj^znVVMFQe> zpjKUsWFq9V7B-&5()#frXYht?um^5e2DcX5;KZ}ofMSl`Q5AMBWUmD z4ip_?q;|FJx#U@}&>8g*k>FLctBhpZ%)tFY`Xxmhw!wa}{+?vD=+jCu8aU<)n=kjq zpu4lu2}#oWx_5*rb1e1ZKaH-h&Qx0bS>wMeR05t#io##$&H z^09#Z50$jTYQ5?0R$a1DV3<>LJ9n|uzjb2#I+aNA=2ms^86T7~0b&do#e>-mkgoDM z5~7TO^O_dsz1^<~K=Y_dL!{n#g7U~Af?@!kF)&>A-t^PqJ&PdF@koagrNCt+Hn=Zt zIwR9G*r51JiTRu=qMlvO;EMG#<*eY4+dg{gnUoDk8Bk3?pSOG1th2$ac6oZViiP{7 z3(DI%4d#=g`Dz3+Kzi=!XHN1I(1zy)7W(-=*>I1ScQ$F8Q~^ip3ZzFDVdS64E1%lhjMA4r`}1c{I!O1s`o;Xf88 z+Bw{lEl}MWXk$5SFADbk24_vERlqvcz7TK|2oY^etco0k_~Zxs_`L!iwGTRr%}%(2 zTVPzpw?3MGf=U5=FMj}Z{Px_S7FMjwM|_3|Q%hqGmzBRv*7BWZcxIb-l*Dv$;uK{? zXJrB`u^=%O)lb3kn+f2U1y9ijB3Ka45rHFumHkcz!w+U&v$Ybk=}J~{0e0z7Kk1#` zbBzQ0^vzyUm65zIG}_0}!GN&qbGISXbb~q71%De$q2L5I$A{=_${A64fu0<^}0v zIopx3yYr>~e2;)$VRX0++?hc&nbVvuVECnxVgJ5atKfH z_jlxKHpD@wQykO>sGA?jzIoRm({?UIsC~#8M2dXl)IepTi3_J4NgrxJ38)2>+{9H0 z3>d8-C#AJCrl;WX(EkGJ88?F)CdFq=l#V+Xv;IcXEo6xs0o*`2ua~tgm1Nb(rn3py=rb&7S_H5g+ zH1QttXogHy!l5aAGVvrKw^hb9ii_XzZ9QUQFZZSqFS3nutASL_2LZJ-a~%_81+A^U z@r+c#;qGXx2X$MiNP#Ve*uwNs{Kq%lUSPVQ@JqUTdH87AWuyX?rtJMsdu;vO)KmBK zt)Za|g0x7m!UnYndU5CEcW*b!fSpjh?WrfqDTUA#Rte?*OVZFWfi!ahCm;JdNAtjUf{y8Ap?oZQoCJ?bf6k zz>(Mep~`M=!J>#rJT&B@=L3^187#Rs4X}O+AmXHW@m)j-`FSl+F5t3!aJzcYjq#-f zO@P?R6yCEJKy6Ncr7g275ak*2JZ}ER?s(o65Q6Nt05FF+PXU&Af`$K1pH`hlqb`NH zq?Du0o}36G8OAN5w${mDGO_AtzS`y-kfafgxIq>6%6^pv+Yw;FGj#$oO1VwXp*OZ#5!+xA~w-0gHb0fM^O*d30R{w_mLmVDk8q5aE%Z$ozEmNcBq-adrE6z zaD?T))@`yMYghYJRcHSjLzzbQ%WNKzU7InBv8t4a|K>0yzAc(=Xp1t-Toq{$`lH&v zX8~6qN4?j2?9#U+ljo@tWOX)2fECXm)WYSh_9+dMj6)IF=({!bAO*QfcLHkqf9doh*Zb$!eh8^oxPjQ*?hJ(V51ZD# zG#Wj^3f2@4=%$QR8cYK=qh!2mN+>NRV5)~-l%N_hkt-b6^d?mExH|c1ophPmM0!P| zt}(QJKG#7Z^2NYh;>UBxD{PVgDFo;;gXPN8BgV&8hmrsK)&syG?lqjQ2vVk|hqKBw zEv1HheWz$p4tx!uK*T7#n`}Uj>e&jwT|;9owF?`LA`OlVPT34PM9+8p4R*y4`E|YK zRHSox*u{@DkiW4fV@;Z-d4Jz4tNvhS@Kk`(i;LhJ4tN`}VJ(s3M~mz;#qp$}kKGT$ zC*RgIo448hs-VD*428PI0IcLQiYvi&lJADxNk)Ucyr5T%(?P{D#hj)YCJG{WVC<2V zyj^FNJUIf0``eYAm|^0;Al7wsp!_1-zPF38Pi?g5sQ;nu8<-juCLvc-;M$aU^C{eR zeScLOOhDSZ@@xB1$ExUH%Nz)n#({g^|5Gduhlz96_bL2EgNOLGS87fo88>TXTITa* zHdXw#PGii>TNR(ccklT3Tk z4AJhnD(b{!ZuGHO?1@3>Mw}9xQ#!3sRy&+172hH>j9Ta}fBp8|^~M{!wACt;={ykq z>eaITExq4Wa7*Ivt5PA>IfMD-{a@Q-9r^QY1H2N{B3w$EMCkRok)x#=*e_XkH*($G zYu>+KF)5tT9nd3HivGf9vg*@n2j~d|T1i|@O-*Xw*aO%A(Et9f>+YM?cJGf&r^dSf zQ)gG0DM{Dhc6szv=mnCv^Cs8iTWk-s9*`e^fjq?!_P6Nc&!ik<2mc7BnZ9y@u zFH&l$8gY+^oGJv$C)N&^SGIWET{b=xSdr+jAqqC2z5Yjy4kHkc9Ykly0!(L4SL}v5 z`C>&Hur2SuFaz!!|H2G_&(mop2L2`!Q2>y!xi|j!rGX*%a$}~W93fj;t`vGtoSmel z!E?IX8(;IL-2GXB_J4|pjVs3HavUERND}->B})ff{B=Pnnh zk|)2kv`FGW^ws)ZZ~POGnFQ@UAkbo8E5+1m@sCcf(@q6J1z};b=qisk$t%% zz&0P(w|D-(m_dHAEu8J2u&2mmzMSbaQw?Mox7+F7S|(}4?nLJE+2W(Kin_>03?$CD zVntH2@(lS93W)So5MkEJTx{|6Ii&{UGZK7W#5>+{9)oi;a5@R$KxO;~6sxgws~MEB(%jV2H! zozE1OT5fVOGc!9rkgfbuv}ZUTU%)|6u)Yu=qo2xkes<|}309f`SvgxgCQq(OOnIP^ zBCO@Qb>e)iLO3j?+2wwEj9y{6u?~opW?-q^LvLQ*t}zZPAm^QK&Ygpxx3dHG`R1@( zfsh<%sSeynG@1)lo1g>0mOH!OlS0lkp&E@Q@JK>JLSU~2J#$Fh)PRC1*f!Wz%=^a) ztcmHaX>od58(sSVe8~{6Ru;0P;yUm#e<);`zvR`nT?;8zZ*W-Ju$8r*+{?CdAbifG zr)!hqa@Zpwp0E^U3zF|3UG7f}f)f!aY6*lS`DbgAtqaso3qFT2l(7ZU%@;t#e~0x6 z=i>Ck=bPt%`#e!gP#T(($sq23X>?2vfS=s6x zlc`1SPqFG-Ck{jM+_W&ze}MS-IoyIddm+MK}-&)^U_ zDJwqt|IEPWVfesomZ%l{Dn6AkPX=WZNRq#j!--*4sWuS|Lgt|f_zVMF_f1Yu_()F= zPVS}&EZVL|i*@0x;?FRUBupsCX8x4Mi&KYdmfm3+@nw!W;93l)RCwpw3fK$JmH~l( zHXiW?4u|XYkK|T5NfG{PgP%|>D{c|YA-CU*{1FP-mOSff_)H!fWy`H_>;?N zO#FCuZQ!2_Iz0>J6;>;Xb8@=ZU0%c2E0Zhi`Ksu@$2Hgw_weU=mRDIOTBA2-(slwY z)X&#p+Uw`L(DMnrw0~|+3ERB*I+~&T!k(ksHDHq)5E&vRT;Jr?2kl73pFw@#UFFYgck3289Ab~$)yeKa%F~MGIN4XM;5H^nI#!=&*oz?%QQU>VA0vUkx z0*pNC9t=dA14&LZPv43vot^Kk3 zY~u%?Gk>M7roDo^gdCLrk*a_9?-g>SMjC-P)WAlLcE$X&DF?0A@mc!CMS79&O!Wk5 z_Q=EDN{h@l#>3|HyDF=n)R)H-oO`pggY-@^82+0W4t?5!{WJ`6G|f#;GgnIuwwg^N z;4KKj;f4wb+!~-#W8-%{@s(TvsEHr}&@0)CpeL7mPR)Vpd@4`b5Jb}Z$k!3Fj$%Gn znK4^SYY?fv39WbhLxMCzi^Iss7=zXQ!+Y9t-*?M-1B6h3iWC*yw}U_f{Twp_GcVv^ zgH-Yb4K^YOpTo(cps7Y@ff3iurd{+GWYlWx?`hhs3V8zSUcin1dD!K39O4b#I*UQ; zYFT6+1b7hEL+}!Nfld2V?M4r*xLtJv{B+~LV(&U-CeOt#BNGu3q@nW|#uP=xA)s&~ zuI$f;sBpmRaz1KPJ{8KH>-$$)p%(k^v&>7T_%?rbTK>lS5p2|4Me=;6s~xa5IqawG zr+roB(?l)U>n#Ax!VH^O4YB*dh$!z{yzcJqdg-=@bs-qH^*HV@Cbag^h!2YAA*6oD zI}7*lSbx@iYSL14>bu&B3p%Ul!!j9O<2L&*WGM-UrJ_{oZOu!9^g3Z+CQsUwHvMDg zu0o)u!UhuGgOUA8i_<$#HECzMN32iZAp-`M57Xf!T)oG4m` zfptVN!=mHU!^ex>K#d0y-*73ETQUMW;LEJ^i)rqVHh^WppB|E1abHW zL--GW)i_YJ0!0o?uAlO$=xW*F3P*WP_b%;nfwUngEWFZi%x*R&RtsC^vKI_hDnU0h z8%K*T0SgJgKZV2Gh+C13PX)F2zzuGzVzAZYL| zVv$6+M6K|FxaCcb$%^Lz+oc~z)Ac2j+~81c;PZ1jnHsBMpy zYi#aucq0-fNJ41T3&|FPdbMVq{5bN=Ll9>qd-?$U{RL}Kz~vMO(H;5#3UP2}#g}?t z>PkPQIGnR(7^E|+)>< zfC6N`4-Uj1OSK%CroJr=)nICYx!;2|ThYY}d)lgQdb1lq#c4W@CISK6bxcQKEhwN` zGq4XXEvYn|l5obZFMFOTo!(L zli5n`xnGyFZ^g$?@Qdw{uKH_5z6o~E)|=M!6g7DAMM_#@ey#h?-uCQrEq4bB$+Hom z?Kx9xf&F^7>O)VMyEo4#xnHti2d!SGL*j`jjy;8bPf}vyJIuuzu+j?5auXOVka6Gn zI|J3V_Uz^jce1YnV6FrJEQ#gN_#%hRf9Sn<3^b~Z-AI7{vzhisr|{pifS9@N`FYbp z#KkcX6Mq~nIyV0GBGLFJ4ughF^5jSxffK-piCxG#Obn>;PF@X+y*he=KDPy6t%hYG19 zu&YAx93Cv^VAtMbi*+Tl!Xn+LKJi=}t}$7Y0HkS6f#?h{L*;o34KvnkbN_?acyyGl zi`p*GlB>&w>E7SqB_UzvjaC4;AYfVUpW3})$zdOd-5$Jp=G>Dqn&N}0*H z_jTb$1;LJk)7j#Fb~v0YnFa=z9aGWyj>MPu^AKLoDnI96#qnJqgwS^#4}1zZ@TQAw zSK_bu!R-!>OQ?N7*1*tk0Xyle^lHV0Ugki-?1UgsILXN@K6(knIjYY*-wVYN<8V53OYnk0 zp!Zv3HJKP{;+_UbyVJ-T%_M<36EcB9!T zM4h>ae7_v3h?0c!5-`wWMjN8N@D|A2q6TXn`;c};OG88B-}B#>b)N6cS3?=N%;Fh_ z`Uqm^V2HO|TxkUe#AbZ$$j;7CU&5e^Exia)=|Ao*coiB4NymHt*?@;O*4IL0v^Sp- zRq^{`UK`kI)7da;@Fh75*$_nPP~wEt)*K+>DCc4N)MpLjN3c-{ZBb>7rjy!Gjv!Hy zu@m`)qjB6wwtYHvp$-%yscnqpwF3ZF3byZs*w?4#rR&4xtUr0*`Tx-!=l>e|fXMc< zvloJ277b>5gmgtQ!~1M$a*C!l7KjMV_O}>VqF#OtWv@jzh6>CViP-ZT;gf9KHD13X zw6SVV79%^v0+gTqahKMz4So9)NcO1$a$_p94@%-5doa2Y0R{UO!z=qUF|eVsMHwcx zx0;L3n~;Q_$P}i1h&Q3$(s108o3mC3jAi(2Do`rol{ja9NyrQp*yjIE_Dw`E{%ucf z?VIT@(lfYHjSv zm3(!)GZAZW^jS?%XrP`GSmz9lu~xmtkev)AXV#NwhSrKyk+=7+usn&I@&iFLd66zy z096jjih<4T5UTNph3*QiRL~-`a9Qc4SCmb?bz}&Lo{OR*XH_SQQb?DqG(EZR2v?Ax zvHT+yfJ%l4b`zKw0*cF9SGVapzx5lrzhq0HotJWZDQfKbSN!h151KzOP^(0cCwEH zak>G(4%prR`KqH1whV)mioTWv*>QLDd1-vb?_13wOWo)xLb4z7lFf~w6se2!`tM?n z-+fEUiN(dek!KH+)P2hlW94Ptk7Yx|fQ#f+^h;318h$^PsOIK`_m+8WF}f#lHErw` z<4#VkQkb4%CNpAT4M`^OYl@E=4u@`2G#9ZqJBJLR+J8lvxAT)cEzSSB-$KF z^_wzijN4uf!?;14DdYFhu^^gU+qn*e_YT3Xu)i7sW;MaTr|fR- z*+Mj=4FbN;*g{+_d2bp&SmYjmYQ^ zS3LXyj_^_IbnCN$>0e4y#)oM0;c2GV)-fvZdU%E zsu!m+rfBDG#4dJ2t%pHz?HyoMWN*@{qLXjpw+xr~vLX?B-aG&?ib>{;rG!4JwR%UsIPba`dOPR(hOM4|4{2`Ai-A^O?MU~-6kVPV% zRRhxWJ`KUfvIT%4n&eA(D4?yc_33-mNoXpYN`~V5ACD%>l^(TfV!sMSEM-G4l7WX`*ym3B=n6m;Z~Za-bC7DUfceXFW%XqfSfAAsYkXR3a}7uwbsALqU*0@`h139( zmdd7wO{jUFz641C3mb< zZp!hYcxrg9|6ZG8g*tcDeX1W#A}7E5CN%)_;nr$f0==t2y&r|UegFKn?cUBTwx;+s z2nzwh`5Pk0Cwb`b65Pgsr;(KNIELETFK)4365X*-LbbPg$AU6zvP6OZyN~B9SuSo8 zypyHVsGVukS(4Bw_YeCs1$|4?9%kBtu&m*#RzE%awP%PC-5v^u1|3xGe9Gte5rtfv% zJW=TUXlFCR^^t6k*~wP&T(%8J=x@XEM3Ghr>dd|t{~VWb*d7$Da}5K-q2@P2Vis$n zNZ|03R-#d@Cz-o>&-lOc0O$sx{kz z72Ja-0XVN=U2|~-VJLSMv$4&(N36==_bG%_E_u|D&VHTa$(GRI2={uF4Onjd8T;j-fYGcm7svX!TqGUBjJp#vLcZ*KHertx?&mJCV9}$-+uNe^ zZA$-&6i51}v;gt^ac9&_y&(+1r3>r~kC2h4O4k1II8jgqfDE)NFMjIA5i_bsrS{?w zKBxq5eoHebTQ{M_$q|WgGwy+ULi$d)rN&`?fi=+AYv95lEaKm}+0l?jd!U}mPjK_R zrP*Yo3KKyc3Nk=#txcnSuJ{_4e{%!{<$cFhv%@`kh)>R1n5caa96E#kKuX8VaTSPd zL~Q6IpwEgsGe>V;2I`G<<x`e zkV0A6(%j*pSS%R{w+mQ2;?lr+Dr$omdlaQ#5&Jw6Xw>CZQU<*j5?x8V#H;|*DeRdU zz~mWNL3QnYJtOw$ocyI^^SB=4Nx(oN03lKB9GCjEA0QLcA+F3~MnwcMnOR+wba4RbfOz}@l8Wp0~sb73ll`N@M+UU-_AmZeqG*(n#dqhIeCLtf{?Qa z4$f5QqV+=xMAQ|%zH;XOMci9PRoS(FyC7YHG$J6d0O>|RB%~YZE=lQblrLTca##a^b1IZ~4lgl*dg4+yk?izI z%_Scc1rkNvo3_+;W+kMTEN8e&~q0^Qcof zy3bZlaz03ki8Ty)OfO7q#Jil{r3mkwj|C8^)#`dpK_B~{Cot+PZv~Bu%Cfj+e+<_Q zzncsXcd?m_!5b9UR~(y3I@DJz)V5gkQdMJe?##|XF%IUNELS4bzH01N187+b)>EvR zPp?-g5}v`K;260bGr^%80F@feqQgb|`n~8-X-a6|U8d*;B2rYMJBSfR{n`C=q|%I% zlVH)fV>8^tC^ugT?}2RC@NB^&noq+-6*9Mbd7}mR*lav@m<#PRTCQ$xobuW_E%&-t zi39)zfT4p%;Q|^M#DG?m!|euEK>=%^(p3Ii5^@mej{!4j5ash0I#^EvZeDmcZT!m2a)b>&^hlTf2IB6SBAS<0$Z?m^bm~}DB>qdyf>Yl z2g*jl_xC3hYm=!6AUZqsqlhjw_9EVDSI+gMOKpW^X-sL3&gA?*iUJ!>rE4WT4a^kzYW4oU${xrPUgh736piTK|Xi=$&()>WcBWL;12Vlx&)3}D8?PunwRy)V{*BP1!%-V0MmB^sk z59!8s#kh63u!^EOtOsP)snzR>Jd&1yi#Bht&h_310H^>+384BdXX`^2bCg0vl1Y9u zm>GEz1^SYU$_kkC|Ili1JpWe1`rlf>?vYInbqxrIKSfJ0d%!bYr_nixr>(-_ziJ!F zPN23)s)T?4c@RaS>-T1y$|&ZigM#SIm? z;V+_}^?ENcX6iIU?(KJMu#6Q;ynu=_oyWuRp#3+xK$+GfSTw7q zGp5EV#rQj*zY4dI%o|h%;BlqI;MXXYiyqF`Kgt<|x&@ z%a_P8v@370H^0R!Z)H`+wLfI7mB=~q`yZQ)P5J9zkjsxI8L;&Hm(Sm)`H0HAZqn`B zkyNo-`Yjb2#$&nAWGh`oH^OU&31Pm@z_by354yyR6ILiix|J~1@jg&RgLCPvQ%#hh zXzj8*)o=<5BBGH0+CTyogzc=oi#Fls8-z|Llc^IzZtg|jk&z?&Z94NYwdg0{P#f4$ zen1vfv>)tQ8rd?rPZ*O!N?rvocna`r+^w@7-@l8ajWe6MGY&T%4$m#U5a=R%3B*ht z5r-fA-bz{^)0SMFA+JkhD%EGIViER3Ih8TLvRmJ}z8sF*(Wo)sTecxAhOZxxQow^j zSJaA{Nh8&U%aZpiU*WbgGV*_Zsno^&W$Om7_nWBg&w_%2+pA?k(fZY-(b`@TtL};8 z3#2dtobh4>4L-gvxh7Aw7?Q=``JVg6+S-HZt)tX-Y%z)Ma-z4@^cmN$ZEo%;?qIv1 zW;z* zMmCddZ!Ny3E^$9acHkjTksbZ>zmc7AjdWV()@gs9ZGb*ix0wDtX*FBzsn7@mb_J&Z zXZkvbiTrqL_SefP6@oLSg7ObUZ^Xg~C;WW*lY=V3jsp`Z^zGt_aD^s97$)|?$$H`2 zql%l0y_>^_4s$8`JjpzcC!&N{Y#0b^o$iZ&XKL-caW#W- zY#@*mY&ataEdl^mAP>*&&Zf+NFy90$7qrT_iAgs%Juz@5eiB>>wFUT8Fos{ZVP3I0 zt#pNw+FJq;v)b2#@@z*$rmH*;cme6NxO<_C-$1 zK`u@ij|9w=WcnG>Pr0i*2K>$=du_1^UKRo z1Qx|u+m4h^%V217=5qc=F4W~0i_w}I82WLxS;wFJ#Zq{gWX^ULOb3skqn~TGH2=9h z+xh(Biz#O1(i>bJ{pbyF%SL$Keui zD#f~mVKHoqyP*UsIVTN-kd-i>Yz{Y>EGWLAir$xN=~!+5%`9}uF7Kd&CGPD-iL@Ti zxYp-ozEJCEGfGA?Nr;I($^(2pYR%@2$3+;+Az-dH-5hu4T&sEP7>#)5{oQc!c36Zh z)!VW4mgmz6r%k1Vz-aZw!KllL??>BRykVqNGNqo4zUY}FQOW8oq%;oG!IkUrK}9qe zjBioNY(EPRR$LAI`rAz~Yn1f4h{2i^w6k>Bi?b0U6;S z2g|GIrYAh(<6PxfolxUVK1+5kG2ms(+FA(6^?Cyn-BSjw@ddqnge8&a^L?uA*1C(G z$wE=%kgIsBEu0GDdgI8xX9g#o$K+2Pv|Pk^0q;EzLW5R_xz>)y{8%X>NIONISM82h z4CH6tk0%yJ!Wpi<n5B;Co+zY`gWLM~WZQY2{`p-8}xumch8mU8Ah6E%g6 z)QoG`+{&W9AIeY?JM#r@RFw7t@3Kw@D*)RWCkR&*rY2SZKiVms7h1$5?-L8nzk5{T3H#?reL1e}6Dq|8+?U2xV1bwx#?0 zlFxG-+>T77wzrLj&`!L-!a610B6r0k7@O;z((76PI08Dl!SxXTegR6JOILyy5A-e!>R*CLukO=DQxZ z4}xDm;2I1Y{pVO>w)@iyK#S##!d~6?TxMh}IUVAZx02qLM8Yjg;kqmJdP@Z%(NZ(1 zVO()2<4&v2%@~gw$;DhGo$g+rU9%!r7^;V?xs_%&35#v1*h3QO0xj0rZZJlSsQTtP zybQc(K(Wacl<#r;ml)q%M16=R`-qRYB1>NEmn2=EZC=Mlj)w{47CC;FP1<#Ih&N{x zY>IQjQI$g9nVzH`!K^~cd{CJZ4=!Qa7GaVFF# zm5TVGnDZZ=;!&^9OuEH!!T;SEnaqTmu|xVgr>|}=ovUTX6)BmS#d&B7Y;$ZEKk3_U z^fF*{l8DRya-NI^LMcGM#?;6A*iVY_g_yEiogfsFCuZQ4Wcnj85t25nT%bR5$h>H` zCMF&tk0pc|t;m{I<00RoI$8+(2->1~#4mq3j$B7TV3KNf+oe)A%%ZRItM8N@`jkO@kKnN^O zKiM@5J0`5CiiN;Bbv&-=6K4cd-@x45H1oImENm1c(S!=65(huJ4YNQ#DZ7FVB!9%n zis;9)Z>j{N1NjKvSHk%V;U*^Mmdl7*GJA`$jA`Vt-~apGe$NLpTGlo49lG&c=9~vK z0SH{agz0!Ryv!Z9{I~5H@~DAlz)CXFLK+BxR_Y6e&&6!PU@bQEQntg#UnI-Q`o1dd z)KN#O(_zX2BLIP?m9#6YWc%rJ<&c<^3^7v9kx;|9qll$s#SqdLOBiaWOr_|1wvvz; zLpLIz2c@?hwP@HQDk2=m?7Pc88q zQjlN{-4!kY&J8;c4Gkp=%?{`v^d5Oe{)wDbL03PCL;5qTg4b(LocjJ}f3V?S5#k>2 zBOpBkDLibkz4;q9qoB9CU_uBEPu&(Niv71scu@kyzbY3oFV)0vlzr9+(Qr>97Hsk+ z120Qrcz7Aah`{Vo(Wk$I1Bj7R$QUlDz<|u5JZ^zh=r4s`)+=ceKd%=!L|6m_(%t}P z(;mqz-`OVCoZ5yb@TB->-b1XRS|Uou{wN|94y*{(rv_!&m)(*S_=r2)pxt{`zPUy}k^lZEO1*T@ZRQbuWHtDuQ7dxEZcqO<+qn@mFTuu4yFuqr8*Xk-B`BqXxJ z)FZxJi6wN?HdBi>24&e}8VY(vg)W{bHIV&c@lyxORTpsuy5%UUFZ1W*Nrdnn)8peMCuc!~Q$yPTq6^;FZ4d zBeUu%zH5RCEMWBx`d&z6agbhj*6bF_+6&D zp{KPRaWFGimrCgX&f+JmPk%3tXe=0c{APcUszQk|zr($jW`c5jsIUTDKe72nyT|(g z^iLwsF{9J-^WV7m`}(^7-k1+k*zorYNXp|#-{X~k{a`+%)98A&CH~}xiGJrKL+ZD> z`RKYG?C=!yEe09h_Vg!OD!>4AJ+d)ZeQkgJH0DAqf`ZYWCnKkQmn4g7+$zrlo`9P_ zK~(FAZT@wOh@Kc~-cX8hKRh++hoX3#mHpiaUqQ!P@X&52@vo^ld@@R16&jAue+X8= zf6DSs^?jTBm`{2~1(cD_yq>|ZDPdf%%8^=pL0Toa3QjIZ-o+Li4FQZ6C>SODU~C4t zLf4}G5#}kzokfr@bhv3Cd9<6&W=7ljz4B$UMCJ6MLzM1Mqp8PoLgf2z_Zgn=;r#rr z4(LdgjKXx2n%%oPy6RL@)qnfxd;ps*YLvcy0Wt0T z7+*L+o=rE1x2fKsF9j*6(scaq4J+zyBob20#4fD_Hj(eZB^CJS4N~NH-$~h_BcHwr<-OBOl#{5DpH8oh}jmm6ir_eezDJd!N&wV+&QsZBm z1FR)ZgW-6P7`4B*V9nk{pwp>hCL>$e+p#%d1vKkWq0Mj$fYjmzhQM2_wf^;yT%`&( z&}>D-*AekpXs96C^J;~5NjoH=csb!$!ds^hBeX7AhqWuf3(y7-z(`<@veNEptg}-p zlpk#;3yU-#{EJx1{U=ZQa?Y*0=ta)h*x2F`LI*ZV{yB|&wX?_JNcdbFSI%7cT2$K7 zo2P+ZYCkbc;C}YWOlhsNHU}UNC2sTK!Qtvn_6O@`Z!JOE^%K~b`@a(}iLRu-N6>b? zP5vLs5=ha#CwVUz)tg8y=;iK`%uz7hLdWU3LYKmdfS#`V1vn28H_1NcR@$Ba!1>)I zIa!nhib3Yn8603>oX~GyI$li|xLuOK!8S^pnJ%v=Hd>~>biTBBcX@N$QeIwezyBwU z=$dkAKmKw5!*`ke&wePlN3|}u{l5t>iFx`{X*Vatv##L)N0r<5EH)#S<|>_;HXyZRrU0JTE0U1?_clAPnxA2U5jq$v2wUqE43Pa7PDZRE zw<1>!OzL?jY%XQR?93eIs=dHf2S^W1C{ZxGIl{ zEI7-CyHYWhTeY=V)39cGta~?{33GeI=318ZCpQ@VDJ%2fw`@JX)wUIj*w-<+?*`kI zZ+1Gdv4WuMBHv*28#tE=#v33Ri`%>|L(8A2zF5}d-3~|N>;gBGPZ5Srz#qJ+h^q^W zjT8z##*_CKeDPrqB@6kA_!)6!FtgRl7v>|v&7g*@juN8$ix5y?`llXo`0$(F^0amy zcVs4&(i6WuylZ$Yz4G4sEEiVWo^hZRU#$KgC0B1c@{M=`F-#|O(GlEK(PF*%@nB5e zkbrR8s*T?=xS#Lk%CXlR&JR%TNw8qi>;fbMWbShBT`XW~0gzP}4F-47-e(Y_7wU;R z|HJjhwpfFW0BAh=5AiWy-Z+)CHNyV^8=pXeUS?dZr3vn3wRvbaASK}agP7?PXhiJ& zB9O_1Sbqv`?Spk8;M&5m5=vX@KqJ|k&D4?c3Os9{Gcg~#uOEK?l3It>NiSGYb0LF}02@i+= zx#BK->Ckc;{P3OM<(jEo2RT`%DClgT6vuhb{E_b!(UkWQfPdq51c7Ay$ziS(c8m5d&Uc0Mvce)b?vL&W=Y^(q@y?Q*AdfLL z<;+0F8D!*r0m8@ZX37qS&(>q|MmOJio@m}t1v|i<5%MIPq1+w(!#zbkBZtn_Ur_3z zF8DR5>iWmuLf-;fS?}o>dmx0OqDMsPSX?ybXli`bZmY@aLA@9Keeah^PF4C6_P3%t zGVyH}%KF)i2$~t#!|3Q)SNY1_R_Rw6yik!r-doRmFKTT8t)|sqz{$vEhsVI@x>tFj z(t!nPUU%HiJs`|1pJckj>vb_bt(tWgI6z4I@%r@69Bt_w(+p+B|Je>MZ%PIVkdA(; ziJX7Ix68>6wSZbyt(!VT6ILfr<$`tPxUnZ}&D&cNN<;nhnD)iZMn#9IKeyYU$FuUH z$LMYcd?|e#{q5j$NxUK5ZSd^TLI{i2L})@T6L^U`Uv8KJ?t7aygV*~Skkc!>(ie~I zF|YG8g)98s&7p?;Bn9SKIc;|GkUut?Q*+eA$c8lMUXg7Pc|Ja9-Qj{3?0n6Fl>X7z z;_W@MWgUd`-KZ9-Esi@AMod;#)0Hrj_5ykXuXsDz1x`0XJ^k(DA&J1KnLXjVq7Phn z0J^7%urX+JPIs z*w_wFL3W=as~GLlQ%K02kj?y6#ntzUcmE(v_=JS32X8!&SbGzOFgAG(SVk>`X2xV{ zQRV`@>;L2TQUPs_iFQz0t=b*|<+kTb%b%`K{EFt@8~eNYmFLinsW*ZqoTI21^D2xj zJ|HUrk(JOpf{a8edA9x8S1Fs12#q$=X(a}ah`d2cBVP*pXp zrzJC`IDXQ$!TqusSfKja&UkgYx#OU(C42Hl{EFef5;4UB#i6k=%-I3Zb`K(xp-&#M zVnCZwC#FBF)g8i)H<@oF*Cwd%ua>5rj_(=1KRN_4b%nfP`hT9g0{<8SMFaf@pK02%likk4Ie7?PRTuN4pOg96>BeXIBo&Omj;Jf zXHAF1uMIeF>hX=B5+=)urNYgKy}2gS+l{#S@6>Nlr>hCZ4j0Nifk1a%pZL_ zU>B8eq6Tc+xW9=4I#luiS~q986h&>cy0Ah44Ge7Ns>qr~Zu39Q8tc@wxWoFh&w5ke zZ9XBoZuC<6gPlxgG+m;ysD{L37M=LFkCt_$skp1net$O=bT0s!xuDHE`~CTNwGkl1 z#0L#zQlS)VNyGu)IN))hg#qcH0 zM)e~#mI(mhN4wL0B>N(O5N{2*%`XpiHmrN$WB3-=ZBDBr zQ(9Z8XXNvY*yAHnccbmL%L7hg)qg`y{h?jON?p>q>)irPhT!u)`f#6wfmSesRq+w} zi{slHziMR2<$%kDelT{AfByWYeQEE_poZp%HvL0 zKgoO=(!z^{sfx|G++b*rhCB%zs{@IfGY&Av*53xg4=B)R3!6zrZ92vpTC$~T{44sp z3&pBMrDU<$?eVN&s(|%)gR%R~V2o^f3x<{X%=gTzVbBv)qLa!V@rl8uL|A16hL^-x z;`029pCv_zx2^n{BG{Z1;Em3!|MCcAD10?nA>9(ozrnIPW{L)4@ZVYhwZ6Qg+q+%+ zZ3yB1UYy5xMuu#%?3e!qn&l8>2iB7rmZezS@^*OL&$*r+(7+@kXIE1VIu357F&De?(0*g+}U=7!-NzFmIQeT(!xXZT3*mqh?fY40I>&mn!@2H%;fr<5EU;Jh2S`eEqUEgK<7# zrNM|1ximp{etH zQTN6Y1_#ZXw_E0+OvZS9Ntcli3c2oN1_06=Nn!q#qr9=Rm@-bCw)@XPaIc&zA=%}T zSL`m2^K*6vKJk*`_ryqS#UFCG@LCLq%c@(+vh&zlbQNv;XakRqg>DyCh*{bTbN|9Op^bWnq-#0iJFf*f*D7l&^_wQ|?UZt)P z9+NSVvI;tjiRBxmGPODN5!Iy1_HH1#oT&;hGlX)r)5G*$6mulkvy-rL+?{ao! z$~d)ra@Y0^G!DzS83ySY=F~%}<|5)Nj5(*dCkQ; zM8&N>5fS6Np^J z&x-MdC-Z|2_XFIdB#(qq@c3x z=PN!NT;vWvNr{|7!&8x+dQQ-MZc$XcK6gD*Vvkh7hSZEgB0DUp=;+n}u$v?69*?Af zzp8LxPV0U~89N^EwpRIpKFLho4#8)aesOU)mAS#}FALv|PEF~oJ6h?i?-9i%Wl`M~h-joD`tnHLu7;OWBY=6E}pWPTi!K&L~c z6ZC;~f%k`9D;0w=OSUW{ndmW67Wj=W&+3m{fNptX=_sg+u=ax7e z^4inNf1xq_rE<`u|B5h5g$(5O^yYldct~r-?fULxB$p>*syqXQ9j5`7{7bcpH>VeX z86O8zgq6aTf$0usG{y_$<)GEec!>72G}CD*?IPBsLy1fZca8J)Y>yv}p(p3?AB zK9!<{-f=)}!YPDhkU6H5DVi&lZZw|)qw%%F&A9B-VyV#FNc|jt{A64bJNqsR%_Si-1kgRApvG zV#wEQRMn4Zv3z@Z2+jd)dstyVx9^-scYA!!p*fi`D8Tnc<{0Ii@4e#hZF4&L9*yc` zL_Ug~L;~(!UOb29K_~gTJzJfj598wA`pCC9|b;* z=m?0qZ1+0=&(>sz*rVAS`C6!NmMDL8C+7`Uw6>a`tXS_QO>DL-P`KM!14`URKnFK;CQ>MO8!Sr)vwB(mNs1Jo&dV~MZ>-gj)C^|l~L!~jtLi52BFRL|=M9yCjxE2k0UUIKnSbQfsL?gQ9J_3}!+V@2=eL&?3!b9xI z0XX&zPt^VQ&cY!yt8nzHmFg)u@0<=53T|>a6lCz4JvN(F_usyi{Zs9*x8mdM-wY!n zp^hFq463^D&xurOY&7P!i(w=4q7a$p0?HwX;J>#d9R$ zFjkbgy&(sac5>c%K)ZT-{te1za2$w_#KXP(Uu$31IG45alp@vpoZ^*W>wj{>UDBiY z4IP0%7h7{EJ|+A^tttAf0RG&+z2jCyPOEbk#dxjsA?R?Q0&Bz|{-0bbUoRjc70CJ2 z{>-u?2 z*mqE7OrL7DBXJkPMGDwXayQ{+LrcABULT0pKZpfwvuHbutl<8UnW^Xe70AIToQbgO z05q%IxY_Qq?HJfQSy!X#9W1FJ$v`k5o!%rSS0qIGW)e5jsqL-#Or74iBhXoVX3hV? zh^8_S^A7I>LDu=8J?_OPN_I0%zA!&7>;X{OHN(1*qUa@C<8R_-2eI>={e7pE2{mnJ z5mblY58R*+cE)>AZq>v6erMtb{Qe~fitbRRc0k(T$yOZZ=Z%%~I8Tq2__zOwmTUHqUaQxxr>Y#?f`SYy;goDqbjYNZ*VH#79>6fXUx^wc|0PHCANC$)_wvsIt8UXzL zN#->?oGbBvJXEF}XT$L&;Wc6$u7!p1zkV}D$^d{>ZWo%AXuud*v7f&WLjA?pknQ9i z`eQn~NKmN!YnocZGMUd{sD#T_%PZEuW-)&qu;J8}gFEz%OpW(hs*$_U*`ZoMn$zwm zp*EY<%SI7yYE;lQ%xk^HnPS8laz4bh)6bIyFb=en=CORNs%%ygD^vI{u)Ul{95z0y zo*JR8_c-@Bj7T;>m{#DpkB-`Ye)^M#5`2CLiFb2y7ChFAnwY#H7b~!*OY$py7tOcA>`HXEZc?C0> zT4kUUrf#Hrt;*jeX@TbD5w{H_Fd-5^qz1sR z&Lhyq(BNdjNX!mH!fI~SPk45mWE4K*O<&&-^MG^;Q5Yq8XFFrnN~dfa$FUl>MQVVI&!97t zs+X@V1+Jp@B_>StRwYrB=@LX^Ue9(^qkJsv_&?w>j4~PjQ2?111A8e>hX8sbX>4VM zApGGQZBJ)sqZ`TDLkGeq^3}m4RwH2Ca%q)yb{67q)mRI)=spVyk}kMcn3*w*g4Kcp zn+WC~QDbU7Hi=10pau6pk`UI16xdbPl!Q9p67JfAKwN>wfSM%mV*FGq$;k-Rc71YU zH8}l*5oHz~IS=U>IR><^hMF3kr8%c9UT%Ymgu&dnuRM zm)hD3%G-1mGjn~CAIZEzKJ_BLg{VK7EMo27EY(;=t^er!!42RHKh=3Y6IhPv@nOh}LM4f(-gW`qzBH}V>JAn}_{f}nmqTwak^uLjx>* zzcicK_c;i_{BDRSx#ki-m^88f`$TKx-F&T~s82n+-FmcRi~hSdNjbRQ-sU;4RtfgQ!TsVC9JceD2|%{z`fn{G8paW zshM}7?Gk)h&G2X07m7~_8Hk_zm~SSOC^SjuK}f`H}$Fl>VyU1OTCww~xGTAa54l zf^6;IC=f;qHGDSH*})6}pI0Ev`_W>;c$5~<_vd!RiFm# z>f%yyz2%48-ka8767>u$?+-#=ZR3B&=dI$B!+>@_c5=JDU}o!P3PP-*RoX zfe;|Op5C>E9Uhw^=(RiF@?)-{_b%C>~w-S?b`wh&penoMjBK!?CWa5EfCU`qVmTA>w zDmE@?glwwIrw>aebKol8Gu)Q2KEzKWqUL0sQt6}Z3@2Udkfr0vmO(9LZZqQ|T7+v1qRktLf$Ru#& z9y`_pcMty9AZ(>?ep_@R(dj)fi~&XIK}eEGfl4u*ttF;GJ1M6?f0E?i&0QhqXps8;#@_hh%V6qZYC^isT(9-s zRwn96!<*u8wDq&Ccc_@ZfBewmcFX<%r%7~sNdAuM?Y@GDrR73GBhQt}M*--AEOi_u zLkjU#^dB_As82^4yX%X-GOe?0380}NjhGO;+gR+HD$xf-SGEgMAfY)>f^xBKqOC?ucQuV-gxy<4hc_*=Wm zN->|q1p1&m_|*zt`aCVITA)&*6(_}qd+`spP}8L~oa~;55pyL%ZY~bGUyIq4S&TpH z#(fQ)`vFW>@UsnI`8-9^)E*0K@WELEdwuC+!}`z_iqDauGCZ`twyvaPBTO#uu6xvf z*j|P%PR%`={P!ch?j6o(J37pjSno&mj!gvZgqc-epWZ%DtRMWddD!+q!hyN$kadg| z70rx*aB6S9tyw-t;>m3aLHRcdw9%c8X7?a9}xt%I11l@bTjX4NYHDhkG)@r%O>B7rv9q1gI!mHY$t3AV%Xe@sf%*C9 zLDbyb>Q!N8Gu4(K3zgAw4GfGo_onS$zoR7_`<1LNkaRWzhSzH0IbtKp-GRreI}{5m zxf08F0UV0eV{Du0<9G{=VM}jCAucZ0UIOSH?F;@*o-jB*v@Z#N+m@Qa{NZ1_X6ai4 ztM8FrVqVXeSf>o^?9zgQO`d;dQ@Od1s*V7{OGHS7mYP;Ni4&f?_0QLgIj3jiydG&> zpT5_blDoc~_h>16n^{>Ik-oem;?K_Q^f|9Mhcwe`%*mV=EP{uIAJwz`A6i@7%1#Y& zlEt#!!-LlZ5QLoGrp0ohHr;r!q0>+4s01qO#NY01e~ch2WB8MOlfM3tuUl}o~H zA`Vg~3YBtnF3%etAZyokD4uINhwHmb4Us0Zo#hta;?LN&-B23n4;OmUdEJgJoYxW< z>qeBM2*0UnB+&-WY6;w&JZ;ekz3f+xg~*X@4qn3lN{rF@C~O#CXqOR5No)=rZ~P)_-S$%?gmIF;>do_yqtrlnQ zN3GOsbmD8{^c_IleKJ4D`&TW`gf$G0L3gs-UU30q zw!Ag~!D6y`0y*8R;5@n~6v+4>TCy{p1#`MaEpe9Mg( zMMbmL-A`vWF!P>D+B)0Sk(s-iQ)odz&97TDcT(=haa*)CM%%xtLOVankfX6_uQ`5J)M1) zcQOsSX(%SAkp1N58<<0lUW4O;w-=DP$EMEW07m%h8eeXL^1^eh<HpH&g^I)sG@#Uo8(SEaU&_wg+cHQho+bK}sIO4>g4PlnFu z&s1HGoG1|{=|iV&tDQCmRzY3b4EP%==D>_fJEh9vFq&k<=Z@Gs?|{pI7DXW& zw~YGb)!z4a1l5w%VEjXKf*C8LZcZ{^0{H~^&p4eiMymCSfUhrZ$M~k6dlEO-TDGqR zWc%PdB6KM7>rN}V&U83i+_^Ffo$qsr%*~*Awkj)|ZX;Y>N|qBSJ$mAa)%+-<$6UiL z=j(f_aWh1{Qp8_Yy7U~6w_OfjBiBwBRPn>UVWe8Dk3$m|+=SkI_V_`!J4UF6VSnb@ zx+S-+K1K$GqBOl9E<`zv%PA=P+`eiV$?!`QPsJ?Iaxbo&#|N~@=2t;I%ih$fa~*e^ zkrh!q?iYQU>GZ{t`aCs%BRo&{h0Tw6S5t&szv{NqYxyWi)g4AGBJ)*+JT)CgW-F=# zA^NO5ZdZKe=2yd%<-8tjiMVmzC|Sv~!=XQOQwwK&Xfm!xoU=0z!YAk??}Ue;d-mgr z+{9gXvdhp9ulHxlo)5J+Z!mpS;4kq2aw~;rty&V8X3EY2hOIM5yeGeQ|MXz`$mDjPq5)@`x?sQRk(}I9vY=4!o8<>p{)Zb z2|gO`*Yi>1Jr`q0k{MnCNL?o3oX@#?ZMP*yJardPUnf^(sRm_1$zjXC*D?5^@T9&9@h4 z9Uce~tlr`E_EGQbM`7GJ1kt^c5ru6YtmK$2mnjginRiJzq)@*-)7s{id{!FFBP7uEd)lFLJ8Izcl z^XT^_nn+n+_wy5R6U2F5Ta&JxUraAyf#cZ4Q~wuzJGC0(AL(EbMU_-aaMB17Z7v+n^RCcAn2yIu^v`SX=&dt@^RrgNmS) z^uh!R;rZjp$8N6YFGn=9hLA}MNM7bwtf8O~|>=Q$*3lssUBi|Mn{kJ{lh_!Fm>@1mP(KPV@GvTBV} z1j7hM=_M-Kmm+I?W^*VT4ne!C$>&fz8z0c$ep*#OIce{~NPFh$Py$!MU0FfV!7}nT zgEH;lr{6E6r36apo_M4r-@n(pa`DUuOt7%>vfUDLKYD3CSL>Z!qS?^*NH))CO>2$o zX}>)Y4jD>0T~CpH%dt&sq!P2fK&;qn-ScO2@HL;n2;VT?PmM^aGPeObmCc3s4R2_0 z@96m9L?T{Ftv^!od|z89|LU#@Y-C|jf)rEy;s;jJ-^@R+u#T8XbIGp*d(zOWvQFjKY1w+dJyJg&BY19+ob4AY@*X(q9lvLFAJb5n-RvE4VuQcu_G{$aKCSkyZj*6R4z++|}($YT9?zzeCb1bjxmwIvlbNe^t_q9Cy=g5|X z__b>LzlkY}VVAC$3@!t|W{qipFBek*$v6uuOTi>!=qp|*RQQ_`-#)R>*7d$&?1EgM zygR+4ag>C+!+8`4R-ojI5gObvH)#=*KO=v%eGeZBo})6dJ^kza4om&r2+6BC`r@KJS9<=mj?-bOL=udZN<=}$}- zUa<%kTI}9Y2Y$YqiW#7+!|@7H_0~;!R_@5d=^_;T}}Vaq13oUo<2=V zUY<|;qT#@4ots)s;)>|7BfubFqzPzqbdwSM(!0Z!|WlM0{Jby=XVAj#nuDp&m63HuL-2 z_XdMJw79E3884tAo*b!9em^M| zqu zIN|2lf|QKf%{!yU(?Eg5c|$*rP4DyGS3)h8F+9oS+;x^wDS__vXs~7BlL|la! zK=XsekZ~ql;m-<|3NZtke2r5hWwMr>A2hD|wtP{+wWpV@6rica&;^OXwP zu~G8rw-7*T0|6e6iXzWMQHAd`i_2omRMD%n;(ow%PYeleXUl|nY_!g&sZNa?6WVUS zdtWXrSDJ2`%Ea_@WDX_5rKNJw>q!Jnf#aU!!<+LR3@QdTDlQ6~#_dgptCMf!1lBzl zyW>Q>qoZX^s3%wA`HxlKgar5=hSe}$I42X#7)Gb3^G){o#lkESO|(Rqe!}wb(2_=T zs9o{@)gWXk@cQ`;HYj~Fj-}s!i<19)g!7Y=kEjEKsedK?AH2O~SXE)ywo7+NcejAF zbT>$sG$>ut-HmifcegZ%v%6Wp zu7JUZHgenNp*n-h&ix?9KEy4@K)=u9*jP_~lj>%FL^q*SFDXsyOZ(PU$}?-eH)mjk zgU&k4&UuRN+|?!?6Ym|EgtZUU(Mb)!#KOMc9600P3^zVQV9Jc#IN}YHx}>Hq`I!2u zvXolgTg_d%)8`KNR&}yY5eH|>Oo4fw!}UTL|843qU#HQ6hUQGWKm6qIC?0&v3LZ0a z%W+oQl_(kI0nHa@d!v&NMP=Gq)LmFi?ZY(ntWt%+^aN%Fy~sm$u-uj0Sk!u@vh^WR;!!S_%p@j)aNm%f!Ssh>Pz?(Fmi z1^lM4ymnf`^w(B>4wdNd}nPvG1K3FexCPSAlR$%OY0j~S3=24I-Ocis$JbT zhzLFyQkX~XXov9VWancwYAuNiktxQR;y{wQm*v^p+jBdCwOwBCWn&eoC%bO8H(a?o z!QxqkS`3OO-1qGCe7HPn6FafIvJ+MqwX;yop$3nA1&pFD{jZ1Ut0Th$CWXC|!%6Pu z`Xx%#PD^=5V4v*h=*whdyDV1<@AU5E#RirTv7iH=fWXIkNZyDFWW6?r9cd>dXnI+= zO*q(W0heqnb6E|IqEKgMZBtAFnnTO`WIFq0uv6uzGg51rxjC&>ySJUlO{hBj z5+v|Yggl^zX@Z%~W8xDdFBTl(zbYc#UpwV}NtP`136Bb)db~!d`vv^m zPHawa9BlV?aWpl`VWxmf!0L*)^dki#Vd8$tzR%NM8uRi>&yLpr>;7zs9#r3xRucdE zb=Ll(4@@Vl(rfeBOl3G6ap3;2sNl+BVZK@%(a6h_VLq1m25sbAPiGUx8u6_*Qge7Q zhR$xM^~{G~Ifl(K<3YR!alEO-w$r? zIq^Fakv|Ok>ijM@&6XJRBDhRHj_tGk7tadQ6{?Mrs zqmWh8BYkNmuE)Ff>j{2cU_i{q;avNw<;i~{l~o!5NbkU4#Se^&q% zvEBCo?JQ5MfQdO+SC*cUF&@8CIjOCG5|=qWDUoFs%y}%P2iNZ~Ok}ux#w zhgB&7Z(^uqGCs$Ic71F4^S}ck?+cTY$AZmFwt`TQ<>X+`FQ`I%-Rr&+NdKo~Oy6p$ zRP;kPl&L;RRkZad|8^Kl^co!d`by>zR8;G4{dBj)Kn+D0IazFDEa@WE=TFEGOP?w+ z*J8i{t|Se($K^;HcYzTX-?8H?reVU8A|Ip{vO~j`MLe$eCLoHfQe~y_b8W`IXP%k4_{7A;&#rrM7+ASo$SF0!?VihdSy{3{Ul_{D#uv-%;Vw=F z!375zCHU@rTR-HaWjaJ%!f;3Wa!I(9r@u%$gKH#^)3zo*Ljlp}K4(4qnp)ncYm=N8 zGy<1@{Bfy&C!XC7mNw+!)HX=;0o-7b^J2|I1`{sp{lssrksbT{RxZ{=)>azLb*zm4 z_qUQtnFh=;hwMejed9m$LMRg}^Lq@nQ+W3Y|Azp3_DQgXbSB{31?#)g$)wW@Dtn`5 zn~CX42XQG^;zOM*!IL}cP2TC?%GIK|COc6V?AHgH%%r?#>}gp_?+P(R%*)F)xAIRk zE(g;Ie`e9~*v-`;Pv)vjT<^k+BWN+58Qt0S^PoX46Ji=cyddhTwq>ltYJ%y4RMNtS z&fP;%L&uYgHvNa*i$hO%omH3@8j+} zp`Lf3?*ybN%aj2QLAYo47s-Y8ClF^M_y}YH*6#xYiYKI5cz7{6{+`Sp&EEzU3uZs8 z5a45@kh%IQK>Li{aLn_bF1Z{mK~}=bU{PUtLgAwnt?2UH??0E>aZgO9rgE5&_ZR$4 zm6RCh^PHSz&3;$3UwSa_y(Q4?rhbIhr(#ydkYOcmvrss>TZ~i3M^cp)q|=J%F4q}* zjM**SAcEk)l>twR6FcO^*P{$Amt!NP-a~&o?imM*?%~XjJ%isIE9Vpnq)-+bZKKb| z)-Tr?$MfHZxh~v){h-zSibRBllEWLpr0#dDYs8mmWpR8B^1WisZRZ>)J9Y6e5w6?P-E@N2ha{FJ7}TL+v}~?{9(#J@>B-;&mNFopuRP z>~|;i2MZw=pj1f&{H<^N%i(*~z(Td7(9rbP3`JYFu|?lDX>`4H`3&Y8FN9k zniVgU*wXC3%HDu12eS}W#dRAYUhG)T)UOS!$tjL*LBoR3jtf5Pz2AQhW=l)w&}9dI zR%lfi=Ng(|V~aV;?Kgi|Ew9i@r2S&;>Z<7c#s-*U(Q>O8ZuGMgZrKoAN=u9 zSYgt2FigmAr0-IV)Q4x%ak8|QIYOY<2-_;bZY|-@pJ|xT&HO?_Y;-%3I>|pgJ^3VO zF7YTT#;>tYcrj%)&1OKe9i^N}4fP~vmqz((@-FufpMIezBk?cwPltFn7rY9tROcSY zs~sr9669Hb4p0B91w;=Fqy~RO-{03jy>O;EsYO5yGYkR_%~LA-!wS_CX5vC7Lk-FJ zXZ9PHPhS8B9cVA3q;xXvqim*$+u~lMC-C?*nH?`2LDGg6Cu<0X`jU(izA)-#!%r1P z=EQ#%QZ4Dlt?^$-5U-Ujx0rHkGc4A8aBIMgOMr%kb&imkeRV_tUIZih%^wb0wV2oY za2l|cy_~5*Gh5cLtX6XnaH1dO0ipzG1Y8ZN-j_VlB!lz9q|fhp@!Eow+>{V(n&$2x zn6x>Y@!R;R5VkC3EC6l5%uK<6QN(92W%&w%htxi!gRjHdgn4B2n%fBH8nK@oqi-U>auM;f8J(uKx8)f8b21S z<(q?_B?#?+!-pAMm5zxDh=G zE{nd`?+Xu^;WXYN!<^$`vhIgmX;81S*VkDnB4^NAu-+qWk6w)qjn&wi@PK{Awi4a{ z1*VAtBAmkJV-1T)8iZR2lC|)B; zsDOg?^S9Mi!TRWD@ij)L`tcfe`oi+|!aGavp+8u?x;1Eb{6qWJV%QJj^i2HyIu-`T z$h`rnAR0x)bgZRu&FZ)}qEb$aIH=p>gL9rxcX|KwnQI8*d#s+DYc3hCBKaRq?B3Mc zXSA^juz$(__#^9JCzV}f96Il})xvhX8zbwlBDp3QY43H1MlMi$ffO6#i74cnM5-4y6lTfS~>)U1l$KKYgDF6O^KMate z5~37ICI3iH6ArI2?2MNPf^z|vRLy_FKS3861_$4*w6y2%D{~aJIq?lb+b`QZIcT@_ z7y8El_5D9A_4g)t`wFW2ix=1Am- zJkjqJiS?lecSJ!y#dv9WI6X6S6<;H271IbISEW)+aY@OCl_osQ^?2CxR*w3*PgP40 zh`hYK0Gx2f$r~8>8jbgU{@e_%AmGy?6yUi?e7Nvj{i#w#S9(8G3nO}TchMO9Wjvd!GhM+5h0N*x=9&Gy3w}s(6+k=ngIP)caeRRjd?kJc? z#0ziJGXmpS41U9Fi$<;)I>p7w3QFMuw%y|TplEY=cXftnWtD9GlhfkFXfgK@-5TDm zd;YwGp^R*>)Ax}({KH#afMc+b9l}DJ#n;TY zm-|xHL?k3$>vh5?F*GzZ8EHM@wjk@StC%d~V>LZacJ0Q3sI(fM2Yq?SKOFAIYMHFl zXtUB&jHrt(_d^cAoxyElh^s%3-F`o2fXwv$F^s z4UI~sH~bCFLB5W=yJd58Zpkmp_@aphWrFo|MDxJ^1PfD`nuOQ)b}(^A`3UaMehl$U zr)Cvl)EeGePZ48xY<5V8ds&_e32B4nXv{xjN(k|;h-7wB{GX5;=B~Tp_v;@)4StFq zjlbYG2_rq)_B6FK7W2L&ft!!W{@zxv`P)(h2EWru2bD=$_e$0Q7@mN{;yBvu?P^Qz z^nt#$)q`=?rnsCV11!sY4IhSBy!*F*=8liwgMjs`3xmF=+tT$Q+9(o(4(}VF)@OO4 zm`FJM`oX|%6`z#J?sO@Z-(O#5ncL}n_7&(#tg7Til|TV}a@r0Y!0c*x`_&$eDn2sh zL91#RR14957FU;zxMtCNow>9z_=0H|-;$vu;V+0rZ|q);Lf`%|DJ18map~Jv$lW?e zZ>;}N|IPZ<-fPZE)G+WEltLpRKOV0Q;+MJgu$Y@nJ`k1N%GqZB8Bv2eG@ZtrKHqC7 z&oQOHI6MAEIoG_!V+oc1tse5Hv8HICx?z@Q#O4S&W%Gr#Q)d~O-=!Q}$l3%2|L_1d zgwp~rc<)1kH~nec7_OASq?>QapdI8Dnb^kT(a?R1c%h}yBgtHSnpzR!d#SFQ&2D4p z*!A>egTpeR3r4W^`<%Z{p!(_C-Ztit;rN)J)3(E|s4{Me=h+g0)filIvfEAa03VCt z5iXE=s5w5TDS>_Z=4i3eA@vvaC~6?Q>s5@)J;NKY0Q5@pJ1K^Wy{TIWNrf+Ca?Q7bt7)BCvXN#5*}ajZq|2p36_<e`LJ!`S4mJx5(CnnX;DeVNEvz+CQ6@72UwStwmq5hW}w>2 zA?RBu3pNfHyu#liD>J%c)7tL_ejLMw-Q99p##6I~M5=9K4atyvgngLvn&(To8RdOhX&{=-qNbc3gVt6i zd|}aoZf(RyzYW&Ed$-EQ&fdRF)PWq*Rb*KVd=+4NCiM(dc)HNP zvIy1GLT+;XeaR(>z$k)2+Od;agM}R|A!cb%hgNkEA<_*HH1=m2vyvqPo;tdG4jMB{ zCvwSnl<;jWjdeP_TbtM8!Pq)pk6VGS?EU;>0-xPD{K{TM&Gu@2V$MeY!FG?Vb9j+< zzOIjEc=o6B*o2ArHVlu~f+9*pcj5E*jNLk3Uc(q-e#r)6`^WCw;9y)5VTS4EnaWeE zpLGN+ymX`9V*!i{l&|60t0$##4v~}SosJ!c&LkX z;i2)*aDydO^8Je0rdk@sqr)X#NWV`z91cjSluw#IXe($9_-E27x>6+*u-S+_bqE>@ zK#rpO+*nn&UA48i?+&(Il5V;o?n){Xyilp9#ht!93XdlNTt0=;R0dWP zI{5UF!?eVR_e2JnLU*yJHLxd-%m*nL){y?QI|-6bvfz=7FUU-O3Mq?1Iv(cPVf@zb zdK?7A_JmA~$nxJtQeEonHFWVKysWf6z%@~Yxrgm@u3nrz*5`Y5nORk;9Gs#xzA2J` zFX)J{%&AZ%hjTSQmHW1&mTLZdK~fb{TQ)W@uqUbvE0-P3wW=MrMI{+Kvjus(HIB1X zjpiKVrGR(&f>}ICN0oNZsc&HXFU~2Nz{cCC*W_cGDi0y=J5{Pofm9# zNoRLJN%MiB2_kHNR|d~(-hT{D*`IZJ_!i!+M8-OR!C@zb)cpkj?kF=*g@CRG?~i0~ zDZJIq>CII-@2vA!R;|N?h!Tn00R}c%p^MEyto}H7)Q^PIop@1uY*3w#pcgzpr9J=M zYp11RWtE`D6FwhI*|7H8+9zo3h;9V+o1&HTby~59_4!KOe)rm)%8h7fe@H1?t=$UJ z5GAs;cKcVgxDYMhn6%9Q?rREjE2CJKS{avM5i{GsT=q?3*hUda$-bViY(G;Y;0YnN zbhV@-eb~P`;CYN5FIcmxu@*h<15GkB8NJ=hb$jgv_6@+op0P<@1O zuL>FRsOu6)^V_?_{7NZv^PxIae(xga;OE_};@+ITuGBf(-=JH{25{GCsvs2=6 zx&FOie?OQ*#J8s+qTUcr!{_1AqY?v9H*rvRk63VKpGwat3k|Bmegw+7sj0CX_QSvc zeoVJf5Att$TOwQAYGDIyS$va2UE`>=Rk+UVbiy7wMh1ouGcfJ>6E#n{K%Q^=LewD` zLAAMAO3&C^fNBOE{YA>3g6h9FJU$AU5OrcOg>_yw%mzT}fmWZ0_h-0hIRaHxRe9w; zeurDLW!VCaH6iOPSBE+EgwONdSt^(Q zMA#WhD5^_xVI?oR-Ig(?K%JOmq503BPDR?JFabZ&Pu4*UlQT`GC1{tKKL;rpA}UR2 z1O$-2{sJR@Vm^7+77skEnXp%M@wB?|ul1NdNymzRWZTh|`#;=XxC0kno1X)=!RgBv zl1d)mi=WdyT{T@@#=mH=aR_jR2Fu9%?z!a<`8$)QUt=Dv(;9oHL8X;Aq9wCvg?`eP z_3vbil!>4zK_H@MpR5KRCMj3@MN6Qs6izcRTH}{yYe{}YoVoBmO^fj2S0(dyRYX(M z3q`&0ItaxX$u3=704@%Xvi94k=yY}i+o@vrAr_}e%oQ>?}z64}b zlf?Z>8CxCU^YS!}be;CdU!ln8vP%Dv7v8jz}1jmXj_v#o`NYdMc_Q z27vkQCzZ<*cJ;^9k-6b8T0_^B0-bR_f0IC47NL#y3wf@io}_<#Y*TDcA0Ht;KlT|( zhm*`!`b6Bt3i@3X#knM#$s#-g4j^l>G5=w6WQt!x<;- zE>m)YiJVsWyUf2cx%zE?fOuz4XzaDwLd)w|BqYeB zyZuUH4>ilw<=@ZzMtf4b@cSt+J087E{;(f5?z}S|$C;9%&Em(2^4;Cr_ZLE?BxIkI z-9&?)qCG^FgZ}OKIe&-&^WlS*nb``W>kJ88A?Ig4ee1L}SZqHG4978sx=ztu*AP zj}XD>i!!?X6kKp`{6b89g@9N8M!!|`^$pmL zVB+P_+hwB@;oz9&dqtB7V#DDUw~fO8AyJ1Mw~(yH#D%xWIo+G6N*ro9<98(^58zkI z7Gjww2BLy3^l)Jj9*53e8AW@V}(8Q8Bq1N7#A z+m62go&7S&Ij}M-4Rzj>>F}p+-L}{5@J&DZNj^WLmd2ei+;q9=sTXYy_WaG>m-_~# z9G-d$O3DJ7q&LihXcxMKghloF#l!E6a;0&L0Ke+x#$V}({#zRrq9|(?Q_o|+LKpNt z#1^Kc-faUwdCdMiS5#7`8S^{lGPtV^7l83JJk;FQxV*gl2X-Xb7FwPhzQ(`EcRUm{!hmNvwB|k@ z&q^gZT#SR^Nfq5P%^}0O%eIr5E=g&akiTgeHDI;Qo&^bzUEgLH_ZsQ`4!S)!mxJ)T z+w2EvI)#jO+ntUg#+mwaQE0C}Quu^@!wnQ8<|n`8IfP3}u1GdpEcVh{N#%Kt=XUYe z8&I95q!LmPp5U}0QHfNm&Y1bhzHzNu6P#O*|D2#q{AwH*%&|&xu|4EFdt<0lNgjoA z4$>v=w?(|_HPx(dN6ztB=68Cl^uD}C{y1if9EOgI!=An_34dSK$>PUTcZwf>C z=WR=7%+6_amtpWgNzH&bO$qScVM>|P9v>Yo6kSf5o`w!n!t6u1D1m(NN;r3E;Nq^ed9pD=Ino9qa@=$iAC}-`eX8qfq~Gb_Abq; zwMl3!sK;O8^u3ZL9WE*`Mo{Z}8np z14*PpVgGh?Ph&+-3 z5?s&F5Vp6X-AD5uKn9}yrIv~h?(c-lSBN4ZpvU7>ZF?4QS>Z?gKJQd)G@0E~96k}- zoYKk*4BQ0Q7kC90xoZeHZ3G4&Z!x{YlV$LRy>AjChn}HO+!Mbky%dFo4Coj~(@iOA zIrr5E{f_>tbhE9_Ae!b}o#Yhkxn6jX0R1(h_uNji+vlu(ffxozP`1PLMScr{PtLM9 z!d9j#R``THtEyJ;y)B+V z(nvHCRuc6)Bac>q+2!F(Ji|vrX2xP8IiKjxJ7;0nx?IHE(Vj3;C}usC+8!3ilt6E& zpg>Wsu;1OR@5!KwK2_TraeQ|O51U1j@t2ZM(rIg|!p8m~&$9{Sx8Y^@^H`4La97ON zb*^UdV3n0^i9UW{Wj6;g31)-KDL%Sydw1)x?{Z{{`lrL)-U}|9NEVM8>bEa! zy9{L(x3C3jOC5e!#OHuw;(qu3lt9??x~v{=eK)V*sVmIK0(2wnbgJNTP|Dd9FM^87 zKb)(x8V)ANY?tfOdE0DtCJ;;W4>vuzJ^K>H9z0zxPgYE(qS}@fo$C7`K9ucAQD9@q;oMAI;Lc8@p zfk!F}(dF$v7rISEkC^Y5tz}?I%Mr}dSNb9+x|F9#ahglQ1n39On z+T0Auq-O+Z77(d|)PgSGzv^|PIos!X)R!o_5{^XdBK-{Q4x@r^BHBHu11HAZVPN`( zpt}wgzc2rQ5kDY#ckh@FU@X`GjW^9&@3mz0+xLM(g_jblEHGcdMA^`_I9PK4Z$^+@y?d_7(JWuCM;8lXUK{KYR0y8bjOU#;hmnW#%V`2Gp3Z z6i6w*TnRi=y@8L!=7h~M>~+3woEJ&cv0(QBv?uWRn36BhhULZ(!! zs=T5?O$Q5(9>8G9e(d3jGHr1~h0D1DU|^eIt4&KwU8jY$ z_5Plf{QDgd?x7z)02!rx%Kpd?BS;}Y`SE6Sx-^evi`ET5j5KmDMLHUqWG&_Ksh=3F zum*$iW#{6_xhdtP^f*2wypaASRlgmmvwBH1M63`zg*W#P&80Pw5 z4;v~{z-AOe183=w0Bi>t(%G$^>F7NkM4R);BNfPq%$92eIqd?mrxQNb7$yJ}NDBS; zQ2%&pVV;eGESQ{fbMgWQXR7UKfTRsQ$}M|vex4P`<<#5rtUT$kSWn*&X_H;RacSZ<=cLkNA4M+e_397zuXM}ciDLowwJ?v zUelusH0HTGE&%`$FiKF=##-PO+>~In!$n2Cm zLEN$ckhDL?^OCo$rhO29xvm7^-@|8M^nSS2P*hYjc;v2nCH@!f#>46L0&1+Pd0q4i z@%gA#1^$0U$Itcd^B+K_HFa7#|EmSy$JI$XJKH$QeQeeK1roAs2t8B`yj!5VE^rk8 z7#W$J`gy!i)DE&Q|01=WD4h6@k0^MTHk6M zo!B05Hzy89-?PAG#h}w8h2i~DJDvn4AaNbO4|0=n2p*46Lb>8jqp?v@p`l%Lf8G*gpU~SIG2ZiA1Y5yL$}*amB0#CllKcSiMK8ww`%sN7f2?Yv8^*sv@@JJ z3BnuyFuD>r_9TkRs^Mb;{*=TF-KugBauz;6ypd19pYG(ILy0S`Hi&*E?k_g+f`Iv< zNx7^PiVRNF9o!|+)P$U+Lw~Ofh;NI-^#aSt`FSVk?%yWetXO-pVYiIveMWiM8%C|K zuP+}dbwsl8l94gu{+;3|p77H_jX{`n|JS>O#Db!tpZ^0uC9|I{v@LccXITus%Ds8R z$yuq<=31r6Z0p>>bz{E0wFM6+h=S}zba@M;5yh;9(Es7CiC98Dk6&Hd(5V|5Im6J8 z#vWf~cKS}C$p8beLWY2W=sKMW0J?O7g3xFRX`GrWwNN8ZaY^2ffrjo9>SAIEIzNAK zp1cjYpgv8*R6E#Bktce43d&&Dq-#`5yFy6GzJ?mV>sZS9-0#EG=u>x%NaL6gG32>DBsk{bM@>j6n~QUx`uf9NR2C>-u{ z>Z#4o5`IcaHIhR{?=Q5rbK-qex`tqn2>*B+mzrNtpt0EU36!LbRzGv;^jba7Cw{$< z0bh~2I!-+8o(^c~v_NB2m;U%5{CUZ4U$fQrSYe;bjm1-$xF(RIp88HZqq%*tN3I~` z)*X9)(C)1-VbZPo33%Zbn=G}-KPH+}z#oqSgxseu^N$^8iho#<(rfdjdwuzEcMZeU z_=Ep7v-$JwSxCRx;rC>)DNCSjm}(d+$>DXqING6*ldaDipbVk>Yo_LMn^6#t<*r7j zO>#4E8l)Zr3BQdFj*RHGnsI3G&oTW;f3|zNDgs-|@H}!vaZhn^z~f}!`TlH0FM@N( zYcxSwjS~Z5B~(PI7o(=&TPk}3Vv){Jp7dwps{MA*_}vL3AXGT+tqdi?u{MipQU`db zG;{6-I2-z4w=&YX<(FPsXe8D$dWp>-^@3$uR9@}Z?^dcewZXl44Vw^r2jLTm6l})B z;k5-}lex0Q`_r%aTt9aq%(tBk@C9(ydESW00QIVy*Xd;Rk6APk`^JrA3Piwg+6n45 zJRMo6l36RC+uk&q4Yd@dIyP|NDcxP{4V+D7V(Aqt5DVmTKSfSnQF=zqbI_TankAam zvDuo`nHAyrdE(D$0SqnG-|=fRXdDEfUuv(}bd3f(_E5D}d=Zr+nq|(@?;G;^BW(UE zoC)$xWFPVltE9u}fH2_E8$0}eQ`W3!64wT;Y}VO$)H(~C%Pp>mPn*DT3o3oY2*?M| z$nkwi5_-!xoL-w-qzl;4tFR2h1HV6@5IdPOpWQ0;-#HIKGyrO)5{|~zS zS~!1OdOppYiNL|J$7 zLpV!QAf=mC+@fL)iH6aLZo=hpOFTY(BPq*5EL21f|Juq*gK)ZLNb~Kw;q^4vHr)3# zAtxb~t-NiciHZA&MDunZqQUSTJpFe^p|&<7!*j~`u{g)!&&lf!ErPOn5GZ?#Y>zdM z5g-ChduA|?U4z$+kKBkmrx0`*L&yR9J$#O?w{uE=M-7KS0v?LO(p{h%Dk3Jvv=HJ) z8oQ-3ZIhaO)8DM3ubPLh!rr4dYX+=K%Mv67do_P!Q)7%-oND&div7gnj!N(BLT{ zM7+i()}ltq=ZiF$)XIe-5dcBAG^ZjS9c{g}vm=a(sNZOJF!XsguG3y<0;wYry+Q=egl*szxGY3WeF9$7ZJZQc< z#C*95Dcb!F&2s9;cb`!d%v}=V_EV{_E54+!PveLs4pbENE*0B5uWrRCS$BSzc+oD)s)ud0NuFPd`~BF4)SOLf&4i$NzCg_ zF0J>CI`xqAi<&e%klhZ*2)9}-U}d=**>iR`=RS)H+FOEbh3i9cR)AFRf1|f zG)>F@tAZFmx-Pu z+ps}67Y*;O^2hON*8Vr+ynT9RoSYVGfN|yq#Xr~HO-2()?|1^}e1U%+czS@kX!h0h zmn*9lIazr#pmT#xi6VY~PIVMf9OKH*gC~Fy52O*jh)6-(*)-6`rMKuGz;<%^V+Aa{ zf#~r>wU%SqOCQKX%0$w)*{vaESuQhCH3Q1aU((;Y9WUqbnp2o$3%QlO_i6*iY2!>t zCdhXg`t80IeyiDsj%!z8_;5ydX<%u>REZK6# zGS;LCePKgBH3^S1CD?XTeYV2=2bcerC}j+hcdY)gPTc}ppK;0cJG?y@e1Hz z+%u`;Gf0Yx8h@Wg1ndf|QI|SvQYE3i{O;WXcSYk08e&B#xGFJ*>co80@imV znJQR_q&zo=vrWvE$ZZTstV*U+H#_e7!|@LDMt2G)#T-Pc??r>V>GOTAh)NE ztS4v{M+ zn%D#i9gSB)T5ET_JI$#h)A7}vK2=0V$VV?(3AUl1t!`tlOwKTuG~)yw1U&%ey@%5I zhk$)`v})dl&wz1*2$YPb6Grwq9MSb11S{QBnf>vc4$EwT%|aFsp#MPC+l}rt3Qmpa3Q~Y(mK|%iZTM(0{dF(%@xzwej>tQS^7Omnqt0NwxFkc?wGcVi&T z?k*@&WoWZ(I7C^Ix)H?66Td}%o;7*f)#M`28iwEfPL6N3&vJ==+q~;(xlI}`p~K^V zv^S`8_xu)h{j16N4Ue6TGN-v>&Nmhb+V$|lO-Fh}u$pZ#(ZU^s54S+{zvKx@!=&1x z0o;xn@$<`wiJ|W>VznDH#2$U5q$N{hSLsTHqbq4|XO?}| z)9;~j@f6b6+;4+pFBt#!sR~xbMGd`mIXLAQ84f<-bx79)6mqdSzWD@>l-Jj@Jd$5U zYF^$~0SWI-&|;1JSU0N_83_ra-AHpt+W1cU;?B6e^v+PntOYrVkgaXV0@2Udh#7=( zA|AAeJ;zRa%JGN0o{^G;2Hf&BXXDVuqSgT!<&g~Sco@Sp3k%iQr)1E0K0yNPQLl@B z)@WtqFeKW4QxW4$3Jyh;2u5LNiV8?PKWDl(^o+nWu91P#+qUoI$=Te*c1(Ixn+WHm z(#ztUOU0$Zqo>?s@Xeq^v9^wtCSpJ)@A8e-pDur#PL~^xn@tM|6BH^8KE*jebg9Z+ zB$C6r;}UE>Kin$t$kMBqcGWO@J9Z#QD{gFn$;oKsptcbm3OIYAii)3-3+>#83yUJ8 zUZ$6pSXRK;kglrw?Qt{-_f*eMMlSTWckExUC{^X{8CGm2XIvb#Mok6i1b;0OeqjND z{lJ>1=d@pd91mzQ)x+ACj=G57Z4;(((9{Lr@Rom3lVo8&kgqS>Y6(=NhEE43I>I~* z5jmJE7r9t&`$l8hv2&Bf1hOzoesgW+c=oSXTQt^uU}^-C5P>_YWTn+BM?y0X>nejA zr(BL9gpuiB`Zcz;Q*Sz#L=%a!y7FdDP8_}76camf`G8A4JEAl(O_I2*GG1dqN+pGm z{1)cmU{71C-4vSd5(N@{ZW=+_2IW&7Mk_ag-p=8;G$??}7t9i8GU-xf~EC(h)Au@V@vAqlz5uy&Fee9LMwS-AcS925jV8P26F6 zewRy16zYU6)s%CSIZqQG40EizJR@W-4k%uNGh^sm-&Sla;K%0j#LgZgIazb9#xb^U9K22gLqEO-;5@ zAPq7>A)uK4+UpP|rBLjIh3b^B`X3u5Tn)u|(=_k5`z?1J>uXBY5aR_oJ5*6vChm*t z(}UTXwHTU{U)zBq;>_vX6sxd;q|Oo>slj~XH_X~0qn2PJLRq0-#460l5g8BV=%Rcj zX+;qy3tj&?-hgL=K9Bejg~!2$CN5bG=@+T69HS^pxw`CyqDOfMhwUyQ9E0u^!%_Vv zmIXei!$!x63+yd%=>On24ckfpdCH{!H{Q}iicBEE9gC$ksXciLjljj${ zQ-hSmL?ZUdh;P2@ z3*L*i`P@@aKP@Qyt`-Do<5SN-=mgvYqg7h(9U-1u_CwUHq<6O3tOW%TjrLrpiF9Kx zHB@OVfr)q5tpaC3MujaOUiRIp!{RFfW(8CWY;_>UW!1#sT{rbvpiClzyD+{tC%}_y z-MIqDWTvpS-pC{^uE$E>fALthqH)#UQ8})!mbh^h`gHm zelq7{wA%eE&I6*7tXI6l z5=bT(B2m%lTe(G35kMyBd;9hU)c}5`S7iuHG-3KUNx^jiB@mq(j+iraIaEbxpCImF zn^42B=*G@Citu7gzzL#cNH&u0yu}>JKsE;fVAqV3gOE(HVeIk8X**9sqS=!?{k-%A zYhdt=oMN7CvNqmjz)V#z1~2bKFT!|aOg0bdm}30UmTog<4AapW@364v_7V*{0^AHO zVz7QY2)=6DQ-9fFKFmeOjg>TF8U5EE*na(3zasN)eply zCbzoC%Rh<5bRhYaP`|W)QH0H+qL`Qesmb^7uBA(QtYlPAz<9~t)e_|RNEzm)MwS}# zns*M7aImM#OJ6c3_f+E8^Iq*wMA+cNyPePFWgWhi1^I5O1MdqLm*Ek@^G>0DpR+wI zA?E-td__{^6s)Rtki=3Sve_|e1N$VIE@dJV0vB^;C^%hqejI^WFgJ_6wGN*@ z)O$W?O1+>V7>360C$&AM#j&iu!>W^eUUR>|h z13Zr3ZAI3J+&|}#Z^UXmyCNRHgiA1SC#Jw?{277#*ji z%ltqzBf#XpB94lbXJsp+W1)Mx9a&s-3zB(}N~^gou~VTg+vo&;7ulhe1{G~w;Ir5X zRI`jn-Ul!k0rDz@D03d$^Nc}Lb32zlkyAb4Xs|--szj#v6Ruvg?Wuo9JjO(xaSc{t zLT2%>jpY<}Lj&kp02N3ofm~e3$jC5~!J*%gsZJ$!3-={!VbSn6W9d_Il^4{BaA5IZ zN+Pl^w&PPbRlFfsYk~WeHymb|j`foWdyY^Em@PNd^A%&d%5-Qlb4dMTN?b}x#?fn+ zO92-8{e_XmGsK7)z+IwIr#Lu{mr#*VFu(8Lw0(%J%w##eNy^q%s7eU+Os1R-!;Yw* zO8xwq8=Wi+^!GVgu(l6#wPhYAk_O()BXUtKQR}4!-?Raj@3{(>WCCzDl1}wftRg!* zeu7`^%_0w+={yNbvx#Pz(m1P(a$rFE8zdHVbkm6lXToEZE_m&u42nLSR3}uF7gWg( zL7uN|(D<=ZboC&jpmeM;63-B^fBtI@7);$4I6hiLh(nyV-5rp5L!L@mESD5ndZaA}KziZ+UfLr<;V(_3#NXpIiElsEe) zzdL;~IM}>#8_9q*b%t%WEvMYT$B!pU8>7$Ur7+ZnnK^p{4e6 z9!mSg2}p=b$D~2Yne6vB^!6)uhxzo(L<5he%uSop73g&K92(-gMfaWy1C7&+&d$Rn zfCeSFtS>5Q26HB5IiLbnIxvLWCXaQ4^h29!FXTJ}Rc*ZgT#|`84x^(f7_VV)B8{?DHyECoOZ;|6@ zw^3U#!@D8gi)PF+JEd=YM<`abUFit(weCs9IDIcOqooXDVPqf`ZZ5W(3Iv)F>3_5@ zbfd=jIx|~ie+G)`t>`ZM&ob+hoF-FP=|ABV4<)>@zv%7P2#I-ZUX*4ZzWL0E%bcm- z$SQ&ayL1j*&{0mr-+9T9;QhWf@8~s2RaYCh{P`V{X$A3RK_`X^PANk`Q8tag65YV+ zlqRtujlqpy<9E6_OpSLuG%OCIW~Q)PsQBVcrZ=$Tf!=_T$ki+?-x*O0AlG(|SDWcd zYX6^2cx+Kr@bggZ-1;fX-32PaLY5OYd~cBX9DEqKV@<}s%3^okdzNboJqFO>@2d#P40yTWBLw; zE0=Yp!!P*G1r|C{Z}0Bh7w9ZlecrI?gkSr&+^w14F5hYcuB*AWHVQP|{_p4A-DNTF zZ0B#5=2X7$$zS$9cb18=Y{E4CNQ>#RJ7);KxcS(PKicAcuf33ys^aB!tgJ>iH`F-n zpEsu_;P}InQTwY5C8GZ1Z)_LYRsLFG-G`(Or??b%tuwmn|AQ|B2lM+SejN_mFD>BG zUYt8C`mg}|-QL-PXUv(I(|~6tb2a5!iA3~UN~~b7-4-=#<|b}&$vDt_wc`)q=JDAY zz=N38)wO?nf7!ig`him}_Z$0q5)9{GZ(`aWnU@zAQsPq<#`ojWmMx~LzpLz3ma3@K z1O%;GuzWS|tn!K1z5LI2bnD+J_Y8NR-S$J|Byi#OLEY(7=EUra`6>0Qs%xp?-54Xw zo0grPCnaCb6crIQFy#;mlz|{fIQ|ebn z1pd4px~4>b;>3wFr%ahMb7t($B2`&gT?wyA1)CCibC(8n9hy0H=2ttrhnJU^yx3SE z@7h*S`%9+;c&cpA0d?Lewcvu+m*d;^vPQG60xo(AeWIgTkbD@rZ~dJ9y?4p1hG&KB zR8-WQoc3mg#>UD{l&U#-^4z)3Zf}p@Q^FhrFK$@o=s7L;v!-ZZ$75i}?xf~Q;4#6# zGp9d(f6})21M`YSi;moPn8nZW(6@r+OF?d~?~ko(f;e+Qd!LtNbY&VGQD3&K_FRIO zXdiGZ^?%bVTB~1Wh}w6pSo49G?aBVORa|-vZoo+BS~eqF;z0gc;B5J-4cqy_r^c~p zDtl~gYqU31G?zD(X3>Z-ha_0CK6A&&Vb_ZpX-D37NFrT)gWI_ zEp`3>Z*TioU(*U(F=77vvs1Lp&aMHDm!7EfKk&iEq&nr$t5@EzTx&PPQ{-KUR%n++yha^9PjdiGToPtzZqU(-V^&Xy$g88w#+%V2|+G#aeQ*xY>su}8M>gU-XNv7+|Cc5=L%?POt~K5 z9o%eE``{-OJEh=*AVSyMZ zENqL6UjB-_x$}O-^YrIWmerm*arW%*Nz=c7eZA)FX*C80#zIdQ#}IR%?-pr<`^5!1 zE_7mL-Kw?fl$S+N`yaa}PoIWtI1LKvO$xIQGCWUvT66Fr3vh)*y|TfCrHf)1**AkK zx0w@i@6Mhhvv^I%I!%zIs+JcQ#9y0~!h4>w-)Fr%VPEaxM$p=ddUoI;-cv6`fi}{2 zwZ?3`uKoOJ{n@i;-4_D`bBPYKn^P;-Q_F=L!M&uaCteZ{G|ctw&EIQ-=JTi68Ylpl zac~3`J@HaidUbvM{e%M#fy=)@a)06{OxBLLsRJy&_SIP4nad065r{qsI0#8dz&@9Z zMMy6wMu3RUTlt~3awtvv&&a^=|9|Drm#Pd53~ZplXJ%L~Q~b(Y-*75Wgu&C*&t;uc GLK6V%n9iR7 literal 0 HcmV?d00001 diff --git a/packages/components/scrollbar/private/e2e.playwright-spec.ts b/packages/components/scrollbar/private/e2e.playwright-spec.ts new file mode 100644 index 0000000000..668aa14518 --- /dev/null +++ b/packages/components/scrollbar/private/e2e.playwright-spec.ts @@ -0,0 +1,17 @@ +import { expect, Page, test } from '@playwright/test'; +import { e2eEnableDarkTheme } from 'packages/e2e/utils'; + +test.describe('KbqScrollbar (private)', () => { + test.describe('E2ePrivateScrollbarStateAndStyle', () => { + const getComponent = (page: Page) => page.getByTestId('e2ePrivateScrollbarStateAndStyle'); + + test('states', async ({ page }) => { + await page.goto('/E2ePrivateScrollbarStateAndStyle'); + const component = getComponent(page); + + await expect(component).toHaveScreenshot('01-light.png'); + await e2eEnableDarkTheme(page); + await expect(component).toHaveScreenshot('01-dark.png'); + }); + }); +}); diff --git a/packages/components/scrollbar/private/e2e.ts b/packages/components/scrollbar/private/e2e.ts new file mode 100644 index 0000000000..6ca7997464 --- /dev/null +++ b/packages/components/scrollbar/private/e2e.ts @@ -0,0 +1,59 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { KbqScrollbar, kbqScrollbarConfigProvider } from './scrollbar'; + +@Component({ + selector: 'e2e-private-scrollbar-state-and-style', + imports: [KbqScrollbar], + template: ` + +

+ + +
+

+ [hover] + {{ content }} +

+
+ + +
+

+ [active] + {{ content }} +

+
+ `, + styles: ` + :host { + display: inline-grid; + grid-template-columns: repeat(2, 250px); + gap: var(--kbq-size-s); + padding: var(--kbq-size-xs); + } + + .e2e-scrollbar { + height: 100px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + margin: var(--kbq-size-l); + } + `, + providers: [kbqScrollbarConfigProvider({ visibility: 'always' })], + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + 'data-testid': 'e2ePrivateScrollbarStateAndStyle' + } +}) +export class E2ePrivateScrollbarStateAndStyle { + readonly content = `In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists of an attacker submitting many possible keys or passwords with the hope of eventually guessing correctly. This strategy can theoretically be used to break any form of encryption that is not information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of successfully guessing the key is negligible.`; +} diff --git a/packages/components/scrollbar/private/examples.scrollbar.en.md b/packages/components/scrollbar/private/examples.scrollbar.en.md new file mode 100644 index 0000000000..2faef3848c --- /dev/null +++ b/packages/components/scrollbar/private/examples.scrollbar.en.md @@ -0,0 +1,5 @@ +🚧 **Documentation in progress** 🚧 + +Unfortunately, the documentation for this section is not ready yet. We are actively working on its creation and plan to add it soon. + +If you would like to contribute to the documentation or have any questions, please feel free to [open an issue](https://github.com/koobiq/angular-components/issues) in our GitHub repository. diff --git a/packages/components/scrollbar/private/examples.scrollbar.ru.md b/packages/components/scrollbar/private/examples.scrollbar.ru.md new file mode 100644 index 0000000000..b7a203c6ab --- /dev/null +++ b/packages/components/scrollbar/private/examples.scrollbar.ru.md @@ -0,0 +1,5 @@ +🚧 **Документация в процессе написания** 🚧 + +К сожалению, документация для этого раздела еще не готова. Мы активно работаем над ее созданием и планируем добавить в ближайшее время. + +Если вы хотите помочь в написании документации или у вас есть вопросы, пожалуйста, [создайте issue](https://github.com/koobiq/angular-components/issues) в нашем репозитории на GitHub. diff --git a/packages/components/scrollbar/private/index.ts b/packages/components/scrollbar/private/index.ts new file mode 100644 index 0000000000..7e1a213e3e --- /dev/null +++ b/packages/components/scrollbar/private/index.ts @@ -0,0 +1 @@ +export * from './public-api'; diff --git a/packages/components/scrollbar/private/ng-package.json b/packages/components/scrollbar/private/ng-package.json new file mode 100644 index 0000000000..bebf62dcb5 --- /dev/null +++ b/packages/components/scrollbar/private/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/packages/components/scrollbar/private/public-api.ts b/packages/components/scrollbar/private/public-api.ts new file mode 100644 index 0000000000..983c1f6eca --- /dev/null +++ b/packages/components/scrollbar/private/public-api.ts @@ -0,0 +1,2 @@ +export * from './scrollbar'; +export * from './scrollbar.module'; diff --git a/packages/components/scrollbar/private/scrollbar.en.md b/packages/components/scrollbar/private/scrollbar.en.md new file mode 100644 index 0000000000..2faef3848c --- /dev/null +++ b/packages/components/scrollbar/private/scrollbar.en.md @@ -0,0 +1,5 @@ +🚧 **Documentation in progress** 🚧 + +Unfortunately, the documentation for this section is not ready yet. We are actively working on its creation and plan to add it soon. + +If you would like to contribute to the documentation or have any questions, please feel free to [open an issue](https://github.com/koobiq/angular-components/issues) in our GitHub repository. diff --git a/packages/components/scrollbar/private/scrollbar.module.ts b/packages/components/scrollbar/private/scrollbar.module.ts new file mode 100644 index 0000000000..534063504e --- /dev/null +++ b/packages/components/scrollbar/private/scrollbar.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { KbqScrollbar, KbqScrollbarViewport } from './scrollbar'; + +const COMPONENTS = [ + KbqScrollbar, + KbqScrollbarViewport +]; + +@NgModule({ + imports: COMPONENTS, + exports: COMPONENTS +}) +export class KbqScrollbarModule {} diff --git a/packages/components/scrollbar/private/scrollbar.ru.md b/packages/components/scrollbar/private/scrollbar.ru.md new file mode 100644 index 0000000000..29a28eb5bb --- /dev/null +++ b/packages/components/scrollbar/private/scrollbar.ru.md @@ -0,0 +1,11 @@ +`KbqScrollbar` - это компонент который используется для настройки параметров скроллбара. + + + +## Виртуальный скролл + + + +## Программное управление скроллом + + diff --git a/packages/components/scrollbar/private/scrollbar.scss b/packages/components/scrollbar/private/scrollbar.scss new file mode 100644 index 0000000000..fae86f328f --- /dev/null +++ b/packages/components/scrollbar/private/scrollbar.scss @@ -0,0 +1,171 @@ +:where(.kbq-private-scrollbar) { + // Thumb thickness (width for a vertical thumb, height for horizontal) plus the gap between the + // thumb and the track's own edge on each side — the track's own thickness is these two + // composed, not an independent value, so it can't drift out of sync with either. No + // `--kbq-size-*` token lands on 3px, hence the literal value. + --kbq-private-scrollbar-size-thumb-thickness: var(--kbq-size-s); + --kbq-private-scrollbar-size-track-padding: 3px; + --kbq-private-scrollbar-size-track-dimension: calc( + var(--kbq-private-scrollbar-size-thumb-thickness) + 2 * var(--kbq-private-scrollbar-size-track-padding) + ); + --kbq-private-scrollbar-size-thumb-min-size: var(--kbq-size-3xl); + --kbq-private-scrollbar-size-thumb-border-radius: var(--kbq-size-s); + --kbq-private-scrollbar-thumb-default-background: var(--kbq-semantic-contrast-a3, var(--kbq-palette-grey-50-a32)); + --kbq-private-scrollbar-thumb-hover-background: var(--kbq-semantic-contrast-a4, var(--kbq-palette-grey-50-a48)); + --kbq-private-scrollbar-thumb-active-background: var(--kbq-semantic-contrast-a5, var(--kbq-palette-grey-20-a55)); + --kbq-private-scrollbar-track-default-background: transparent; + --kbq-private-scrollbar-track-hover-background: transparent; + --kbq-private-scrollbar-track-active-background: transparent; +} + +:where(.kbq-dark .kbq-private-scrollbar) { + --kbq-private-scrollbar-thumb-default-background: var( + --kbq-semantic-dark-contrast-a9, + var(--kbq-palette-grey-50-a48) + ); + --kbq-private-scrollbar-thumb-hover-background: var( + --kbq-semantic-dark-contrast-a10, + var(--kbq-palette-grey-50-a60) + ); + --kbq-private-scrollbar-thumb-active-background: var( + --kbq-semantic-dark-contrast-a11, + var(--kbq-palette-grey-50-a80) + ); +} + +.kbq-private-scrollbar { + position: relative; +} + +// Hides the native scrollbar on the effective scroll element (host, or the delegated +// kbqScrollbarViewport child) cross-browser. Applied only when overlaying with our own +// track/thumb — i.e. not on coarse pointers and not under the `native` config option. +.kbq-private-scrollbar_hide-native { + scrollbar-width: none; + -ms-overflow-style: none; + + &::-webkit-scrollbar, + &::-webkit-scrollbar-thumb { + display: none; + } +} + +// Auto-created wrapper for the host's own content when there's no explicit +// kbqScrollbarViewport — holds `overflow`, so the track/thumb (siblings on the host) stay a +// fixed overlay instead of scrolling away with the content. +.kbq-private-scrollbar-viewport { + box-sizing: border-box; + width: 100%; + height: 100%; +} + +.kbq-private-scrollbar-track { + position: absolute; + z-index: 1; + box-sizing: border-box; + background: var(--kbq-private-scrollbar-track-default-background); + opacity: 0; + visibility: hidden; + pointer-events: auto; + transition: + opacity 0.15s, + visibility 0.15s, + background-color 0.15s; + + // The synthetic `.kbq-hover`/`.kbq-active` forms match on an ANCESTOR (the host), not on this + // element itself — the track is created via `Renderer2`, not template-authored, so nothing + // outside this directive can put a class directly on it. Matches how `kbq-scrollbar`'s own + // `.os-scrollbar-handle` is driven the same way (`_scrollbar-component-theme.scss`). + &:hover, + .kbq-private-scrollbar.kbq-hover & { + background: var(--kbq-private-scrollbar-track-hover-background); + } + + &:active, + .kbq-private-scrollbar.kbq-active & { + background: var(--kbq-private-scrollbar-track-active-background); + } +} + +.kbq-private-scrollbar_disable-interaction .kbq-private-scrollbar-track { + pointer-events: none; +} + +.kbq-private-scrollbar_visible .kbq-private-scrollbar-track { + opacity: 1; + visibility: visible; +} + +.kbq-private-scrollbar-track_vertical { + top: 0; + right: 0; + bottom: 0; + width: var(--kbq-private-scrollbar-size-track-dimension); +} + +.kbq-private-scrollbar_rtl .kbq-private-scrollbar-track_vertical { + right: auto; + left: 0; +} + +.kbq-private-scrollbar-track_horizontal { + left: 0; + right: 0; + bottom: 0; + height: var(--kbq-private-scrollbar-size-track-dimension); +} + +// `axis: 'both'`, both tracks visible: each track stops short of the shared corner instead of +// running underneath the other track's thumb. +.kbq-private-scrollbar-track_vertical.kbq-private-scrollbar-track_has-horizontal { + bottom: var(--kbq-private-scrollbar-size-track-dimension); +} + +.kbq-private-scrollbar-track_horizontal.kbq-private-scrollbar-track_has-vertical { + right: var(--kbq-private-scrollbar-size-track-dimension); +} + +.kbq-private-scrollbar_rtl .kbq-private-scrollbar-track_horizontal.kbq-private-scrollbar-track_has-vertical { + right: 0; + left: var(--kbq-private-scrollbar-size-track-dimension); +} + +.kbq-private-scrollbar-thumb { + position: absolute; + box-sizing: border-box; + border-radius: var(--kbq-private-scrollbar-size-thumb-border-radius); + background: var(--kbq-private-scrollbar-thumb-default-background); + cursor: default; + transition: background-color 0.15s; + + &:hover, + .kbq-private-scrollbar.kbq-hover & { + background: var(--kbq-private-scrollbar-thumb-hover-background); + } + + &:active, + .kbq-private-scrollbar.kbq-active & { + background: var(--kbq-private-scrollbar-thumb-active-background); + } +} + +.kbq-private-scrollbar_dragging { + user-select: none; + + .kbq-private-scrollbar-thumb { + background: var(--kbq-private-scrollbar-thumb-active-background); + } +} + +// Insets (not `width: 100%`) so the thumb is actually narrower than the track by the padding on +// each side — a percentage width on an absolutely positioned element resolves against the +// containing block's padding box, so it would ignore the track's own padding entirely. +.kbq-private-scrollbar-track_vertical .kbq-private-scrollbar-thumb { + left: var(--kbq-private-scrollbar-size-track-padding); + right: var(--kbq-private-scrollbar-size-track-padding); +} + +.kbq-private-scrollbar-track_horizontal .kbq-private-scrollbar-thumb { + top: var(--kbq-private-scrollbar-size-track-padding); + bottom: var(--kbq-private-scrollbar-size-track-padding); +} diff --git a/packages/components/scrollbar/private/scrollbar.spec.ts b/packages/components/scrollbar/private/scrollbar.spec.ts new file mode 100644 index 0000000000..1241b50c74 --- /dev/null +++ b/packages/components/scrollbar/private/scrollbar.spec.ts @@ -0,0 +1,866 @@ +import { Directionality } from '@angular/cdk/bidi'; +import { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + inject, + Provider, + Type, + viewChild +} from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { KBQ_WINDOW } from '@koobiq/components/core'; +import { Subject } from 'rxjs'; +import { KBQ_SCROLLBAR_CONFIG, KbqScrollbar, KbqScrollbarViewport, KbqScrollbarVisibility } from './scrollbar'; + +const createComponent = (component: Type, providers: Provider[] = []): ComponentFixture => { + TestBed.configureTestingModule({ imports: [component], providers }); + const fixture = TestBed.createComponent(component); + + fixture.autoDetectChanges(); + + return fixture; +}; + +const setMetrics = (el: HTMLElement, metrics: Record): void => { + for (const [key, value] of Object.entries(metrics)) { + Object.defineProperty(el, key, { configurable: true, writable: true, value }); + } +}; + +/** + * The actual scroll element for the non-delegated case: `KbqScrollbar` moves the host's content + * into an auto-created `.kbq-private-scrollbar-viewport` wrapper so the track/thumb (siblings on the + * host) aren't clipped/scrolled away by the host's own `overflow`. Scroll metrics belong on this + * wrapper, not the host. + */ +const getAutoViewport = (host: HTMLElement): HTMLElement => host.querySelector('.kbq-private-scrollbar-viewport')!; + +class MockDirectionality { + value: 'ltr' | 'rtl' = 'ltr'; + readonly change = new Subject<'ltr' | 'rtl'>(); +} + +const coarsePointerWindowProvider: Provider = { + provide: KBQ_WINDOW, + useValue: { + matchMedia: () => ({ matches: true }) as MediaQueryList, + getComputedStyle: (...args: Parameters) => window.getComputedStyle(...args) + } +}; + +@Component({ + selector: 'test-scrollbar-host', + imports: [KbqScrollbar], + template: ` +
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarHost { + readonly scrollbar = viewChild.required(KbqScrollbar); +} + +@Component({ + selector: 'test-scrollbar-disable-interaction-host', + imports: [KbqScrollbar], + template: ` +
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarDisableInteractionHost { + readonly scrollbar = viewChild.required(KbqScrollbar); +} + +@Component({ + selector: 'test-scrollbar-non-floating-host', + imports: [KbqScrollbar], + template: ` +
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarNonFloatingHost { + readonly scrollbar = viewChild.required(KbqScrollbar); +} + +@Component({ + selector: 'test-scrollbar-visibility-host', + imports: [KbqScrollbar], + template: ` +
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarVisibilityHost { + // `visibility` is a plain (non-signal) field read through an OnPush host, so writes need an + // explicit `markForCheck()` — via this component's own injected `ChangeDetectorRef`; + // `fixture.changeDetectorRef.markForCheck()` from outside does not mark this view dirty. + private readonly cdr = inject(ChangeDetectorRef); + private _visibility: KbqScrollbarVisibility = 'hover'; + + get visibility(): KbqScrollbarVisibility { + return this._visibility; + } + + set visibility(value: KbqScrollbarVisibility) { + this._visibility = value; + this.cdr.markForCheck(); + } + + readonly scrollbar = viewChild.required(KbqScrollbar); +} + +@Component({ + selector: 'test-scrollbar-dual-overflow-host', + imports: [KbqScrollbar], + template: ` +
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarDualOverflowHost { + readonly scrollbar = viewChild.required(KbqScrollbar); +} + +@Component({ + selector: 'test-scrollbar-viewport-host', + imports: [KbqScrollbar, KbqScrollbarViewport], + template: ` +
+
+
content
+
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarViewportHost { + readonly scrollbar = viewChild.required(KbqScrollbar); +} + +@Component({ + selector: 'test-scrollbar-virtual-host', + imports: [KbqScrollbar, KbqScrollbarViewport, ScrollingModule], + template: ` +
+ +
{{ item }}
+
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarVirtualHost { + readonly scrollbar = viewChild.required(KbqScrollbar); + readonly items = Array.from({ length: 100 }, (_, i) => i); +} + +@Component({ + selector: 'test-scrollbar-composed-host', + template: ` +
content
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + hostDirectives: [KbqScrollbar] +}) +class TestScrollbarComposedHost {} + +describe(KbqScrollbar.name, () => { + describe('basic rendering', () => { + it('creates a vertical track and thumb by default; builds but hides the horizontal track since there is no horizontal overflow', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const horizontalTrack = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + + expect(host.querySelector('.kbq-private-scrollbar-track_vertical')).toBeTruthy(); + expect(host.querySelector('.kbq-private-scrollbar-thumb')).toBeTruthy(); + expect(horizontalTrack).toBeTruthy(); + expect(horizontalTrack.style.display).toBe('none'); + }); + + it('keeps the track as a direct child of the host, not inside the scrolling wrapper — otherwise it would be clipped/scrolled away with the content instead of staying a fixed overlay', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + + expect(track.parentElement).toBe(host); + expect(scrollEl.contains(track)).toBe(false); + expect(scrollEl.style.overflowY).toBe('auto'); + }); + + it('creates both vertical and horizontal tracks by default', () => { + const fixture = createComponent(TestScrollbarDualOverflowHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + expect(host.querySelector('.kbq-private-scrollbar-track_vertical')).toBeTruthy(); + expect(host.querySelector('.kbq-private-scrollbar-track_horizontal')).toBeTruthy(); + }); + + it('marks both tracks with the corner-avoidance modifier when both actually overflow', () => { + const fixture = createComponent(TestScrollbarDualOverflowHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const verticalTrack = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const horizontalTrack = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + + setMetrics(scrollEl, { clientHeight: 100, scrollHeight: 500, clientWidth: 100, scrollWidth: 500 }); + setMetrics(verticalTrack, { clientHeight: 100 }); + setMetrics(horizontalTrack, { clientWidth: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(verticalTrack.classList.contains('kbq-private-scrollbar-track_has-horizontal')).toBe(true); + expect(horizontalTrack.classList.contains('kbq-private-scrollbar-track_has-vertical')).toBe(true); + }); + + it('does not mark the corner-avoidance modifier when only one axis actually overflows', () => { + const fixture = createComponent(TestScrollbarDualOverflowHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const verticalTrack = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const horizontalTrack = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + + // Only the vertical axis overflows this time — equal client/scroll width. + setMetrics(scrollEl, { clientHeight: 100, scrollHeight: 500, clientWidth: 100, scrollWidth: 100 }); + setMetrics(verticalTrack, { clientHeight: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(horizontalTrack.style.display).toBe('none'); + expect(verticalTrack.classList.contains('kbq-private-scrollbar-track_has-horizontal')).toBe(false); + }); + + it('does not create track/thumb when KBQ_SCROLLBAR_CONFIG sets native: true', () => { + const fixture = createComponent(TestScrollbarHost, [ + { provide: KBQ_SCROLLBAR_CONFIG, useValue: { native: true } } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + expect(host.querySelector('.kbq-private-scrollbar-track')).toBeNull(); + }); + + it('still tracks isAtTop/isAtBottom and fires reachTop/reachBottom when native: true, despite having no track/thumb of its own', () => { + const fixture = createComponent(TestScrollbarHost, [ + { provide: KBQ_SCROLLBAR_CONFIG, useValue: { native: true } } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollbar = fixture.componentInstance.scrollbar(); + const topSpy = jest.fn(); + const bottomSpy = jest.fn(); + + scrollbar.reachTop.subscribe(topSpy); + scrollbar.reachBottom.subscribe(bottomSpy); + + // `native: true` never creates an auto-viewport wrapper — the host itself is the + // effective scroll element. + setMetrics(host, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + scrollbar.update(); + + expect(scrollbar.isAtTop()).toBe(true); + expect(scrollbar.isAtBottom()).toBe(false); + expect(topSpy).toHaveBeenCalled(); + expect(bottomSpy).not.toHaveBeenCalled(); + + setMetrics(host, { scrollTop: 400 }); + scrollbar.update(); + + expect(scrollbar.isAtTop()).toBe(false); + expect(scrollbar.isAtBottom()).toBe(true); + expect(bottomSpy).toHaveBeenCalled(); + }); + + it('adds the disable-interaction class when kbqScrollbarDisableInteraction is set', () => { + const fixture = createComponent(TestScrollbarDisableInteractionHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + expect(host.classList.contains('kbq-private-scrollbar_disable-interaction')).toBe(true); + }); + + it('does not create track/thumb on a coarse pointer', () => { + const fixture = createComponent(TestScrollbarHost, [coarsePointerWindowProvider]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + expect(host.querySelector('.kbq-private-scrollbar-track')).toBeNull(); + }); + + it('hides the track when content does not overflow', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + + setMetrics(scrollEl, { clientHeight: 500, scrollHeight: 500 }); + setMetrics(track, { clientHeight: 500 }); + fixture.componentInstance.scrollbar().update(); + + expect(track.style.display).toBe('none'); + }); + + it('recovers when content overflow is detected late instead of getting stuck hidden — e.g. a CdkVirtualScrollViewport whose real scrollHeight is not known on the very first pass', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + + // In a real browser, `clientHeight` is 0 while `display: none` and the host's real + // size otherwise — jsdom doesn't compute layout, so this getter simulates that + // coupling to exercise the exact trap the un-hide-before-measure ordering guards + // against: hiding once must not make every later measurement read 0 forever after. + Object.defineProperty(track, 'clientHeight', { + configurable: true, + get(this: HTMLElement) { + return this.style.display === 'none' ? 0 : 200; + } + }); + + setMetrics(scrollEl, { clientHeight: 200, scrollHeight: 200 }); + fixture.componentInstance.scrollbar().update(); + expect(track.style.display).toBe('none'); + + setMetrics(scrollEl, { clientHeight: 200, scrollHeight: 2000 }); + fixture.componentInstance.scrollbar().update(); + + expect(track.style.display).not.toBe('none'); + }); + + it('sizes the thumb proportionally to the visible ratio, clamped to the min size', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 200, scrollHeight: 500 }); + // 206, not 200: the thumb travels within the track minus a 3px gap at each end (jsdom + // can't resolve --kbq-private-scrollbar-size-track-padding, so it falls back to the + // same 3px default scrollbar.scss ships), so travelLength = 206 - 2*3 = 200. + setMetrics(track, { clientHeight: 206 }); + fixture.componentInstance.scrollbar().update(); + + // ratio = ceil(200/500 * 100) / 100 = 0.4; thumbSize = max(0.4 * 200, minThumbSize) = 80 + expect(thumb.style.height).toBe('80px'); + expect(thumb.style.top).toBe('3px'); + }); + + it("keeps the thumb inset from the track's own start/end edges by cssTrackPadding at both scroll extremes", () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + setMetrics(track, { clientHeight: 106 }); + fixture.componentInstance.scrollbar().update(); + + expect(thumb.style.top).toBe('3px'); + + setMetrics(scrollEl, { scrollTop: 400 }); + fixture.componentInstance.scrollbar().update(); + + const thumbBottom = parseFloat(thumb.style.top) + parseFloat(thumb.style.height); + + expect(thumbBottom).toBe(103); + }); + }); + + describe('floating', () => { + it('reserves no layout space by default — the track/thumb float over the content', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + expect(scrollEl.style.paddingRight).toBe(''); + expect(scrollEl.style.paddingBottom).toBe(''); + }); + + it('kbqScrollbarFloating="false" reserves a gutter equal to the track dimension instead', () => { + const fixture = createComponent(TestScrollbarNonFloatingHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + expect(scrollEl.style.paddingRight).toBe('var(--kbq-private-scrollbar-size-track-dimension)'); + expect(scrollEl.style.paddingBottom).toBe('var(--kbq-private-scrollbar-size-track-dimension)'); + }); + }); + + describe('scrollTo / scrollToElement', () => { + it('scrollTo sets scrollTop on the effective scroll element', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + setMetrics(scrollEl, { scrollTop: 0 }); + fixture.componentInstance.scrollbar().scrollTo({ top: 42 }); + + expect(scrollEl.scrollTop).toBe(42); + }); + + it('scrollToTop / scrollToBottom scroll the vertical axis', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + setMetrics(scrollEl, { scrollTop: 250, scrollHeight: 500 }); + + fixture.componentInstance.scrollbar().scrollToTop(); + expect(scrollEl.scrollTop).toBe(0); + + fixture.componentInstance.scrollbar().scrollToBottom(); + expect(scrollEl.scrollTop).toBe(500); + }); + + it('scrollToElement computes independent top/left offsets from the target bounding rect', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const target = scrollEl.querySelector('div') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, scrollLeft: 0 }); + jest.spyOn(scrollEl, 'getBoundingClientRect').mockReturnValue({ top: 0, left: 0 } as DOMRect); + jest.spyOn(target, 'getBoundingClientRect').mockReturnValue({ top: 150, left: 200 } as DOMRect); + + // Only a `top` offset is given — `left` must stay unaffected, proving the two axes are + // independent rather than sharing a single offset value. + fixture.componentInstance.scrollbar().scrollToElement(target, { top: 10 }); + + expect(scrollEl.scrollTop).toBe(140); + expect(scrollEl.scrollLeft).toBe(200); + }); + + it('scrollStart / scrollEnd scroll the horizontal axis', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + setMetrics(scrollEl, { scrollLeft: 40, clientWidth: 100, scrollWidth: 500 }); + + fixture.componentInstance.scrollbar().scrollEnd(); + expect(scrollEl.scrollLeft).toBe(400); + + fixture.componentInstance.scrollbar().scrollStart(); + expect(scrollEl.scrollLeft).toBe(0); + }); + + it('routes scrollTo through CdkVirtualScrollViewport.scrollToOffset when delegated', () => { + const fixture = createComponent(TestScrollbarVirtualHost); + const scrollToOffsetSpy = jest + .spyOn(CdkVirtualScrollViewport.prototype, 'scrollToOffset') + .mockImplementation(() => {}); + + fixture.componentInstance.scrollbar().scrollTo({ top: 123 }); + + expect(scrollToOffsetSpy).toHaveBeenCalledWith(123, 'auto'); + }); + }); + + describe('RTL', () => { + it('toggles the _rtl class when Directionality changes', () => { + const mockDir = new MockDirectionality(); + const fixture = createComponent(TestScrollbarHost, [{ provide: Directionality, useValue: mockDir }]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + expect(host.classList.contains('kbq-private-scrollbar_rtl')).toBe(false); + + mockDir.value = 'rtl'; + mockDir.change.next('rtl'); + fixture.detectChanges(); + + expect(host.classList.contains('kbq-private-scrollbar_rtl')).toBe(true); + }); + + it('scrollEnd scrolls to a negative scrollLeft, and scrollStart back to 0, per the CSSOM View spec', () => { + const mockDir = new MockDirectionality(); + + mockDir.value = 'rtl'; + + const fixture = createComponent(TestScrollbarHost, [{ provide: Directionality, useValue: mockDir }]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + setMetrics(scrollEl, { clientWidth: 100, scrollWidth: 500 }); + + fixture.componentInstance.scrollbar().scrollEnd(); + expect(scrollEl.scrollLeft).toBe(-400); + + fixture.componentInstance.scrollbar().scrollStart(); + expect(scrollEl.scrollLeft).toBe(0); + }); + + it("positions the horizontal thumb from the physical left, normalizing RTL's negative scrollLeft — at scrollLeft 0 (RTL start) the thumb sits flush with the track's physical right edge", () => { + const mockDir = new MockDirectionality(); + + mockDir.value = 'rtl'; + + const fixture = createComponent(TestScrollbarHost, [{ provide: Directionality, useValue: mockDir }]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + const thumb = track.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { + scrollLeft: 0, + clientWidth: 100, + scrollWidth: 500, + clientHeight: 100, + scrollHeight: 100 + }); + setMetrics(track, { clientWidth: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(thumb.style.width).toBe('32px'); + expect(thumb.style.left).toBe('65px'); + }); + }); + + describe('visibility', () => { + // `TestBed.tick()` is needed on top of `detectChanges()` because the mode-change reaction + // runs in an `effect()`, which doesn't flush synchronously as part of `detectChanges()`. + const setVisibility = ( + fixture: ComponentFixture, + value: KbqScrollbarVisibility + ) => { + fixture.componentInstance.visibility = value; + fixture.detectChanges(); + TestBed.tick(); + }; + + it("reacts to kbqScrollbarVisibility switching to 'always' at runtime, without needing a hover", () => { + const fixture = createComponent(TestScrollbarVisibilityHost); + const visibleSpy = jest.fn(); + + fixture.componentInstance.scrollbar().visibilityChange.subscribe(visibleSpy); + + setVisibility(fixture, 'always'); + + expect(visibleSpy).toHaveBeenCalledWith(true); + }); + + it("hides again when switching away from 'always'", () => { + const fixture = createComponent(TestScrollbarVisibilityHost); + + setVisibility(fixture, 'always'); + + const visibleSpy = jest.fn(); + + fixture.componentInstance.scrollbar().visibilityChange.subscribe(visibleSpy); + + setVisibility(fixture, 'scroll'); + + expect(visibleSpy).toHaveBeenCalledWith(false); + }); + + it("switching to 'hover' while the pointer is already over the host shows it immediately, instead of waiting for the next pointerenter", () => { + const fixture = createComponent(TestScrollbarVisibilityHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + setVisibility(fixture, 'scroll'); + host.dispatchEvent(new Event('pointerenter')); + + const visibleSpy = jest.fn(); + + fixture.componentInstance.scrollbar().visibilityChange.subscribe(visibleSpy); + + setVisibility(fixture, 'hover'); + + expect(visibleSpy).toHaveBeenCalledWith(true); + }); + + it("stays invisible in 'hidden' mode even when hovered", () => { + const fixture = createComponent(TestScrollbarVisibilityHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + setVisibility(fixture, 'hidden'); + + const visibleSpy = jest.fn(); + + fixture.componentInstance.scrollbar().visibilityChange.subscribe(visibleSpy); + host.dispatchEvent(new Event('pointerenter')); + + expect(visibleSpy).not.toHaveBeenCalledWith(true); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(false); + }); + }); + + describe('kbqScrollbarViewport delegation', () => { + it('measures and scrolls the delegated viewport instead of the host', () => { + const fixture = createComponent(TestScrollbarViewportHost); + const viewport = fixture.debugElement.nativeElement.querySelector( + '[data-testid="viewport"]' + ) as HTMLElement; + + setMetrics(viewport, { scrollTop: 0 }); + + expect(fixture.componentInstance.scrollbar().getScrollElement()).toBe(viewport); + + fixture.componentInstance.scrollbar().scrollTo({ top: 42 }); + + expect(viewport.scrollTop).toBe(42); + }); + }); + + describe('composed via hostDirectives', () => { + it('initializes when used as hostDirectives: [KbqScrollbar] rather than the [kbqScrollbar] attribute', () => { + const fixture = createComponent(TestScrollbarComposedHost); + const host = fixture.debugElement.nativeElement as HTMLElement; + const scrollbar = fixture.debugElement.injector.get(KbqScrollbar); + + expect(host.classList.contains('kbq-private-scrollbar')).toBe(true); + expect(host.querySelector('.kbq-private-scrollbar-track_vertical')).toBeTruthy(); + + const scrollEl = getAutoViewport(host); + + setMetrics(scrollEl, { scrollTop: 0 }); + scrollbar.scrollTo({ top: 10 }); + + expect(scrollEl.scrollTop).toBe(10); + }); + }); + + describe('reach edges', () => { + it('reachTop / reachBottom fire for the vertical axis', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const topSpy = jest.fn(); + const bottomSpy = jest.fn(); + + fixture.componentInstance.scrollbar().reachTop.subscribe(topSpy); + fixture.componentInstance.scrollbar().reachBottom.subscribe(bottomSpy); + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + setMetrics(track, { clientHeight: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(topSpy).toHaveBeenCalled(); + expect(bottomSpy).not.toHaveBeenCalled(); + + setMetrics(scrollEl, { scrollTop: 400 }); + fixture.componentInstance.scrollbar().update(); + + expect(bottomSpy).toHaveBeenCalled(); + }); + + it('reachStart / reachEnd fire for the horizontal axis in LTR, matching the physical left/right edges', () => { + const fixture = createComponent(TestScrollbarDualOverflowHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const vTrack = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const hTrack = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + const startSpy = jest.fn(); + const endSpy = jest.fn(); + + fixture.componentInstance.scrollbar().reachStart.subscribe(startSpy); + fixture.componentInstance.scrollbar().reachEnd.subscribe(endSpy); + + setMetrics(scrollEl, { + scrollLeft: 0, + clientWidth: 100, + scrollWidth: 500, + clientHeight: 100, + scrollHeight: 500 + }); + setMetrics(vTrack, { clientHeight: 100 }); + setMetrics(hTrack, { clientWidth: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(startSpy).toHaveBeenCalled(); + expect(endSpy).not.toHaveBeenCalled(); + + setMetrics(scrollEl, { scrollLeft: 400 }); + fixture.componentInstance.scrollbar().update(); + + expect(endSpy).toHaveBeenCalled(); + }); + + it('reachStart / reachEnd flip which physical edge they fire on in RTL, matching scrollStart()/scrollEnd()', () => { + const mockDir = new MockDirectionality(); + + mockDir.value = 'rtl'; + + const fixture = createComponent(TestScrollbarDualOverflowHost, [ + { provide: Directionality, useValue: mockDir } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const vTrack = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const hTrack = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + const startSpy = jest.fn(); + const endSpy = jest.fn(); + + fixture.componentInstance.scrollbar().reachStart.subscribe(startSpy); + fixture.componentInstance.scrollbar().reachEnd.subscribe(endSpy); + + // scrollLeft: 0 is RTL's logical start — physically the right edge. + setMetrics(scrollEl, { + scrollLeft: 0, + clientWidth: 100, + scrollWidth: 500, + clientHeight: 100, + scrollHeight: 500 + }); + setMetrics(vTrack, { clientHeight: 100 }); + setMetrics(hTrack, { clientWidth: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(startSpy).toHaveBeenCalled(); + expect(endSpy).not.toHaveBeenCalled(); + + // scrollLeft: -400 is RTL's logical end — physically the left edge. + setMetrics(scrollEl, { scrollLeft: -400 }); + fixture.componentInstance.scrollbar().update(); + + expect(endSpy).toHaveBeenCalled(); + }); + }); + + describe('isAtTop / isAtBottom / isAtStart / isAtEnd', () => { + it('isAtTop / isAtBottom track the current vertical position, not just the moment it was reached', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const scrollbar = fixture.componentInstance.scrollbar(); + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + setMetrics(track, { clientHeight: 100 }); + scrollbar.update(); + + expect(scrollbar.isAtTop()).toBe(true); + expect(scrollbar.isAtBottom()).toBe(false); + + setMetrics(scrollEl, { scrollTop: 200 }); + scrollbar.update(); + + expect(scrollbar.isAtTop()).toBe(false); + expect(scrollbar.isAtBottom()).toBe(false); + + setMetrics(scrollEl, { scrollTop: 400 }); + scrollbar.update(); + + expect(scrollbar.isAtBottom()).toBe(true); + }); + + it('isAtStart / isAtEnd track the current horizontal position in LTR', () => { + const fixture = createComponent(TestScrollbarDualOverflowHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const vTrack = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const hTrack = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + const scrollbar = fixture.componentInstance.scrollbar(); + + setMetrics(scrollEl, { + scrollLeft: 0, + clientWidth: 100, + scrollWidth: 500, + clientHeight: 100, + scrollHeight: 500 + }); + setMetrics(vTrack, { clientHeight: 100 }); + setMetrics(hTrack, { clientWidth: 100 }); + scrollbar.update(); + + expect(scrollbar.isAtStart()).toBe(true); + expect(scrollbar.isAtEnd()).toBe(false); + + setMetrics(scrollEl, { scrollLeft: 400 }); + scrollbar.update(); + + expect(scrollbar.isAtStart()).toBe(false); + expect(scrollbar.isAtEnd()).toBe(true); + }); + + it('isAtStart / isAtEnd flip which physical edge they track in RTL', () => { + const mockDir = new MockDirectionality(); + + mockDir.value = 'rtl'; + + const fixture = createComponent(TestScrollbarDualOverflowHost, [ + { provide: Directionality, useValue: mockDir } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const vTrack = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const hTrack = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + const scrollbar = fixture.componentInstance.scrollbar(); + + // scrollLeft: 0 is RTL's logical start — physically the right edge. + setMetrics(scrollEl, { + scrollLeft: 0, + clientWidth: 100, + scrollWidth: 500, + clientHeight: 100, + scrollHeight: 500 + }); + setMetrics(vTrack, { clientHeight: 100 }); + setMetrics(hTrack, { clientWidth: 100 }); + scrollbar.update(); + + expect(scrollbar.isAtStart()).toBe(true); + expect(scrollbar.isAtEnd()).toBe(false); + + // scrollLeft: -400 is RTL's logical end — physically the left edge. + setMetrics(scrollEl, { scrollLeft: -400 }); + scrollbar.update(); + + expect(scrollbar.isAtStart()).toBe(false); + expect(scrollbar.isAtEnd()).toBe(true); + }); + + it('reads as both at-start and at-end when there is no overflow to scroll at all', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const scrollbar = fixture.componentInstance.scrollbar(); + + setMetrics(scrollEl, { clientHeight: 500, scrollHeight: 500 }); + scrollbar.update(); + + expect(scrollbar.isAtTop()).toBe(true); + expect(scrollbar.isAtBottom()).toBe(true); + }); + }); + + describe('update()', () => { + it('recomputes and emits kbqScrollbarUpdate', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + const updateSpy = jest.fn(); + + fixture.componentInstance.scrollbar().updated.subscribe(updateSpy); + + setMetrics(scrollEl, { scrollTop: 250, clientHeight: 100, scrollHeight: 500 }); + setMetrics(track, { clientHeight: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(updateSpy).toHaveBeenCalled(); + expect(thumb.style.top).not.toBe('0px'); + }); + }); +}); diff --git a/packages/components/scrollbar/private/scrollbar.ts b/packages/components/scrollbar/private/scrollbar.ts new file mode 100644 index 0000000000..cdf24f7353 --- /dev/null +++ b/packages/components/scrollbar/private/scrollbar.ts @@ -0,0 +1,886 @@ +import { Directionality } from '@angular/cdk/bidi'; +import { coerceCssPixelValue } from '@angular/cdk/coercion'; +import { SharedResizeObserver } from '@angular/cdk/observers/private'; +import { _CdkPrivateStyleLoader } from '@angular/cdk/private'; +import { CdkScrollable, CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { DOCUMENT } from '@angular/common'; +import { + afterNextRender, + booleanAttribute, + Component, + computed, + contentChild, + DestroyRef, + Directive, + effect, + ElementRef, + forwardRef, + inject, + InjectionToken, + Injector, + input, + NgZone, + numberAttribute, + OnDestroy, + output, + OutputEmitterRef, + Provider, + Renderer2, + signal, + ViewEncapsulation, + WritableSignal +} from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + KBQ_OVERFLOW_SHADOW_SOURCE, + KBQ_WINDOW, + kbqInjectNativeElement, + KbqOverflowShadowSource +} from '@koobiq/components/core'; +import { fromEvent, Observable, Subject } from 'rxjs'; + +/** When the custom track/thumb are shown. `'hidden'` never shows them, but scrolling stays fully functional. */ +export type KbqScrollbarVisibility = 'hover' | 'always' | 'scroll' | 'hidden'; + +/** Payload of `kbqScrollbarScrollChange`. */ +export type KbqScrollbarScrollChangeEvent = { + top: number; + left: number; +}; + +/** Options accepted by `KbqScrollbar.scrollTo`. */ +export type KbqScrollbarScrollToOptions = Partial<{ + top: number; + left: number; + behavior: ScrollBehavior; +}>; + +/** Options accepted by `KbqScrollbar.scrollToElement`. */ +export type KbqScrollbarScrollToElementOptions = Partial<{ + /** Extra gap to leave above the target, in px — e.g. so it doesn't end up under a sticky header. */ + top: number; + /** Extra gap to leave to the left of the target, in px. */ + left: number; + behavior: ScrollBehavior; +}>; + +/** Shape of the values that can be supplied via `KBQ_SCROLLBAR_CONFIG`. */ +export type KbqScrollbarConfig = Partial<{ + /** Default for the `kbqScrollbarVisibility` input — see `KbqScrollbarVisibility`. */ + visibility: KbqScrollbarVisibility; + /** Default for the `kbqScrollbarAutoHideDelay` input. */ + autoHideDelay: number; + /** Default for the `kbqScrollbarFloating` input. */ + floating: boolean; + /** + * Falls back to the native scrollbar entirely, bypassing the custom track/thumb — same as on a + * coarse pointer. App/module-level only, not a per-instance input: it decides whether the + * custom DOM gets built at all, which happens once at directive construction, so a value that + * could change per instance at runtime wouldn't have anything to react to anyway. + */ + native: boolean; + /** Default for the `kbqScrollbarDisableInteraction` input. */ + disableInteraction: boolean; +}>; + +/** + * Default values for `kbqScrollbar` inputs. `autoHideDelay` mirrors + * `KBQ_SCROLLBAR_OPTIONS_DEFAULT_CONFIG` (`../scrollbar.types.ts`) to keep UX parity with the + * `overlayscrollbars`-based `kbq-scrollbar`. + * + * @docs-private + */ +export const KBQ_SCROLLBAR_DEFAULT_CONFIG: Required = { + visibility: 'hover', + autoHideDelay: 100, + floating: true, + native: false, + disableInteraction: false +}; + +/** + * Injection token for app/module-level `kbqScrollbar` defaults. Resolves to + * `KBQ_SCROLLBAR_DEFAULT_CONFIG` out of the box, so `KbqScrollbar` works without requiring any + * provider to be registered. + * + * @docs-private + */ +export const KBQ_SCROLLBAR_CONFIG = new InjectionToken('KBQ_SCROLLBAR_CONFIG', { + factory: () => KBQ_SCROLLBAR_DEFAULT_CONFIG +}); + +/** Registers app/module-level defaults for `kbqScrollbar` — provide it wherever `KBQ_SCROLLBAR_CONFIG` should resolve to `config` instead of `KBQ_SCROLLBAR_DEFAULT_CONFIG`. */ +export const kbqScrollbarConfigProvider = (config: KbqScrollbarConfig): Provider => ({ + provide: KBQ_SCROLLBAR_CONFIG, + useValue: config +}); + +/** The two axes a `kbqScrollbar` always builds a track for — see `updateAxis`. */ +type Axis = 'vertical' | 'horizontal'; + +const AXES: readonly Axis[] = ['vertical', 'horizontal']; + +const clamp = (value: number, min: number, max: number): number => Math.min(max, Math.max(min, value)); + +type DragContext = { + axis: Axis; + pointerId: number; + trackStart: number; + trackTravel: number; + grabOffset: number; + scrollRange: number; +}; + +/** + * Component used to load the `.kbq-private-scrollbar` styles. + */ +@Component({ + selector: 'scrollbar-style-loader', + template: '', + styleUrl: 'scrollbar.scss', + encapsulation: ViewEncapsulation.None +}) +class KbqScrollbarStyleLoader {} + +/** + * Marks the element that is actually scrollable when it doesn't coincide with the `kbqScrollbar` + * host — e.g. a `` nested inside a `
`. When + * present, `KbqScrollbar` measures, listens to, and scrolls this element instead of its own host. + */ +@Directive({ + selector: '[kbqScrollbarViewport]', + host: { class: 'kbq-private-scrollbar-viewport' } +}) +export class KbqScrollbarViewport { + /** @docs-private */ + readonly elementRef = inject>(ElementRef); + /** @docs-private Set only when the viewport element also hosts `CdkVirtualScrollViewport`. */ + readonly virtualScrollViewport = inject(CdkVirtualScrollViewport, { optional: true, self: true }); +} + +/** + * Dependency-free custom scrollbar directive. Draws its own track/thumb via `Renderer2` on top of + * the host's content — no third-party library, unlike `kbqScrollbar`/`kbq-scrollbar` + * (`../scrollbar.directive.ts`/`../scrollbar.component.ts`), which wrap `overlayscrollbars`. + * + * Usable as a plain attribute (`
`) or composed via `hostDirectives: + * [KbqScrollbar]` in another component. + * + * On coarse-pointer (touch) devices the custom track/thumb are never created — scrolling stays + * native. Bypassed the same way when `KbqScrollbarConfig.native` is set. + */ +@Directive({ + selector: '[kbqScrollbar]', + providers: [{ provide: KBQ_OVERFLOW_SHADOW_SOURCE, useExisting: forwardRef(() => KbqScrollbar) }], + host: { + class: 'kbq-private-scrollbar', + '[class.kbq-private-scrollbar_rtl]': 'rtl()', + '[class.kbq-private-scrollbar_disable-interaction]': 'disableInteraction()' + }, + hostDirectives: [CdkScrollable], + exportAs: 'kbqScrollbar' +}) +export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { + private readonly renderer = inject(Renderer2); + private readonly hostElement = kbqInjectNativeElement(); + private readonly document = inject(DOCUMENT); + private readonly window = inject(KBQ_WINDOW); + private readonly sharedResizeObserver = inject(SharedResizeObserver); + private readonly destroyRef = inject(DestroyRef); + private readonly zone = inject(NgZone); + private readonly injector = inject(Injector); + private readonly config = inject(KBQ_SCROLLBAR_CONFIG); + private readonly directionality = inject(Directionality, { optional: true }); + + /** + * See `KbqScrollbarConfig.native`. Read once from config rather than an `input()` — whether the + * custom track/thumb DOM gets built at all is decided once in `afterNextRender`, so there's no + * later point at which a per-instance change could take effect. + */ + private readonly native = this.config.native ?? KBQ_SCROLLBAR_DEFAULT_CONFIG.native; + + /** + * Used from the host binding. A signal (not a plain method) so the class + * updates even when the host lives inside an `OnPush` ancestor that isn't otherwise dirty — + * `dir.change` is a plain `EventEmitter`, not something Angular's zone/OnPush tracking picks + * up on its own. Same reasoning as `overflow` in `core/overflow-shadow/overflow-shadow.ts`. + * + * @docs-private + */ + protected readonly rtl = signal(this.directionality?.value === 'rtl'); + + private readonly viewport = contentChild(KbqScrollbarViewport); + + /** Controls when the custom track/thumb are shown — see `KbqScrollbarVisibility`. */ + readonly visibility = input( + this.config.visibility ?? KBQ_SCROLLBAR_DEFAULT_CONFIG.visibility, + { alias: 'kbqScrollbarVisibility' } + ); + + /** Delay, in ms, before auto-hiding after a scroll in `'scroll'` visibility mode. */ + readonly autoHideDelay = input(this.config.autoHideDelay ?? KBQ_SCROLLBAR_DEFAULT_CONFIG.autoHideDelay, { + alias: 'kbqScrollbarAutoHideDelay', + transform: numberAttribute + }); + + /** `true` (default) floats the track/thumb over the content; `false` reserves layout space like a native scrollbar. */ + readonly floating = input(this.config.floating ?? KBQ_SCROLLBAR_DEFAULT_CONFIG.floating, { + alias: 'kbqScrollbarFloating', + transform: booleanAttribute + }); + + /** Keeps scrolling working but disables drag-on-thumb and click-on-track. */ + readonly disableInteraction = input( + this.config.disableInteraction ?? KBQ_SCROLLBAR_DEFAULT_CONFIG.disableInteraction, + { alias: 'kbqScrollbarDisableInteraction', transform: booleanAttribute } + ); + + /** Emits the scroll position on every scroll event. */ + readonly scrollChange = output({ alias: 'kbqScrollbarScrollChange' }); + /** Emits when the vertical axis reaches its top. */ + readonly reachTop = output({ alias: 'kbqScrollbarReachTop' }); + /** Emits when the vertical axis reaches its bottom. */ + readonly reachBottom = output({ alias: 'kbqScrollbarReachBottom' }); + /** Emits when the horizontal axis reaches its logical start — the right edge in RTL, left in LTR. */ + readonly reachStart = output({ alias: 'kbqScrollbarReachStart' }); + /** Emits when the horizontal axis reaches its logical end — the left edge in RTL, right in LTR. */ + readonly reachEnd = output({ alias: 'kbqScrollbarReachEnd' }); + /** Emits whenever the custom track/thumb show or hide. */ + readonly visibilityChange = output({ alias: 'kbqScrollbarVisibilityChange' }); + /** + * Emits once, after the directive finishes its initial setup — including the custom + * track/thumb render when one is built, but also under `native`/coarse pointer, where there's + * no track/thumb of its own to render. + */ + readonly initialized = output({ alias: 'kbqScrollbarInitialized' }); + /** + * Emits after every recompute (resize, content change, or manual `update()`) — thumb + * size/position when the custom track/thumb are built, or just the overflow/edge measurement + * behind `isAtTop`-family signals and `reach*` otherwise (`native`, coarse pointer). + */ + readonly updated = output({ alias: 'kbqScrollbarUpdated' }); + + // Separate from the `*At*` signals below: whether an axis overflows at all, so a track with + // nothing to scroll reads as trivially at both of its own edges (see the early return in + // `updateAxis`) without that no-overflow state overwriting — and being mistaken later for — a + // real, measured edge position once content does overflow. + private readonly verticalOverflows = signal(false); + private readonly horizontalOverflows = signal(false); + + private readonly verticalAtTop = signal(false); + private readonly verticalAtBottom = signal(false); + private readonly horizontalAtLeft = signal(false); + private readonly horizontalAtRight = signal(false); + + /** Whether the vertical axis is currently scrolled all the way to the top. */ + readonly isAtTop = computed(() => !this.verticalOverflows() || this.verticalAtTop()); + /** Whether the vertical axis is currently scrolled all the way to the bottom. */ + readonly isAtBottom = computed(() => !this.verticalOverflows() || this.verticalAtBottom()); + /** + * Whether the horizontal axis is currently scrolled to its logical start — the right edge in + * RTL, left in LTR. Matches `scrollStart()`/`reachStart`. + */ + readonly isAtStart = computed( + () => !this.horizontalOverflows() || (this.rtl() ? this.horizontalAtRight() : this.horizontalAtLeft()) + ); + /** + * Whether the horizontal axis is currently scrolled to its logical end — the left edge in + * RTL, right in LTR. Matches `scrollEnd()`/`reachEnd`. + */ + readonly isAtEnd = computed( + () => !this.horizontalOverflows() || (this.rtl() ? this.horizontalAtLeft() : this.horizontalAtRight()) + ); + + private readonly scrollSubject = new Subject(); + /** @docs-private Implementation of `KbqOverflowShadowSource`. */ + readonly onScroll: Observable = this.scrollSubject.asObservable(); + + private readonly tracks = new Map(); + private readonly thumbs = new Map(); + + // Fallback values for when `readCssTokens` can't resolve the real custom property (e.g. no + // `document`, or run before the first render) — otherwise overwritten with the real + // `scrollbar.scss` token values below. Deliberately not configurable except via CSS — see + // `--kbq-private-scrollbar-size-thumb-min-size`/`--kbq-private-scrollbar-size-track-padding`. + private cssMinThumbSize = 32; + private cssTrackPadding = 3; + private isCoarsePointer = false; + private isVisible = false; + private isPointerOver = false; + private autoHideTimeoutId: ReturnType | undefined; + private dragContext: DragContext | null = null; + + /** + * Auto-created scroll wrapper, only when there's no explicit `kbqScrollbarViewport` and the + * custom track/thumb are actually being rendered. The track/thumb are `position: absolute` + * siblings of this wrapper on the (non-scrolling) host — if they were children of the element + * that itself has `overflow: auto` (i.e. the host, when the host is also the scroll element), + * they'd be clipped and scrolled away with the rest of the content instead of staying fixed as + * an overlay. + */ + private autoViewport: HTMLElement | null = null; + + constructor() { + inject(_CdkPrivateStyleLoader).load(KbqScrollbarStyleLoader); + + afterNextRender(() => { + this.isCoarsePointer = this.window.matchMedia?.('(pointer: coarse)').matches ?? false; + this.readCssTokens(); + + const buildingCustomUi = !this.native && !this.isCoarsePointer; + + this.zone.runOutsideAngular(() => { + if (buildingCustomUi && !this.viewport()) { + this.autoViewport = this.createAutoViewport(); + } + + this.applyOverflow(); + this.wireScroll(); + this.wireResize(); + this.wireDirectionality(); + + if (buildingCustomUi) { + this.applyNativeHiding(); + this.applyGutterReservation(); + this.buildDom(); + this.wireGlobalDragListeners(); + this.wireVisibility(); + } + + // Runs even under `native`/coarse pointer — `isAtTop`-family signals and `reach*` + // outputs still need an initial measurement, not just the custom track/thumb. + this.recompute(); + + // A `kbqScrollbarViewport` backed by `CdkVirtualScrollViewport` (or any scroll + // element whose own layout isn't settled yet) can make the very first recompute + // under-measure — a 0-height track, or overflow that hasn't shown up yet. Its outer + // box may never resize again afterwards, so there's no guaranteed later trigger to + // correct it — retry once, next frame. + this.window.requestAnimationFrame?.(() => this.recompute()); + }); + + this.emit(this.initialized, undefined); + }); + } + + /** Moves the host's existing content into a new wrapper so the host itself can stay a + * non-scrolling, non-clipping positioning context for the track/thumb overlay. */ + private createAutoViewport(): HTMLElement { + const wrapper = this.renderer.createElement('div') as HTMLElement; + + this.renderer.addClass(wrapper, 'kbq-private-scrollbar-viewport'); + + for (const child of Array.from(this.hostElement.childNodes)) { + this.renderer.appendChild(wrapper, child); + } + + this.renderer.appendChild(this.hostElement, wrapper); + + return wrapper; + } + + ngOnDestroy(): void { + clearTimeout(this.autoHideTimeoutId); + this.tracks.forEach((track) => this.renderer.removeChild(this.hostElement, track)); + this.tracks.clear(); + this.thumbs.clear(); + } + + /** Scrolls the effective scroll element (or delegates to `kbqScrollbarViewport`'s virtual scroll viewport). */ + scrollTo(options: KbqScrollbarScrollToOptions): void { + const virtualScrollViewport = this.viewport()?.virtualScrollViewport; + + if (virtualScrollViewport) { + virtualScrollViewport.scrollToOffset(options.top ?? options.left ?? 0, options.behavior ?? 'auto'); + + return; + } + + const element = this.scrollElement; + + // Feature-detected: some environments (older WebViews, this repo's jsdom-based Jest + // setup) don't implement `Element.scrollTo`. Falling back to direct scrollTop/scrollLeft + // assignment loses `behavior: 'smooth'` there, but stays functionally correct everywhere. + if (typeof element.scrollTo === 'function') { + element.scrollTo({ top: options.top, left: options.left, behavior: options.behavior }); + + return; + } + + if (options.top !== undefined) element.scrollTop = options.top; + if (options.left !== undefined) element.scrollLeft = options.left; + } + + /** Scrolls `target` (an element, or a selector resolved against the scroll element) into view. */ + scrollToElement(target: HTMLElement | string, options?: KbqScrollbarScrollToElementOptions): void { + const element = typeof target === 'string' ? this.scrollElement.querySelector(target) : target; + + if (!element) return; + + const scrollElementRect = this.scrollElement.getBoundingClientRect(); + const targetRect = element.getBoundingClientRect(); + const top = targetRect.top - scrollElementRect.top + this.scrollElement.scrollTop - (options?.top ?? 0); + const left = targetRect.left - scrollElementRect.left + this.scrollElement.scrollLeft - (options?.left ?? 0); + + this.scrollTo({ top, left, behavior: options?.behavior }); + } + + /** Scrolls to the start of the vertical axis. */ + scrollToTop(behavior?: ScrollBehavior): void { + this.scrollTo({ top: 0, behavior }); + } + + /** Scrolls to the end of the vertical axis. */ + scrollToBottom(behavior?: ScrollBehavior): void { + this.scrollTo({ top: this.scrollElement.scrollHeight, behavior }); + } + + /** + * Scrolls to the logical start of the horizontal axis — the right edge in RTL, left in LTR. + * Always `left: 0`: per the CSSOM View spec, `scrollLeft` rests at `0` at the start in both + * directions and moves negative toward the end in RTL, positive in LTR. + */ + scrollStart(behavior?: ScrollBehavior): void { + this.scrollTo({ left: 0, behavior }); + } + + /** Scrolls to the logical end of the horizontal axis — the left edge in RTL, right in LTR. */ + scrollEnd(behavior?: ScrollBehavior): void { + const element = this.scrollElement; + const maxScroll = element.scrollWidth - element.clientWidth; + + this.scrollTo({ left: this.rtl() ? -maxScroll : maxScroll, behavior }); + } + + /** + * Forces a recompute of track/thumb size and position. Scroll and viewport-box-size changes + * are handled automatically via `SharedResizeObserver`; call this manually when content grows + * `scrollHeight`/`scrollWidth` without the scroll element's own box size changing — a resize + * observer can't see that on its own (same caveat as `core/overflow-shadow/overflow-shadow.ts`). + */ + update(): void { + this.recompute(); + } + + /** @docs-private Implementation of `KbqOverflowShadowSource`. */ + getScrollElement(): HTMLElement | null { + return this.scrollElement; + } + + private get scrollElement(): HTMLElement { + return this.viewport()?.elementRef.nativeElement ?? this.autoViewport ?? this.hostElement; + } + + private readCssTokens(): void { + this.cssMinThumbSize = this.readCssPx('--kbq-private-scrollbar-size-thumb-min-size', this.cssMinThumbSize); + this.cssTrackPadding = this.readCssPx('--kbq-private-scrollbar-size-track-padding', this.cssTrackPadding); + } + + private readCssPx(property: string, fallback: number): number { + const raw = this.window.getComputedStyle(this.hostElement).getPropertyValue(property); + const parsed = parseFloat(raw); + + return Number.isNaN(parsed) ? fallback : parsed; + } + + private wireScroll(): void { + fromEvent(this.scrollElement, 'scroll', { passive: true }) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => { + this.scrollSubject.next(); + this.recompute(); + this.onUserScroll(); + }); + } + + private wireResize(): void { + this.sharedResizeObserver + .observe(this.scrollElement) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.recompute()); + } + + private wireDirectionality(): void { + this.directionality?.change?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((direction) => { + this.rtl.set(direction === 'rtl'); + this.recompute(); + }); + } + + private applyNativeHiding(): void { + this.renderer.addClass(this.scrollElement, 'kbq-private-scrollbar_hide-native'); + } + + /** + * Establishes native scroll on both axes — this directive doesn't just decorate an + * already-`overflow: auto` element, it makes the effective scroll element scrollable itself + * (the custom track/thumb are a UI layer on top of real native scrolling, not a replacement + * for it). + */ + private applyOverflow(): void { + this.renderer.setStyle(this.scrollElement, 'overflowY', 'auto'); + this.renderer.setStyle(this.scrollElement, 'overflowX', 'auto'); + } + + private applyGutterReservation(): void { + if (this.floating()) return; + + const dimension = 'var(--kbq-private-scrollbar-size-track-dimension)'; + + this.renderer.setStyle(this.scrollElement, this.rtl() ? 'paddingLeft' : 'paddingRight', dimension); + this.renderer.setStyle(this.scrollElement, 'paddingBottom', dimension); + } + + private buildDom(): void { + for (const axis of AXES) { + this.buildTrack(axis); + } + } + + private buildTrack(axis: Axis): void { + const track = this.renderer.createElement('div') as HTMLElement; + + this.renderer.addClass(track, 'kbq-private-scrollbar-track'); + this.renderer.addClass(track, `kbq-private-scrollbar-track_${axis}`); + this.renderer.setAttribute(track, 'aria-hidden', 'true'); + + const thumb = this.renderer.createElement('div') as HTMLElement; + + this.renderer.addClass(thumb, 'kbq-private-scrollbar-thumb'); + this.renderer.appendChild(track, thumb); + this.renderer.appendChild(this.hostElement, track); + + this.tracks.set(axis, track); + this.thumbs.set(axis, thumb); + + this.wireTrackInteraction(axis, track, thumb); + } + + private wireTrackInteraction(axis: Axis, track: HTMLElement, thumb: HTMLElement): void { + fromEvent(thumb, 'pointerdown') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((event) => { + if (this.disableInteraction()) return; + this.beginInteraction(axis, track, thumb, event, false); + }); + + // "Jump to click": clicking the track (not the thumb itself) re-centers the thumb under + // the pointer and continues as a drag if the button stays down — same math as a real + // drag-start, just with the grab offset hardcoded to the thumb's center. + fromEvent(track, 'pointerdown') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((event) => { + if (this.disableInteraction() || event.target === thumb) return; + this.beginInteraction(axis, track, thumb, event, true); + }); + } + + private wireGlobalDragListeners(): void { + fromEvent(this.document, 'pointermove') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((event) => { + if (!this.dragContext || event.pointerId !== this.dragContext.pointerId) return; + + if (!event.buttons) { + this.endDrag(); + + return; + } + + this.applyDrag(this.dragContext.axis === 'vertical' ? event.clientY : event.clientX); + }); + + fromEvent(this.document, 'pointerup') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((event) => { + if (!this.dragContext || event.pointerId !== this.dragContext.pointerId) return; + this.endDrag(); + }); + } + + private beginInteraction( + axis: Axis, + track: HTMLElement, + thumb: HTMLElement, + event: PointerEvent, + centerOnThumb: boolean + ): void { + event.preventDefault(); + + const isVertical = axis === 'vertical'; + const trackRect = track.getBoundingClientRect(); + const thumbRect = thumb.getBoundingClientRect(); + // Inset by the same `cssTrackPadding` gap `updateAxis` positions the thumb within, so a + // drag maps 1:1 to the thumb's actual travel range instead of the track's full box. + const trackStart = (isVertical ? trackRect.top : trackRect.left) + this.cssTrackPadding; + const trackLength = (isVertical ? trackRect.height : trackRect.width) - 2 * this.cssTrackPadding; + const thumbSize = isVertical ? thumbRect.height : thumbRect.width; + const thumbStart = isVertical ? thumbRect.top : thumbRect.left; + const pointerCoord = isVertical ? event.clientY : event.clientX; + + const scrollEl = this.scrollElement; + const scrollRange = isVertical + ? scrollEl.scrollHeight - scrollEl.clientHeight + : scrollEl.scrollWidth - scrollEl.clientWidth; + + this.dragContext = { + axis, + pointerId: event.pointerId, + trackStart, + trackTravel: trackLength - thumbSize, + grabOffset: centerOnThumb ? thumbSize / 2 : pointerCoord - thumbStart, + scrollRange + }; + + this.setVisible(true); + this.renderer.addClass(this.hostElement, 'kbq-private-scrollbar_dragging'); + this.applyDrag(pointerCoord); + } + + private applyDrag(pointerCoord: number): void { + const ctx = this.dragContext; + + if (!ctx || ctx.trackTravel <= 0) return; + + const pointerRelative = pointerCoord - ctx.trackStart; + // Physical ratio: 0 at the track's physical start (left/top), 1 at its physical end. + const ratio = clamp((pointerRelative - ctx.grabOffset) / ctx.trackTravel, 0, 1); + + if (ctx.axis === 'vertical') { + this.scrollElement.scrollTop = ratio * ctx.scrollRange; + } else if (this.rtl()) { + // RTL `scrollLeft` runs 0 (physical right) to -scrollRange (physical left) — invert + // the physical ratio derived above onto that range. + this.scrollElement.scrollLeft = (ratio - 1) * ctx.scrollRange; + } else { + this.scrollElement.scrollLeft = ratio * ctx.scrollRange; + } + } + + private endDrag(): void { + if (!this.dragContext) return; + + this.dragContext = null; + this.renderer.removeClass(this.hostElement, 'kbq-private-scrollbar_dragging'); + } + + private wireVisibility(): void { + fromEvent(this.hostElement, 'pointerenter') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => { + this.isPointerOver = true; + + if (this.visibility() === 'hover') this.setVisible(true); + }); + + fromEvent(this.hostElement, 'pointerleave') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => { + this.isPointerOver = false; + + if (this.visibility() === 'hover' && !this.dragContext) this.setVisible(false); + }); + + // Reacts to `kbqScrollbarVisibility` changing at runtime, not just its value at first + // render — sets the new mode's steady state. `isPointerOver` is read (not tracked) so this + // only reruns on an actual mode change, using whatever the live hover state is at that + // moment; ongoing hover-driven toggling still comes from the listeners above. 'scroll' + // mode's temporary reveal-on-scroll keeps coming from onUserScroll()/showTemporarily() — + // 'hidden' starts from the same steady state but never gets revealed from anywhere. + effect( + () => { + const mode = this.visibility(); + + if (mode === 'always') { + this.setVisible(true); + } else if (mode === 'hover') { + this.setVisible(this.isPointerOver); + } else { + this.setVisible(false); + } + }, + { injector: this.injector } + ); + } + + private onUserScroll(): void { + this.emit(this.scrollChange, { top: this.scrollElement.scrollTop, left: this.scrollElement.scrollLeft }); + + if (this.visibility() === 'scroll') this.showTemporarily(); + } + + private showTemporarily(): void { + this.setVisible(true); + clearTimeout(this.autoHideTimeoutId); + this.autoHideTimeoutId = setTimeout(() => { + if (!this.dragContext) this.setVisible(false); + }, this.autoHideDelay()); + } + + private setVisible(value: boolean): void { + if (this.isVisible === value) return; + + this.isVisible = value; + this.renderer[value ? 'addClass' : 'removeClass'](this.hostElement, 'kbq-private-scrollbar_visible'); + this.emit(this.visibilityChange, value); + } + + private recompute(): void { + for (const axis of AXES) { + this.updateAxis(axis); + } + + // Corner-avoidance only makes sense between two custom tracks — a no-op anyway without + // them, but skip it outright under `native`/coarse pointer. + if (!this.native && !this.isCoarsePointer) { + this.updateCornerAvoidance(); + } + + this.emit(this.updated, undefined); + } + + /** + * Marks each track with whether the other one is currently also visible, so `scrollbar.scss` + * can shorten its length by the other's thickness — otherwise they'd overlap by one + * track-thickness in the shared corner, the way native scrollbars leave an empty square where + * they meet. A no-op whenever content only overflows on one axis. + */ + private updateCornerAvoidance(): void { + const verticalTrack = this.tracks.get('vertical'); + const horizontalTrack = this.tracks.get('horizontal'); + const bothVisible = + !!verticalTrack && + !!horizontalTrack && + verticalTrack.style.display !== 'none' && + horizontalTrack.style.display !== 'none'; + + if (verticalTrack) { + this.renderer[bothVisible ? 'addClass' : 'removeClass']( + verticalTrack, + 'kbq-private-scrollbar-track_has-horizontal' + ); + } + + if (horizontalTrack) { + this.renderer[bothVisible ? 'addClass' : 'removeClass']( + horizontalTrack, + 'kbq-private-scrollbar-track_has-vertical' + ); + } + } + + /** + * Measures overflow/edge state for `isAtTop`-family signals and `reach*` outputs, then — only + * when this axis actually has a track/thumb built (i.e. not `native`, not a coarse pointer) — + * paints the thumb's size and position. The measurement half runs unconditionally: those + * signals/outputs are meant to keep working even when there's no custom UI of our own to show. + */ + private updateAxis(axis: Axis): void { + const scrollEl = this.scrollElement; + const isVertical = axis === 'vertical'; + const viewportSize = isVertical ? scrollEl.clientHeight : scrollEl.clientWidth; + const contentSize = isVertical ? scrollEl.scrollHeight : scrollEl.scrollWidth; + const rawScrollOffset = isVertical ? scrollEl.scrollTop : scrollEl.scrollLeft; + const overflows = isVertical ? this.verticalOverflows : this.horizontalOverflows; + const track = this.tracks.get(axis); + const thumb = this.thumbs.get(axis); + + if (contentSize <= viewportSize) { + if (track) this.renderer.setStyle(track, 'display', 'none'); + // Nothing to scroll on this axis — `isAtTop()`/`isAtBottom()`-family signals short- + // circuit to `true` off this flag, without touching the real edge-position signals + // below (those stay at whatever they last genuinely measured). + overflows.set(false); + + return; + } + + overflows.set(true); + + const scrollRange = contentSize - viewportSize; + // Physical distance from the left/top edge, in [0, scrollRange] — normalizes RTL's negative + // `scrollLeft` range (0 at the right edge, down to -scrollRange at the left) onto the same + // physical axis the thumb's CSS position is anchored to. `checkReachedEdges` below + // re-derives the logical start/end from this physical value itself. + const scrollOffset = !isVertical && this.rtl() ? scrollRange + rawScrollOffset : rawScrollOffset; + + this.checkReachedEdges(axis, scrollOffset, scrollRange); + + // No custom track/thumb to paint for this axis — `native`/coarse pointer, or the DOM for + // it hasn't been built. Measurement above already ran either way. + if (!track || !thumb) return; + + // Un-hide *before* measuring, not after: a `CdkVirtualScrollViewport` (or any scroll + // element whose layout isn't settled on the very first pass) can make an earlier call + // land in the branch above and set `display: none`. A hidden element always measures a + // 0 clientHeight/Width, so if we measured first and only unhid on success, a stale + // `display: none` would make every subsequent pass measure 0 and bail out below, + // trapping the track hidden forever even once real content overflow is detected here. + this.renderer.removeStyle(track, 'display'); + + const trackLength = isVertical ? track.clientHeight : track.clientWidth; + + // Track not laid out yet (e.g. inserted this same tick) — a later pass will retry. + if (trackLength <= 0) return; + + // The thumb travels within the track minus a `cssTrackPadding` gap at *each* end, so it + // never sits flush against the track's own start/end edge — matching the CSS insets + // already applied on its cross-axis (`scrollbar.scss`'s `left`/`right`/`top`/`bottom` + // rules), just computed here since this axis is otherwise entirely JS-driven. + const travelLength = trackLength - 2 * this.cssTrackPadding; + + // Round the ratio up so the thumb never reads as undersized from float error. + const ratio = Math.min(1, Math.ceil((viewportSize / contentSize) * 100) / 100); + const thumbSize = Math.min(travelLength, Math.max(ratio * travelLength, this.cssMinThumbSize)); + const scrollRatio = scrollRange > 0 ? scrollOffset / scrollRange : 0; + const thumbOffset = this.cssTrackPadding + scrollRatio * (travelLength - thumbSize); + + if (isVertical) { + this.renderer.setStyle(thumb, 'height', coerceCssPixelValue(thumbSize)); + this.renderer.setStyle(thumb, 'top', coerceCssPixelValue(thumbOffset)); + } else { + this.renderer.setStyle(thumb, 'width', coerceCssPixelValue(thumbSize)); + this.renderer.setStyle(thumb, 'left', coerceCssPixelValue(thumbOffset)); + } + } + + /** + * The writable `isAtTop`/`isAtStart`-family signals for one physical axis — `[start, end]`, + * both always physically named (top/bottom, left/right); the public `isAtStart`/`isAtEnd` + * computed signals are what re-map "left/right" onto RTL-aware "start/end". + */ + private edgeSignals(axis: Axis): readonly [WritableSignal, WritableSignal] { + return axis === 'vertical' + ? [this.verticalAtTop, this.verticalAtBottom] + : [this.horizontalAtLeft, this.horizontalAtRight]; + } + + private checkReachedEdges(axis: Axis, scrollOffset: number, scrollRange: number): void { + const atPhysicalStart = scrollOffset <= 0; + const atPhysicalEnd = scrollRange > 0 && Math.round(scrollOffset) >= Math.round(scrollRange); + const [startState, endState] = this.edgeSignals(axis); + + // Vertical has no RTL concept — top is always physically first. Horizontal's physical + // left/right flips which *logical* edge (reachStart/reachEnd) it maps to in RTL, matching + // scrollStart()/scrollEnd()'s own RTL-aware semantics. + const physicalStartEmitter = axis === 'vertical' ? this.reachTop : this.rtl() ? this.reachEnd : this.reachStart; + const physicalEndEmitter = + axis === 'vertical' ? this.reachBottom : this.rtl() ? this.reachStart : this.reachEnd; + + if (atPhysicalStart && !startState()) { + this.emit(physicalStartEmitter, undefined); + } + + startState.set(atPhysicalStart); + + if (atPhysicalEnd && !endState()) { + this.emit(physicalEndEmitter, undefined); + } + + endState.set(atPhysicalEnd); + } + + private emit(emitter: OutputEmitterRef, value: T): void { + this.zone.run(() => emitter.emit(value)); + } +} diff --git a/packages/docs-examples/components/scrollbar/index.ts b/packages/docs-examples/components/scrollbar/index.ts index 45f5dd136d..66be5e0f2b 100644 --- a/packages/docs-examples/components/scrollbar/index.ts +++ b/packages/docs-examples/components/scrollbar/index.ts @@ -1,15 +1,28 @@ import { NgModule } from '@angular/core'; import { KBQ_SCROLLBAR_OPTIONS_DEFAULT_CONFIG_PROVIDER } from '@koobiq/components/scrollbar'; +import { PrivateScrollbarScrollToExample } from './private-scrollbar-scroll-to/private-scrollbar-scroll-to-example'; +import { PrivateScrollbarVirtualScrollExample } from './private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example'; +import { PrivateScrollbarVisibilityExample } from './private-scrollbar-visibility/private-scrollbar-visibility-example'; import { ScrollbarOverviewExample } from './scrollbar-overview/scrollbar-overview-example'; import { ScrollbarScrollToTopExample } from './scrollbar-scroll-to-top/scrollbar-scroll-to-top-example'; import { ScrollbarWithCustomConfigExample } from './scrollbar-with-custom-config/scrollbar-with-custom-config-example'; -export { ScrollbarOverviewExample, ScrollbarScrollToTopExample, ScrollbarWithCustomConfigExample }; +export { + PrivateScrollbarScrollToExample, + PrivateScrollbarVirtualScrollExample, + PrivateScrollbarVisibilityExample, + ScrollbarOverviewExample, + ScrollbarScrollToTopExample, + ScrollbarWithCustomConfigExample +}; const EXAMPLES = [ ScrollbarScrollToTopExample, ScrollbarOverviewExample, - ScrollbarWithCustomConfigExample + ScrollbarWithCustomConfigExample, + PrivateScrollbarVisibilityExample, + PrivateScrollbarVirtualScrollExample, + PrivateScrollbarScrollToExample ]; @NgModule({ diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts new file mode 100644 index 0000000000..c4f2316343 --- /dev/null +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts @@ -0,0 +1,102 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { KbqButtonModule } from '@koobiq/components/button'; +import { KbqScrollbar } from '@koobiq/components/scrollbar/private'; + +/** + * @title Scrollbar scrollTo methods + */ +@Component({ + selector: 'private-scrollbar-scroll-to-example', + imports: [KbqScrollbar, KbqButtonModule], + template: ` +
+ + + + + +
+ +
+

+ In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists + of an attacker submitting many possible keys or passwords with the hope of eventually guessing + correctly. This strategy can theoretically be used to break any form of encryption that is not + information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of + successfully guessing the key is negligible. +

+

+ Brute-force attacks can be made less effective by implementing key stretching techniques making it more + difficult for an attacker to recognize when the code has been cracked or by making the attacker do more + work to test each guess. One of the measures of the strength of an encryption system is how long it + would theoretically take an attacker to mount a successful brute-force attack against it.[3] +

+

+ [Scroll target] + When cracking passwords, this method is very fast when used to check all short passwords, but for longer + passwords other methods such as the dictionary attack are used because a brute-force search takes too + long. Longer passwords, passphrases and keys have more possible values, making them exponentially more + difficult to crack than shorter ones due to the diversity of characters.[2] +

+

+ Brute-force attacks can be made less effective by implementing key stretching techniques making it more + difficult for an attacker to recognize when the code has been cracked or by making the attacker do more + work to test each guess. One of the measures of the strength of an encryption system is how long it + would theoretically take an attacker to mount a successful brute-force attack against it.[3] +

+

+ Brute-force attacks can be made less effective by implementing key stretching techniques making it more + difficult for an attacker to recognize when the code has been cracked or by making the attacker do more + work to test each guess. One of the measures of the strength of an encryption system is how long it + would theoretically take an attacker to mount a successful brute-force attack against it.[3] +

+

+ Brute-force attacks are an application of brute-force search, the general problem-solving technique of + enumerating all candidates and checking each one. The word 'hammering' is sometimes used to describe a + brute-force attack,[4] with 'anti-hammering' for countermeasures.[5] +

+
+ `, + styles: ` + :host { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--kbq-size-l); + overflow: hidden; + padding: var(--kbq-size-l); + } + + .example-buttons { + display: flex; + flex-wrap: wrap; + gap: var(--kbq-size-s); + } + + .example-scrollbar { + height: 200px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + margin: var(--kbq-size-l); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PrivateScrollbarScrollToExample {} diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example.ts new file mode 100644 index 0000000000..d6364c450c --- /dev/null +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example.ts @@ -0,0 +1,54 @@ +import { ScrollingModule } from '@angular/cdk/scrolling'; +import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; +import { KbqButtonModule } from '@koobiq/components/button'; +import { KbqScrollbar, KbqScrollbarViewport } from '@koobiq/components/scrollbar/private'; + +/** + * @title Scrollbar with virtual scroll + */ +@Component({ + selector: 'private-scrollbar-virtual-scroll-example', + imports: [KbqScrollbar, KbqScrollbarViewport, ScrollingModule, KbqButtonModule], + template: ` + + +
+ +
{{ item }}
+
+
+ `, + styles: ` + :host { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--kbq-size-l); + padding: var(--kbq-size-l); + } + + .example-scrollbar { + height: 200px; + width: 200px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + .example-item { + padding: 0 var(--kbq-size-s); + line-height: 32px; + height: 32px; + } + `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PrivateScrollbarVirtualScrollExample { + protected readonly items = signal(Array.from({ length: 1000 }).map((_, i) => `Item #${i}`)); + + protected addItems(): void { + const nextIndex = this.items().length; + const newItems = Array.from({ length: 100 }).map((_, i) => `Item #${nextIndex + i}`); + + this.items.update((items) => [...items, ...newItems]); + } +} diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-visibility/private-scrollbar-visibility-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-visibility/private-scrollbar-visibility-example.ts new file mode 100644 index 0000000000..1327c0f3fb --- /dev/null +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-visibility/private-scrollbar-visibility-example.ts @@ -0,0 +1,78 @@ +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { KbqScrollbar, KbqScrollbarVisibility } from '@koobiq/components/scrollbar/private'; +import { KbqSelectModule } from '@koobiq/components/select'; + +/** + * @title Scrollbar visibility + */ +@Component({ + selector: 'private-scrollbar-visibility-example', + imports: [KbqScrollbar, KbqSelectModule, FormsModule], + template: ` + + + @for (visibility of visibilities; track visibility) { + {{ visibility }} + } + + + +
+

+ In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists + of an attacker submitting many possible keys or passwords with the hope of eventually guessing + correctly. This strategy can theoretically be used to break any form of encryption that is not + information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of + successfully guessing the key is negligible. +

+

+ When cracking passwords, this method is very fast when used to check all short passwords, but for longer + passwords other methods such as the dictionary attack are used because a brute-force search takes too + long. Longer passwords, passphrases and keys have more possible values, making them exponentially more + difficult to crack than shorter ones due to the diversity of characters.[2] +

+

+ Brute-force attacks can be made less effective by implementing key stretching techniques making it more + difficult for an attacker to recognize when the code has been cracked or by making the attacker do more + work to test each guess. One of the measures of the strength of an encryption system is how long it + would theoretically take an attacker to mount a successful brute-force attack against it.[3] +

+

+ Brute-force attacks are an application of brute-force search, the general problem-solving technique of + enumerating all candidates and checking each one. The word 'hammering' is sometimes used to describe a + brute-force attack,[4] with 'anti-hammering' for countermeasures.[5] +

+
+ `, + styles: ` + :host { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--kbq-size-l); + overflow: hidden; + padding: var(--kbq-size-l); + } + + .example-form-field { + width: 200px; + } + + .example-scrollbar { + height: 200px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + margin: var(--kbq-size-l); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PrivateScrollbarVisibilityExample { + protected readonly visibilities: KbqScrollbarVisibility[] = ['always', 'hover', 'scroll', 'hidden'] as const; + protected readonly visibility = model('hover'); +} diff --git a/packages/e2e/routes.ts b/packages/e2e/routes.ts index a4f71ed9d7..20d269da13 100644 --- a/packages/e2e/routes.ts +++ b/packages/e2e/routes.ts @@ -1,6 +1,7 @@ import { Routes } from '@angular/router'; import { E2eFormHorizontal } from 'packages/components/core/forms/e2e'; import { E2eTypographyStyles } from 'packages/components/core/styles/typography/e2e'; +import { E2ePrivateScrollbarStateAndStyle } from 'packages/components/scrollbar/private/e2e'; import { E2eAccordionStates } from '../components/accordion/e2e'; import { E2eActionsPanelWithOverlayContainer } from '../components/actions-panel/e2e'; import { E2eAlertStateAndStyle } from '../components/alert/e2e'; @@ -64,7 +65,6 @@ import { E2ePopoverPositioning, E2ePopoverStates } from '../components/popover/e import { E2eProgressBarStateAndStyle } from '../components/progress-bar/e2e'; import { E2eProgressSpinnerStates } from '../components/progress-spinner/e2e'; import { E2eRadioStateAndStyle } from '../components/radio/e2e'; -import { E2eScrollbarStateAndStyle } from '../components/scrollbar/e2e'; import { E2eSearchExpandableStates } from '../components/search-expandable/e2e'; import { E2eMultilineSelectStates, @@ -191,7 +191,7 @@ const components = [ E2eMarkdownStates, E2eSearchExpandableStates, E2eInputStateAndStyle, - E2eScrollbarStateAndStyle, + E2ePrivateScrollbarStateAndStyle, E2eRadioStateAndStyle, E2eProgressBarStateAndStyle, E2eProgressSpinnerStates, diff --git a/tools/api-extractor/api-extractor.ts b/tools/api-extractor/api-extractor.ts index deccafaaf5..5f063babb0 100644 --- a/tools/api-extractor/api-extractor.ts +++ b/tools/api-extractor/api-extractor.ts @@ -34,7 +34,13 @@ function runExtractor(folder: string, component: string): ExtractorResult { .replace('components', folder) .replace('button', component); const reportFolder = configObject!.apiReport!.reportFolder!.replace('components', folder); - const reportFileName = configObject!.apiReport!.reportFileName!.replace('', component); + // `reportFileName` must be a plain filename — api-extractor rejects path separators — so a + // nested entry point like "scrollbar/private" flattens to "scrollbar-private.api.md" here, + // while `mainEntryPointFilePath` above keeps the real nested dist path unchanged. + const reportFileName = configObject!.apiReport!.reportFileName!.replace( + '', + component.replace('/', '-') + ); configObject.mainEntryPointFilePath = mainEntryPointFilePath; configObject!.apiReport!.reportFolder = reportFolder; diff --git a/tools/api-extractor/config.json b/tools/api-extractor/config.json index 201c7240f0..fd9a01fd3c 100644 --- a/tools/api-extractor/config.json +++ b/tools/api-extractor/config.json @@ -41,6 +41,7 @@ "radio", "resizer", "scrollbar", + "scrollbar/private", "select", "search-expandable", "sidebar", diff --git a/tools/cspell-locales/ru.json b/tools/cspell-locales/ru.json index cec81a4a12..203860f3c9 100644 --- a/tools/cspell-locales/ru.json +++ b/tools/cspell-locales/ru.json @@ -41,6 +41,7 @@ "виджете", "виджетом", "викисклада", + "вьюпорта", "гггг", "гига", "грид", @@ -171,7 +172,9 @@ "скроллбар", "скроллбара", "скроллбаров", + "скроллбары", "скролле", + "скроллить", "скроллом", "спиcка", "спиннера", diff --git a/tools/public_api_guard/components/scrollbar-private.api.md b/tools/public_api_guard/components/scrollbar-private.api.md new file mode 100644 index 0000000000..7f49c92cc5 --- /dev/null +++ b/tools/public_api_guard/components/scrollbar-private.api.md @@ -0,0 +1,125 @@ +## API Report File for "koobiq" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import * as _angular_core from '@angular/core'; +import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { ElementRef } from '@angular/core'; +import * as i1 from '@angular/cdk/scrolling'; +import { InjectionToken } from '@angular/core'; +import { KbqOverflowShadowSource } from '@koobiq/components/core'; +import { Observable } from 'rxjs'; +import { OnDestroy } from '@angular/core'; +import { OutputEmitterRef } from '@angular/core'; +import { Provider } from '@angular/core'; +import { WritableSignal } from '@angular/core'; + +// @public +export const KBQ_SCROLLBAR_CONFIG: InjectionToken>; + +// @public +export const KBQ_SCROLLBAR_DEFAULT_CONFIG: Required; + +// @public +export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { + constructor(); + readonly autoHideDelay: _angular_core.InputSignalWithTransform; + readonly disableInteraction: _angular_core.InputSignalWithTransform; + readonly floating: _angular_core.InputSignalWithTransform; + getScrollElement(): HTMLElement | null; + readonly initialized: OutputEmitterRef; + readonly isAtBottom: _angular_core.Signal; + readonly isAtEnd: _angular_core.Signal; + readonly isAtStart: _angular_core.Signal; + readonly isAtTop: _angular_core.Signal; + // (undocumented) + ngOnDestroy(): void; + readonly onScroll: Observable; + readonly reachBottom: OutputEmitterRef; + readonly reachEnd: OutputEmitterRef; + readonly reachStart: OutputEmitterRef; + readonly reachTop: OutputEmitterRef; + protected readonly rtl: WritableSignal; + readonly scrollChange: OutputEmitterRef; + scrollEnd(behavior?: ScrollBehavior): void; + scrollStart(behavior?: ScrollBehavior): void; + scrollTo(options: KbqScrollbarScrollToOptions): void; + scrollToBottom(behavior?: ScrollBehavior): void; + scrollToElement(target: HTMLElement | string, options?: KbqScrollbarScrollToElementOptions): void; + scrollToTop(behavior?: ScrollBehavior): void; + update(): void; + readonly updated: OutputEmitterRef; + readonly visibility: _angular_core.InputSignal; + readonly visibilityChange: OutputEmitterRef; + // (undocumented) + static ɵdir: _angular_core.ɵɵDirectiveDeclaration; + // (undocumented) + static ɵfac: _angular_core.ɵɵFactoryDeclaration; +} + +// @public +export type KbqScrollbarConfig = Partial<{ + visibility: KbqScrollbarVisibility; + autoHideDelay: number; + floating: boolean; + native: boolean; + disableInteraction: boolean; +}>; + +// @public +export const kbqScrollbarConfigProvider: (config: KbqScrollbarConfig) => Provider; + +// @public (undocumented) +export class KbqScrollbarModule { + // (undocumented) + static ɵfac: _angular_core.ɵɵFactoryDeclaration; + // (undocumented) + static ɵinj: _angular_core.ɵɵInjectorDeclaration; + // (undocumented) + static ɵmod: _angular_core.ɵɵNgModuleDeclaration; +} + +// @public +export type KbqScrollbarScrollChangeEvent = { + top: number; + left: number; +}; + +// @public +export type KbqScrollbarScrollToElementOptions = Partial<{ + top: number; + left: number; + behavior: ScrollBehavior; +}>; + +// @public +export type KbqScrollbarScrollToOptions = Partial<{ + top: number; + left: number; + behavior: ScrollBehavior; +}>; + +// @public +export class KbqScrollbarViewport { + readonly elementRef: ElementRef; + readonly virtualScrollViewport: CdkVirtualScrollViewport | null; + // (undocumented) + static ɵdir: _angular_core.ɵɵDirectiveDeclaration; + // (undocumented) + static ɵfac: _angular_core.ɵɵFactoryDeclaration; +} + +// @public +export type KbqScrollbarVisibility = 'hover' | 'always' | 'scroll' | 'hidden'; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/tsconfig.json b/tsconfig.json index 7c213e4df4..ce7f010556 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -71,6 +71,7 @@ "@koobiq/components/sidepanel": ["packages/components/sidepanel/index.ts"], "@koobiq/components/skeleton": ["packages/components/skeleton/index.ts"], "@koobiq/components/scrollbar": ["packages/components/scrollbar/index.ts"], + "@koobiq/components/scrollbar/private": ["packages/components/scrollbar/private/index.ts"], "@koobiq/components/splitter": ["packages/components/splitter/index.ts"], "@koobiq/components/split-button": ["packages/components/split-button/index.ts"], "@koobiq/components/table": ["packages/components/table/index.ts"], From d176d8cc96474c58b0e6b03981a225c1e62b0504 Mon Sep 17 00:00:00 2001 From: Artem Belik Date: Thu, 30 Jul 2026 13:01:12 +0300 Subject: [PATCH 02/13] refactor: code review --- .../scrollbar/private/e2e.playwright-spec.ts | 107 ++- packages/components/scrollbar/private/e2e.ts | 154 +++- .../scrollbar/private/scrollbar.module.ts | 4 +- .../scrollbar/private/scrollbar.scss | 5 + .../scrollbar/private/scrollbar.spec.ts | 824 ++++++++++++++++-- .../components/scrollbar/private/scrollbar.ts | 244 ++++-- .../private-scrollbar-scroll-to-example.ts | 26 +- ...rivate-scrollbar-virtual-scroll-example.ts | 6 +- .../private-scrollbar-visibility-example.ts | 7 + packages/e2e/routes.ts | 10 +- .../components/scrollbar-private.api.md | 18 +- 11 files changed, 1246 insertions(+), 159 deletions(-) diff --git a/packages/components/scrollbar/private/e2e.playwright-spec.ts b/packages/components/scrollbar/private/e2e.playwright-spec.ts index 668aa14518..25f383a0e0 100644 --- a/packages/components/scrollbar/private/e2e.playwright-spec.ts +++ b/packages/components/scrollbar/private/e2e.playwright-spec.ts @@ -1,4 +1,4 @@ -import { expect, Page, test } from '@playwright/test'; +import { expect, Locator, Page, test } from '@playwright/test'; import { e2eEnableDarkTheme } from 'packages/e2e/utils'; test.describe('KbqScrollbar (private)', () => { @@ -14,4 +14,109 @@ test.describe('KbqScrollbar (private)', () => { await expect(component).toHaveScreenshot('01-dark.png'); }); }); + + test.describe('E2ePrivateScrollbarDrag', () => { + // `KbqScrollbar` moves the host's content into an auto-created + // `.kbq-private-scrollbar-viewport` wrapper so the track/thumb (siblings on the host) stay + // a fixed overlay — real scroll metrics belong on that wrapper, not the host itself. + const getViewport = (container: Locator) => container.locator('.kbq-private-scrollbar-viewport'); + + test('dragging the vertical thumb scrolls the content down', async ({ page }) => { + await page.goto('/E2ePrivateScrollbarDrag'); + const container = page.getByTestId('drag'); + const viewport = getViewport(container); + const thumb = container.locator('.kbq-private-scrollbar-track_vertical .kbq-private-scrollbar-thumb'); + + await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBe(0); + + const box = await thumb.boundingBox(); + + if (!box) throw new Error('bounding box is null'); + + await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await page.mouse.down(); + await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2 + 40, { steps: 5 }); + await page.mouse.up(); + + await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBeGreaterThan(0); + }); + + test('clicking the track away from the thumb jumps the scroll position toward the click', async ({ page }) => { + await page.goto('/E2ePrivateScrollbarDrag'); + const container = page.getByTestId('drag'); + const viewport = getViewport(container); + const track = container.locator('.kbq-private-scrollbar-track_vertical'); + + await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBe(0); + + const box = await track.boundingBox(); + + if (!box) throw new Error('bounding box is null'); + + // The thumb starts at the track's top — clicking near the bottom lands on the track + // itself, not the thumb, triggering "jump to click" rather than a drag. + await page.mouse.click(box.x + box.width / 2, box.y + box.height - 5); + + await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBeGreaterThan(0); + }); + + test('kbqScrollbarDisableInteraction blocks a real thumb drag', async ({ page }) => { + await page.goto('/E2ePrivateScrollbarDrag'); + const container = page.getByTestId('drag-disabled'); + const viewport = getViewport(container); + const thumb = container.locator('.kbq-private-scrollbar-track_vertical .kbq-private-scrollbar-thumb'); + + const box = await thumb.boundingBox(); + + if (!box) throw new Error('bounding box is null'); + + await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await page.mouse.down(); + await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2 + 40, { steps: 5 }); + await page.mouse.up(); + + expect(await viewport.evaluate((el) => el.scrollTop)).toBe(0); + }); + }); + + test.describe('E2ePrivateScrollbarScrollTo', () => { + test('scrollToBottom/scrollToTop buttons scroll the content and update their own disabled state via isBottomReached/isTopReached', async ({ + page + }) => { + await page.goto('/E2ePrivateScrollbarScrollTo'); + const scrollTopButton = page.getByTestId('scroll-top'); + const scrollBottomButton = page.getByTestId('scroll-bottom'); + + await expect(scrollTopButton).toBeDisabled(); + await expect(scrollBottomButton).toBeEnabled(); + + await scrollBottomButton.click(); + + await expect(scrollTopButton).toBeEnabled(); + await expect(scrollBottomButton).toBeDisabled(); + + await scrollTopButton.click(); + + await expect(scrollTopButton).toBeDisabled(); + await expect(scrollBottomButton).toBeEnabled(); + }); + }); + + test.describe('E2ePrivateScrollbarHoverVisibility', () => { + test("'hover' mode (the default) reveals the custom track on real mouse hover and hides it again on mouse leave", async ({ + page + }) => { + await page.goto('/E2ePrivateScrollbarHoverVisibility'); + const container = page.getByTestId('hover-visibility'); + const visibleClass = /(?:^|\s)kbq-private-scrollbar_visible(?:\s|$)/; + + await expect(container).not.toHaveClass(visibleClass); + + await container.hover(); + await expect(container).toHaveClass(visibleClass); + + await page.mouse.move(0, 0); + await expect(container).not.toHaveClass(visibleClass); + }); + }); }); diff --git a/packages/components/scrollbar/private/e2e.ts b/packages/components/scrollbar/private/e2e.ts index 6ca7997464..86a7cca95c 100644 --- a/packages/components/scrollbar/private/e2e.ts +++ b/packages/components/scrollbar/private/e2e.ts @@ -7,44 +7,37 @@ import { KbqScrollbar, kbqScrollbarConfigProvider } from './scrollbar'; template: `
-

- [basic] - {{ content }} -

+

basic

-

- [hover] - {{ content }} -

+

hover

-

- [active] - {{ content }} -

+

active

`, styles: ` :host { display: inline-grid; - grid-template-columns: repeat(2, 250px); + grid-template-columns: repeat(3, 100px); gap: var(--kbq-size-s); padding: var(--kbq-size-xs); } .e2e-scrollbar { + width: 100%; height: 100px; border-radius: var(--kbq-size-border-radius); background-color: var(--kbq-background-bg-secondary); } p { - width: 150%; + width: 200%; + height: 200%; margin: var(--kbq-size-l); } `, @@ -54,6 +47,137 @@ import { KbqScrollbar, kbqScrollbarConfigProvider } from './scrollbar'; 'data-testid': 'e2ePrivateScrollbarStateAndStyle' } }) -export class E2ePrivateScrollbarStateAndStyle { +export class E2ePrivateScrollbarStateAndStyle {} + +@Component({ + selector: 'e2e-private-scrollbar-drag', + imports: [KbqScrollbar], + template: ` +
+

{{ content }}

+
+ +
+

{{ content }}

+
+ `, + styles: ` + :host { + display: inline-flex; + gap: var(--kbq-size-s); + padding: var(--kbq-size-xs); + } + + .e2e-scrollbar { + width: 200px; + height: 100px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + margin: var(--kbq-size-l); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + 'data-testid': 'e2ePrivateScrollbarDrag' + } +}) +export class E2ePrivateScrollbarDrag { + readonly content = `In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists of an attacker submitting many possible keys or passwords with the hope of eventually guessing correctly. This strategy can theoretically be used to break any form of encryption that is not information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of successfully guessing the key is negligible.`; +} + +@Component({ + selector: 'e2e-private-scrollbar-scroll-to', + imports: [KbqScrollbar], + template: ` + + + +
+

{{ content }}

+
+ `, + styles: ` + :host { + display: block; + padding: var(--kbq-size-xs); + } + + .e2e-scrollbar { + width: 200px; + height: 100px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + margin: var(--kbq-size-l); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + 'data-testid': 'e2ePrivateScrollbarScrollTo' + } +}) +export class E2ePrivateScrollbarScrollTo { + readonly content = `In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists of an attacker submitting many possible keys or passwords with the hope of eventually guessing correctly. This strategy can theoretically be used to break any form of encryption that is not information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of successfully guessing the key is negligible.`; +} + +@Component({ + selector: 'e2e-private-scrollbar-hover-visibility', + imports: [KbqScrollbar], + template: ` +
+

{{ content }}

+
+ `, + styles: ` + :host { + display: block; + padding: var(--kbq-size-xs); + } + + .e2e-scrollbar { + width: 200px; + height: 100px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + margin: var(--kbq-size-l); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + 'data-testid': 'e2ePrivateScrollbarHoverVisibility' + } +}) +export class E2ePrivateScrollbarHoverVisibility { readonly content = `In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists of an attacker submitting many possible keys or passwords with the hope of eventually guessing correctly. This strategy can theoretically be used to break any form of encryption that is not information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of successfully guessing the key is negligible.`; } diff --git a/packages/components/scrollbar/private/scrollbar.module.ts b/packages/components/scrollbar/private/scrollbar.module.ts index 534063504e..dff0156223 100644 --- a/packages/components/scrollbar/private/scrollbar.module.ts +++ b/packages/components/scrollbar/private/scrollbar.module.ts @@ -1,9 +1,9 @@ import { NgModule } from '@angular/core'; -import { KbqScrollbar, KbqScrollbarViewport } from './scrollbar'; +import { KbqScrollbar, KbqScrollbarVirtualViewport } from './scrollbar'; const COMPONENTS = [ KbqScrollbar, - KbqScrollbarViewport + KbqScrollbarVirtualViewport ]; @NgModule({ diff --git a/packages/components/scrollbar/private/scrollbar.scss b/packages/components/scrollbar/private/scrollbar.scss index fae86f328f..11c6f6ddfb 100644 --- a/packages/components/scrollbar/private/scrollbar.scss +++ b/packages/components/scrollbar/private/scrollbar.scss @@ -54,6 +54,11 @@ // kbqScrollbarViewport — holds `overflow`, so the track/thumb (siblings on the host) stay a // fixed overlay instead of scrolling away with the content. .kbq-private-scrollbar-viewport { + // The auto-created wrapper is a real component (`kbq-scrollbar-viewport`, an + // unknown custom element to the browser) so it can carry `hostDirectives: [CdkScrollable]` — + // unlike a plain `div`, it doesn't default to `display: block`, so `width`/`height: 100%` + // wouldn't apply without this. + display: block; box-sizing: border-box; width: 100%; height: 100%; diff --git a/packages/components/scrollbar/private/scrollbar.spec.ts b/packages/components/scrollbar/private/scrollbar.spec.ts index 1241b50c74..041f788041 100644 --- a/packages/components/scrollbar/private/scrollbar.spec.ts +++ b/packages/components/scrollbar/private/scrollbar.spec.ts @@ -1,5 +1,5 @@ import { Directionality } from '@angular/cdk/bidi'; -import { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling'; +import { CdkVirtualScrollViewport, ScrollDispatcher, ScrollingModule } from '@angular/cdk/scrolling'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -12,7 +12,7 @@ import { import { ComponentFixture, TestBed } from '@angular/core/testing'; import { KBQ_WINDOW } from '@koobiq/components/core'; import { Subject } from 'rxjs'; -import { KBQ_SCROLLBAR_CONFIG, KbqScrollbar, KbqScrollbarViewport, KbqScrollbarVisibility } from './scrollbar'; +import { KBQ_SCROLLBAR_CONFIG, KbqScrollbar, KbqScrollbarVirtualViewport, KbqScrollbarVisibility } from './scrollbar'; const createComponent = (component: Type, providers: Provider[] = []): ComponentFixture => { TestBed.configureTestingModule({ imports: [component], providers }); @@ -92,6 +92,35 @@ class TestScrollbarNonFloatingHost { readonly scrollbar = viewChild.required(KbqScrollbar); } +@Component({ + selector: 'test-scrollbar-dynamic-floating-host', + imports: [KbqScrollbar], + template: ` +
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarDynamicFloatingHost { + // Same `markForCheck()` requirement as `TestScrollbarVisibilityHost` — a plain (non-signal) + // field read through an OnPush host needs it, and `fixture.changeDetectorRef.markForCheck()` + // from outside doesn't mark this view dirty. + private readonly cdr = inject(ChangeDetectorRef); + private _floating = true; + + get floating(): boolean { + return this._floating; + } + + set floating(value: boolean) { + this._floating = value; + this.cdr.markForCheck(); + } + + readonly scrollbar = viewChild.required(KbqScrollbar); +} + @Component({ selector: 'test-scrollbar-visibility-host', imports: [KbqScrollbar], @@ -140,29 +169,13 @@ class TestScrollbarDualOverflowHost { readonly scrollbar = viewChild.required(KbqScrollbar); } -@Component({ - selector: 'test-scrollbar-viewport-host', - imports: [KbqScrollbar, KbqScrollbarViewport], - template: ` -
-
-
content
-
-
- `, - changeDetection: ChangeDetectionStrategy.OnPush -}) -class TestScrollbarViewportHost { - readonly scrollbar = viewChild.required(KbqScrollbar); -} - @Component({ selector: 'test-scrollbar-virtual-host', - imports: [KbqScrollbar, KbqScrollbarViewport, ScrollingModule], + imports: [KbqScrollbar, KbqScrollbarVirtualViewport, ScrollingModule], template: `
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarInitializedHost { + initializedCount = 0; +} + +@Component({ + selector: 'test-scrollbar-scroll-visibility-host', + imports: [KbqScrollbar], + template: ` +
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarScrollVisibilityHost { + readonly scrollbar = viewChild.required(KbqScrollbar); +} + describe(KbqScrollbar.name, () => { describe('basic rendering', () => { it('creates a vertical track and thumb by default; builds but hides the horizontal track since there is no horizontal overflow', () => { @@ -252,6 +304,35 @@ describe(KbqScrollbar.name, () => { expect(verticalTrack.classList.contains('kbq-private-scrollbar-track_has-horizontal')).toBe(false); }); + it('sizes the thumb against the track size AFTER corner-avoidance shrinks it, not a stale pre-shrink size', () => { + const fixture = createComponent(TestScrollbarDualOverflowHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const verticalTrack = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const horizontalTrack = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + const thumb = verticalTrack.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + // jsdom doesn't run layout, so this getter stands in for what a real browser would do: + // once corner-avoidance's `_has-horizontal` modifier lands, `scrollbar.scss`'s + // `bottom: var(--...)` rule shrinks the track's real clientHeight. + Object.defineProperty(verticalTrack, 'clientHeight', { + configurable: true, + get(this: HTMLElement) { + return this.classList.contains('kbq-private-scrollbar-track_has-horizontal') ? 92 : 106; + } + }); + + setMetrics(scrollEl, { clientHeight: 250, scrollHeight: 500, clientWidth: 100, scrollWidth: 500 }); + setMetrics(horizontalTrack, { clientWidth: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(verticalTrack.classList.contains('kbq-private-scrollbar-track_has-horizontal')).toBe(true); + // Correct (post-shrink 92px track): travelLength 92-2*3=86, ratio ceil(250/500*100)/100 + // = 0.5, thumbSize max(0.5*86, 32) = 43. A stale pre-shrink 106px track would instead + // give travelLength 100 and thumbSize 50. + expect(thumb.style.height).toBe('43px'); + }); + it('does not create track/thumb when KBQ_SCROLLBAR_CONFIG sets native: true', () => { const fixture = createComponent(TestScrollbarHost, [ { provide: KBQ_SCROLLBAR_CONFIG, useValue: { native: true } } @@ -261,7 +342,7 @@ describe(KbqScrollbar.name, () => { expect(host.querySelector('.kbq-private-scrollbar-track')).toBeNull(); }); - it('still tracks isAtTop/isAtBottom and fires reachTop/reachBottom when native: true, despite having no track/thumb of its own', () => { + it('still tracks isTopReached/isBottomReached and fires reachTop/reachBottom when native: true, despite having no track/thumb of its own', () => { const fixture = createComponent(TestScrollbarHost, [ { provide: KBQ_SCROLLBAR_CONFIG, useValue: { native: true } } ]); @@ -278,16 +359,16 @@ describe(KbqScrollbar.name, () => { setMetrics(host, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); scrollbar.update(); - expect(scrollbar.isAtTop()).toBe(true); - expect(scrollbar.isAtBottom()).toBe(false); + expect(scrollbar.isTopReached()).toBe(true); + expect(scrollbar.isBottomReached()).toBe(false); expect(topSpy).toHaveBeenCalled(); expect(bottomSpy).not.toHaveBeenCalled(); setMetrics(host, { scrollTop: 400 }); scrollbar.update(); - expect(scrollbar.isAtTop()).toBe(false); - expect(scrollbar.isAtBottom()).toBe(true); + expect(scrollbar.isTopReached()).toBe(false); + expect(scrollbar.isBottomReached()).toBe(true); expect(bottomSpy).toHaveBeenCalled(); }); @@ -384,6 +465,331 @@ describe(KbqScrollbar.name, () => { expect(thumbBottom).toBe(103); }); + + it('establishes native scroll on both axes, not just vertical', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + expect(scrollEl.style.overflowY).toBe('auto'); + expect(scrollEl.style.overflowX).toBe('auto'); + }); + + it('hides the native scrollbar on the effective scroll element when building the custom UI', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + expect(scrollEl.classList.contains('kbq-private-scrollbar_hide-native')).toBe(true); + }); + + it('does not hide the native scrollbar when KBQ_SCROLLBAR_CONFIG sets native: true', () => { + const fixture = createComponent(TestScrollbarHost, [ + { provide: KBQ_SCROLLBAR_CONFIG, useValue: { native: true } } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + // `native: true` never creates an auto-viewport wrapper — the host itself is the + // effective scroll element. + expect(host.classList.contains('kbq-private-scrollbar_hide-native')).toBe(false); + }); + + it('reads a real, resolvable --kbq-private-scrollbar-size-thumb-min-size CSS token over the hardcoded fallback', () => { + const customTokenProvider: Provider = { + provide: KBQ_WINDOW, + useValue: { + getComputedStyle: () => ({ + getPropertyValue: (property: string) => + property === '--kbq-private-scrollbar-size-thumb-min-size' ? '60px' : '' + }) + } + }; + + const fixture = createComponent(TestScrollbarHost, [customTokenProvider]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + // A very small viewport ratio so the min-size clamp is what actually determines thumb + // size, not the ratio itself. + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 10, scrollHeight: 1000 }); + setMetrics(track, { clientHeight: 106 }); + fixture.componentInstance.scrollbar().update(); + + expect(thumb.style.height).toBe('60px'); + }); + }); + + describe('drag interaction', () => { + const mockRects = (track: HTMLElement, thumb: HTMLElement, axis: 'vertical' | 'horizontal') => { + if (axis === 'vertical') { + jest.spyOn(track, 'getBoundingClientRect').mockReturnValue({ top: 0, height: 106 } as DOMRect); + jest.spyOn(thumb, 'getBoundingClientRect').mockReturnValue({ top: 3, height: 32 } as DOMRect); + } else { + jest.spyOn(track, 'getBoundingClientRect').mockReturnValue({ left: 0, width: 106 } as DOMRect); + jest.spyOn(thumb, 'getBoundingClientRect').mockReturnValue({ left: 3, width: 32 } as DOMRect); + } + }; + + it('dragging the thumb scrolls proportionally and toggles the dragging class', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + // trackStart = 0 + padding(3) = 3; trackLength = 106 - 2*3 = 100; thumbSize = 32 (mocked + // rect); trackTravel = 100 - 32 = 68. Grabbing exactly the thumb's own top edge (clientY: + // 3) makes grabOffset 0, so pointer position maps 1:1 onto the [trackStart, trackStart + + // trackTravel] range. + thumb.dispatchEvent(new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true })); + + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(true); + + document.dispatchEvent(new MouseEvent('pointermove', { clientY: 37, buttons: 1, cancelable: true })); + // ratio = (37 - 3 - 0) / 68 = 0.5; scrollRange = 500 - 100 = 400. + expect(scrollEl.scrollTop).toBe(200); + + document.dispatchEvent(new MouseEvent('pointermove', { clientY: 71, buttons: 1, cancelable: true })); + expect(scrollEl.scrollTop).toBe(400); + + document.dispatchEvent(new MouseEvent('pointerup', { cancelable: true })); + + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); + }); + + it('clicking the track (not the thumb) jumps the thumb to the click point and continues as a drag', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + // grabOffset is hardcoded to thumbSize/2 (16) for a track click. clientY: 53 -> + // pointerRelative = 53 - trackStart(3) = 50; ratio = (50 - 16) / trackTravel(68) = 0.5. + track.dispatchEvent(new MouseEvent('pointerdown', { clientY: 53, bubbles: true, cancelable: true })); + + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(true); + expect(scrollEl.scrollTop).toBe(200); + }); + + it('disableInteraction blocks both thumb drag and track jump-to-click', () => { + const fixture = createComponent(TestScrollbarDisableInteractionHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + thumb.dispatchEvent(new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true })); + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); + + track.dispatchEvent(new MouseEvent('pointerdown', { clientY: 53, bubbles: true, cancelable: true })); + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); + expect(scrollEl.scrollTop).toBe(0); + }); + + it('ignores pointermove events from a different pointerId than the one that started the drag', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + const downEvent = new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true }); + + Object.defineProperty(downEvent, 'pointerId', { value: 1, configurable: true }); + thumb.dispatchEvent(downEvent); + + const moveEvent = new MouseEvent('pointermove', { clientY: 71, buttons: 1, cancelable: true }); + + Object.defineProperty(moveEvent, 'pointerId', { value: 2, configurable: true }); + document.dispatchEvent(moveEvent); + + expect(scrollEl.scrollTop).toBe(0); + }); + + it('ends the drag if pointermove reports no buttons pressed, instead of continuing to scroll', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + thumb.dispatchEvent(new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true })); + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(true); + + document.dispatchEvent(new MouseEvent('pointermove', { clientY: 71, buttons: 0, cancelable: true })); + + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); + expect(scrollEl.scrollTop).toBe(0); + }); + + it('does not attempt to scroll when the thumb fills the entire track travel range', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + jest.spyOn(track, 'getBoundingClientRect').mockReturnValue({ top: 0, height: 106 } as DOMRect); + // thumbSize (100) equals trackLength (106 - 2*3), so trackTravel is 0. + jest.spyOn(thumb, 'getBoundingClientRect').mockReturnValue({ top: 3, height: 100 } as DOMRect); + + thumb.dispatchEvent(new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true })); + document.dispatchEvent(new MouseEvent('pointermove', { clientY: 71, buttons: 1, cancelable: true })); + + expect(scrollEl.scrollTop).toBe(0); + }); + + it('drags correctly on the horizontal axis in RTL, inverting the physical ratio onto the negative scrollLeft range', () => { + const mockDir = new MockDirectionality(); + + mockDir.value = 'rtl'; + + const fixture = createComponent(TestScrollbarDualOverflowHost, [ + { provide: Directionality, useValue: mockDir } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_horizontal') as HTMLElement; + const thumb = track.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollLeft: 0, clientWidth: 100, scrollWidth: 500, clientHeight: 500 }); + mockRects(track, thumb, 'horizontal'); + + // Dragging all the way to the track's physical left (ratio 0) must land on the most + // negative scrollLeft (RTL's logical end) — not 0, which is what the LTR branch would + // give at the same ratio. + thumb.dispatchEvent(new MouseEvent('pointerdown', { clientX: 3, bubbles: true, cancelable: true })); + document.dispatchEvent(new MouseEvent('pointermove', { clientX: 3, buttons: 1, cancelable: true })); + + expect(scrollEl.scrollLeft).toBe(-400); + }); + + it('ignores pointerdown from a non-primary mouse button (e.g. right-click)', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + thumb.dispatchEvent( + new MouseEvent('pointerdown', { clientY: 3, button: 2, bubbles: true, cancelable: true }) + ); + + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); + }); + + it('ignores a pointerdown that is explicitly not the primary pointer', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + const event = new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true }); + + Object.defineProperty(event, 'isPrimary', { value: false, configurable: true }); + thumb.dispatchEvent(event); + + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); + }); + + it('ends the drag on pointercancel, the same as pointerup, instead of leaving it stuck', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + thumb.dispatchEvent(new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true })); + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(true); + + document.dispatchEvent(new MouseEvent('pointercancel', { cancelable: true })); + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); + + // A pointermove after the cancel must be a no-op — the drag already ended. + document.dispatchEvent(new MouseEvent('pointermove', { clientY: 71, buttons: 1, cancelable: true })); + expect(scrollEl.scrollTop).toBe(0); + }); + + it("hides the scrollbar once a drag ends in 'hover' mode if the pointer already left the host mid-drag", () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + host.dispatchEvent(new Event('pointerenter')); + thumb.dispatchEvent(new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true })); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + + // Pointer leaves the host while still dragging — must not hide yet, a drag is active. + host.dispatchEvent(new Event('pointerleave')); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + + document.dispatchEvent(new MouseEvent('pointerup', { cancelable: true })); + + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(false); + }); + + it("schedules the auto-hide once a drag ends in 'scroll' mode, instead of leaving the scrollbar visible forever", () => { + const fixture = createComponent(TestScrollbarScrollVisibilityHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); + + jest.useFakeTimers(); + + try { + // A click-without-movement: `beginInteraction()` shows the scrollbar directly + // (not via `showTemporarily()`), so without the fix nothing would ever schedule + // hiding it. + thumb.dispatchEvent(new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true })); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + + document.dispatchEvent(new MouseEvent('pointerup', { cancelable: true })); + + jest.advanceTimersByTime(50); + + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(false); + } finally { + jest.useRealTimers(); + } + }); }); describe('floating', () => { @@ -404,6 +810,61 @@ describe(KbqScrollbar.name, () => { expect(scrollEl.style.paddingRight).toBe('var(--kbq-private-scrollbar-size-track-dimension)'); expect(scrollEl.style.paddingBottom).toBe('var(--kbq-private-scrollbar-size-track-dimension)'); }); + + it('reserves the gutter on the left in RTL instead of the right', () => { + const mockDir = new MockDirectionality(); + + mockDir.value = 'rtl'; + + const fixture = createComponent(TestScrollbarNonFloatingHost, [ + { provide: Directionality, useValue: mockDir } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + expect(scrollEl.style.paddingLeft).toBe('var(--kbq-private-scrollbar-size-track-dimension)'); + expect(scrollEl.style.paddingRight).toBe(''); + }); + + it('reacts to kbqScrollbarFloating changing at runtime, not just its value at first render', () => { + const fixture = createComponent(TestScrollbarDynamicFloatingHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + expect(scrollEl.style.paddingRight).toBe(''); + + fixture.componentInstance.floating = false; + fixture.detectChanges(); + TestBed.tick(); + + expect(scrollEl.style.paddingRight).toBe('var(--kbq-private-scrollbar-size-track-dimension)'); + + fixture.componentInstance.floating = true; + fixture.detectChanges(); + TestBed.tick(); + + expect(scrollEl.style.paddingRight).toBe(''); + }); + + it('moves the gutter to the other side when direction flips at runtime, instead of leaving both', () => { + const mockDir = new MockDirectionality(); + const fixture = createComponent(TestScrollbarNonFloatingHost, [ + { provide: Directionality, useValue: mockDir } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + expect(scrollEl.style.paddingRight).toBe('var(--kbq-private-scrollbar-size-track-dimension)'); + expect(scrollEl.style.paddingLeft).toBe(''); + + mockDir.value = 'rtl'; + mockDir.change.next('rtl'); + fixture.detectChanges(); + TestBed.tick(); + + expect(scrollEl.style.paddingLeft).toBe('var(--kbq-private-scrollbar-size-track-dimension)'); + expect(scrollEl.style.paddingRight).toBe(''); + }); }); describe('scrollTo / scrollToElement', () => { @@ -450,6 +911,52 @@ describe(KbqScrollbar.name, () => { expect(scrollEl.scrollLeft).toBe(200); }); + it('scrollToElement applies an independent left offset', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const target = scrollEl.querySelector('div') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, scrollLeft: 0 }); + jest.spyOn(scrollEl, 'getBoundingClientRect').mockReturnValue({ top: 0, left: 0 } as DOMRect); + jest.spyOn(target, 'getBoundingClientRect').mockReturnValue({ top: 150, left: 200 } as DOMRect); + + fixture.componentInstance.scrollbar().scrollToElement(target, { left: 20 }); + + expect(scrollEl.scrollTop).toBe(150); + expect(scrollEl.scrollLeft).toBe(180); + }); + + it('scrollToElement resolves a string target as a selector against the scroll element', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const target = scrollEl.querySelector('div') as HTMLElement; + + target.setAttribute('data-target', ''); + setMetrics(scrollEl, { scrollTop: 0, scrollLeft: 0 }); + jest.spyOn(scrollEl, 'getBoundingClientRect').mockReturnValue({ top: 0, left: 0 } as DOMRect); + jest.spyOn(target, 'getBoundingClientRect').mockReturnValue({ top: 150, left: 200 } as DOMRect); + + fixture.componentInstance.scrollbar().scrollToElement('[data-target]'); + + expect(scrollEl.scrollTop).toBe(150); + expect(scrollEl.scrollLeft).toBe(200); + }); + + it('scrollToElement does nothing when the string selector matches no element', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + setMetrics(scrollEl, { scrollTop: 55, scrollLeft: 33 }); + + fixture.componentInstance.scrollbar().scrollToElement('.does-not-exist'); + + expect(scrollEl.scrollTop).toBe(55); + expect(scrollEl.scrollLeft).toBe(33); + }); + it('scrollStart / scrollEnd scroll the horizontal axis', () => { const fixture = createComponent(TestScrollbarHost); const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; @@ -602,22 +1109,150 @@ describe(KbqScrollbar.name, () => { expect(visibleSpy).not.toHaveBeenCalledWith(true); expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(false); }); + + it("'hover' mode (the default) shows on a real pointerenter and hides again on pointerleave", () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(false); + + host.dispatchEvent(new Event('pointerenter')); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + + host.dispatchEvent(new Event('pointerleave')); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(false); + }); + + it("'scroll' mode temporarily reveals on a real scroll event and auto-hides after kbqScrollbarAutoHideDelay", () => { + const fixture = createComponent(TestScrollbarScrollVisibilityHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + jest.useFakeTimers(); + + try { + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(false); + + scrollEl.dispatchEvent(new Event('scroll')); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + + jest.advanceTimersByTime(49); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + + jest.advanceTimersByTime(1); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(false); + } finally { + jest.useRealTimers(); + } + }); + + it("'scroll' mode restarts the auto-hide timer on every subsequent scroll instead of hiding on the first timer", () => { + const fixture = createComponent(TestScrollbarScrollVisibilityHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + jest.useFakeTimers(); + + try { + scrollEl.dispatchEvent(new Event('scroll')); + jest.advanceTimersByTime(30); + + scrollEl.dispatchEvent(new Event('scroll')); + jest.advanceTimersByTime(30); + + // Only 30ms since the second scroll, not yet the full 50ms delay. + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + + jest.advanceTimersByTime(20); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(false); + } finally { + jest.useRealTimers(); + } + }); + + it("switching away from 'scroll' clears its pending auto-hide timer, instead of that timer hiding the scrollbar later under the new mode", () => { + const fixture = createComponent(TestScrollbarVisibilityHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + jest.useFakeTimers(); + + try { + setVisibility(fixture, 'scroll'); + scrollEl.dispatchEvent(new Event('scroll')); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + + setVisibility(fixture, 'always'); + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + + // Well past the default autoHideDelay (100ms) — the stale 'scroll'-mode timer must + // not fire and hide a scrollbar that's supposed to stay permanently visible now. + jest.advanceTimersByTime(200); + + expect(host.classList.contains('kbq-private-scrollbar_visible')).toBe(true); + } finally { + jest.useRealTimers(); + } + }); }); - describe('kbqScrollbarViewport delegation', () => { - it('measures and scrolls the delegated viewport instead of the host', () => { - const fixture = createComponent(TestScrollbarViewportHost); + describe('kbqScrollbarVirtualViewport delegation', () => { + it('measures the delegated virtual viewport instead of the host', () => { + const fixture = createComponent(TestScrollbarVirtualHost); const viewport = fixture.debugElement.nativeElement.querySelector( '[data-testid="viewport"]' ) as HTMLElement; - setMetrics(viewport, { scrollTop: 0 }); - expect(fixture.componentInstance.scrollbar().getScrollElement()).toBe(viewport); + }); + }); - fixture.componentInstance.scrollbar().scrollTo({ top: 42 }); + describe('CdkScrollable / ScrollDispatcher integration', () => { + const registeredElements = (): HTMLElement[] => + Array.from(TestBed.inject(ScrollDispatcher).scrollContainers.keys()).map( + (scrollable) => scrollable.getElementRef().nativeElement + ); + + it('registers the auto-viewport (the real scroll element) with ScrollDispatcher', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + // `KbqScrollbar`'s own `hostDirectives: [CdkScrollable]` also still registers the host + // itself — harmlessly: the host has no `overflow` of its own in this case (the + // auto-viewport does), so its `CdkScrollable` never actually fires, it just stays + // registered alongside the one that matters. + expect(registeredElements()).toContain(scrollEl); + }); + + it('sees a delegated virtual viewport via its own built-in CdkScrollable', () => { + const fixture = createComponent(TestScrollbarVirtualHost); + const viewport = fixture.debugElement.nativeElement.querySelector( + '[data-testid="viewport"]' + ) as HTMLElement; + + expect(registeredElements()).toContain(viewport); + }); + + it('registers the host itself when it is the real scroll element (native: true)', () => { + const fixture = createComponent(TestScrollbarHost, [ + { provide: KBQ_SCROLLBAR_CONFIG, useValue: { native: true } } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + expect(registeredElements()).toContain(host); + }); + + it('de-registers the auto-viewport from ScrollDispatcher on destroy', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + + expect(registeredElements()).toContain(scrollEl); - expect(viewport.scrollTop).toBe(42); + fixture.destroy(); + + expect(registeredElements()).not.toContain(scrollEl); }); }); @@ -735,10 +1370,97 @@ describe(KbqScrollbar.name, () => { expect(endSpy).toHaveBeenCalled(); }); + + it('does not re-emit reachTop on subsequent updates while still at the top', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const topSpy = jest.fn(); + + fixture.componentInstance.scrollbar().reachTop.subscribe(topSpy); + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + setMetrics(track, { clientHeight: 100 }); + fixture.componentInstance.scrollbar().update(); + fixture.componentInstance.scrollbar().update(); + fixture.componentInstance.scrollbar().update(); + + expect(topSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe('outputs', () => { + it('emits scrollChange with the current scroll position on a real scroll event', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const spy = jest.fn(); + + fixture.componentInstance.scrollbar().scrollChange.subscribe(spy); + + setMetrics(scrollEl, { scrollTop: 77, scrollLeft: 12 }); + scrollEl.dispatchEvent(new Event('scroll')); + + expect(spy).toHaveBeenCalledWith({ top: 77, left: 12 }); + }); + + it('onScroll (KbqOverflowShadowSource) emits on the same real scroll event as scrollChange', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const spy = jest.fn(); + + fixture.componentInstance.scrollbar().onScroll.subscribe(spy); + expect(fixture.componentInstance.scrollbar().getScrollElement()).toBe(scrollEl); + + scrollEl.dispatchEvent(new Event('scroll')); + + expect(spy).toHaveBeenCalled(); + }); + + it('emits kbqScrollbarInitialized exactly once after initial setup', () => { + const fixture = createComponent(TestScrollbarInitializedHost); + + expect(fixture.componentInstance.initializedCount).toBe(1); + }); + + it('emits kbqScrollbarInitialized even under native: true, where there is no track/thumb of its own to render', () => { + const fixture = createComponent(TestScrollbarInitializedHost, [ + { provide: KBQ_SCROLLBAR_CONFIG, useValue: { native: true } } + ]); + + expect(fixture.componentInstance.initializedCount).toBe(1); + }); + }); + + describe('ngOnDestroy', () => { + it('removes the track/thumb DOM on destroy', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + expect(host.querySelector('.kbq-private-scrollbar-track')).toBeTruthy(); + + fixture.destroy(); + + expect(host.querySelector('.kbq-private-scrollbar-track')).toBeNull(); + }); + + it('clears a pending auto-hide timeout on destroy', () => { + const fixture = createComponent(TestScrollbarScrollVisibilityHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const clearTimeoutSpy = jest.spyOn(window, 'clearTimeout'); + + scrollEl.dispatchEvent(new Event('scroll')); + fixture.destroy(); + + expect(clearTimeoutSpy).toHaveBeenCalled(); + }); }); - describe('isAtTop / isAtBottom / isAtStart / isAtEnd', () => { - it('isAtTop / isAtBottom track the current vertical position, not just the moment it was reached', () => { + describe('isTopReached / isBottomReached / isStartReached / isEndReached', () => { + it('isTopReached / isBottomReached track the current vertical position, not just the moment it was reached', () => { const fixture = createComponent(TestScrollbarHost); const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; const scrollEl = getAutoViewport(host); @@ -749,22 +1471,22 @@ describe(KbqScrollbar.name, () => { setMetrics(track, { clientHeight: 100 }); scrollbar.update(); - expect(scrollbar.isAtTop()).toBe(true); - expect(scrollbar.isAtBottom()).toBe(false); + expect(scrollbar.isTopReached()).toBe(true); + expect(scrollbar.isBottomReached()).toBe(false); setMetrics(scrollEl, { scrollTop: 200 }); scrollbar.update(); - expect(scrollbar.isAtTop()).toBe(false); - expect(scrollbar.isAtBottom()).toBe(false); + expect(scrollbar.isTopReached()).toBe(false); + expect(scrollbar.isBottomReached()).toBe(false); setMetrics(scrollEl, { scrollTop: 400 }); scrollbar.update(); - expect(scrollbar.isAtBottom()).toBe(true); + expect(scrollbar.isBottomReached()).toBe(true); }); - it('isAtStart / isAtEnd track the current horizontal position in LTR', () => { + it('isStartReached / isEndReached track the current horizontal position in LTR', () => { const fixture = createComponent(TestScrollbarDualOverflowHost); const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; const scrollEl = getAutoViewport(host); @@ -783,17 +1505,17 @@ describe(KbqScrollbar.name, () => { setMetrics(hTrack, { clientWidth: 100 }); scrollbar.update(); - expect(scrollbar.isAtStart()).toBe(true); - expect(scrollbar.isAtEnd()).toBe(false); + expect(scrollbar.isStartReached()).toBe(true); + expect(scrollbar.isEndReached()).toBe(false); setMetrics(scrollEl, { scrollLeft: 400 }); scrollbar.update(); - expect(scrollbar.isAtStart()).toBe(false); - expect(scrollbar.isAtEnd()).toBe(true); + expect(scrollbar.isStartReached()).toBe(false); + expect(scrollbar.isEndReached()).toBe(true); }); - it('isAtStart / isAtEnd flip which physical edge they track in RTL', () => { + it('isStartReached / isEndReached flip which physical edge they track in RTL', () => { const mockDir = new MockDirectionality(); mockDir.value = 'rtl'; @@ -819,15 +1541,15 @@ describe(KbqScrollbar.name, () => { setMetrics(hTrack, { clientWidth: 100 }); scrollbar.update(); - expect(scrollbar.isAtStart()).toBe(true); - expect(scrollbar.isAtEnd()).toBe(false); + expect(scrollbar.isStartReached()).toBe(true); + expect(scrollbar.isEndReached()).toBe(false); // scrollLeft: -400 is RTL's logical end — physically the left edge. setMetrics(scrollEl, { scrollLeft: -400 }); scrollbar.update(); - expect(scrollbar.isAtStart()).toBe(false); - expect(scrollbar.isAtEnd()).toBe(true); + expect(scrollbar.isStartReached()).toBe(false); + expect(scrollbar.isEndReached()).toBe(true); }); it('reads as both at-start and at-end when there is no overflow to scroll at all', () => { @@ -839,8 +1561,8 @@ describe(KbqScrollbar.name, () => { setMetrics(scrollEl, { clientHeight: 500, scrollHeight: 500 }); scrollbar.update(); - expect(scrollbar.isAtTop()).toBe(true); - expect(scrollbar.isAtBottom()).toBe(true); + expect(scrollbar.isTopReached()).toBe(true); + expect(scrollbar.isBottomReached()).toBe(true); }); }); diff --git a/packages/components/scrollbar/private/scrollbar.ts b/packages/components/scrollbar/private/scrollbar.ts index cdf24f7353..6fdb051050 100644 --- a/packages/components/scrollbar/private/scrollbar.ts +++ b/packages/components/scrollbar/private/scrollbar.ts @@ -6,10 +6,14 @@ import { CdkScrollable, CdkVirtualScrollViewport } from '@angular/cdk/scrolling' import { DOCUMENT } from '@angular/common'; import { afterNextRender, + ApplicationRef, booleanAttribute, + ChangeDetectionStrategy, Component, + ComponentRef, computed, contentChild, + createComponent, DestroyRef, Directive, effect, @@ -115,7 +119,7 @@ export const kbqScrollbarConfigProvider = (config: KbqScrollbarConfig): Provider useValue: config }); -/** The two axes a `kbqScrollbar` always builds a track for — see `updateAxis`. */ +/** The two axes a `kbqScrollbar` always builds a track for — see `measureAxis`/`paintAxis`. */ type Axis = 'vertical' | 'horizontal'; const AXES: readonly Axis[] = ['vertical', 'horizontal']; @@ -143,21 +147,29 @@ type DragContext = { class KbqScrollbarStyleLoader {} /** - * Marks the element that is actually scrollable when it doesn't coincide with the `kbqScrollbar` - * host — e.g. a `` nested inside a `
`. When - * present, `KbqScrollbar` measures, listens to, and scrolls this element instead of its own host. + * Marks a `` nested inside a `
` as the element + * `KbqScrollbar` should actually measure, listen to, and scroll — instead of its own host. */ @Directive({ - selector: '[kbqScrollbarViewport]', - host: { class: 'kbq-private-scrollbar-viewport' } + selector: '[kbqScrollbarVirtualViewport]' }) -export class KbqScrollbarViewport { +export class KbqScrollbarVirtualViewport { /** @docs-private */ readonly elementRef = inject>(ElementRef); - /** @docs-private Set only when the viewport element also hosts `CdkVirtualScrollViewport`. */ - readonly virtualScrollViewport = inject(CdkVirtualScrollViewport, { optional: true, self: true }); + /** @docs-private */ + readonly viewport = inject(CdkVirtualScrollViewport, { self: true }); } +/** Auto-created scroll wrapper for `KbqScrollbar` when there's no explicit `kbqScrollbarVirtualViewport`. */ +@Component({ + selector: 'kbq-scrollbar-viewport', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'kbq-private-scrollbar-viewport' }, + hostDirectives: [CdkScrollable] +}) +class KbqScrollbarViewport {} + /** * Dependency-free custom scrollbar directive. Draws its own track/thumb via `Renderer2` on top of * the host's content — no third-party library, unlike `kbqScrollbar`/`kbq-scrollbar` @@ -177,6 +189,10 @@ export class KbqScrollbarViewport { '[class.kbq-private-scrollbar_rtl]': 'rtl()', '[class.kbq-private-scrollbar_disable-interaction]': 'disableInteraction()' }, + // Redundant (but harmless) whenever `scrollElement` resolves to a `kbqScrollbarVirtualViewport` + // or the auto-created viewport — both already provide their own `CdkScrollable`. Needed here + // for the remaining case: `native`/coarse pointer with no explicit viewport, where neither of + // those exists and the host itself becomes the real, scrolling element. hostDirectives: [CdkScrollable], exportAs: 'kbqScrollbar' }) @@ -189,6 +205,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { private readonly destroyRef = inject(DestroyRef); private readonly zone = inject(NgZone); private readonly injector = inject(Injector); + private readonly appRef = inject(ApplicationRef); private readonly config = inject(KBQ_SCROLLBAR_CONFIG); private readonly directionality = inject(Directionality, { optional: true }); @@ -209,7 +226,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { */ protected readonly rtl = signal(this.directionality?.value === 'rtl'); - private readonly viewport = contentChild(KbqScrollbarViewport); + private readonly viewport = contentChild(KbqScrollbarVirtualViewport); /** Controls when the custom track/thumb are shown — see `KbqScrollbarVisibility`. */ readonly visibility = input( @@ -256,13 +273,13 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { /** * Emits after every recompute (resize, content change, or manual `update()`) — thumb * size/position when the custom track/thumb are built, or just the overflow/edge measurement - * behind `isAtTop`-family signals and `reach*` otherwise (`native`, coarse pointer). + * behind `isTopReached`-family signals and `reach*` otherwise (`native`, coarse pointer). */ readonly updated = output({ alias: 'kbqScrollbarUpdated' }); // Separate from the `*At*` signals below: whether an axis overflows at all, so a track with // nothing to scroll reads as trivially at both of its own edges (see the early return in - // `updateAxis`) without that no-overflow state overwriting — and being mistaken later for — a + // `measureAxis`) without that no-overflow state overwriting — and being mistaken later for — a // real, measured edge position once content does overflow. private readonly verticalOverflows = signal(false); private readonly horizontalOverflows = signal(false); @@ -273,21 +290,21 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { private readonly horizontalAtRight = signal(false); /** Whether the vertical axis is currently scrolled all the way to the top. */ - readonly isAtTop = computed(() => !this.verticalOverflows() || this.verticalAtTop()); + readonly isTopReached = computed(() => !this.verticalOverflows() || this.verticalAtTop()); /** Whether the vertical axis is currently scrolled all the way to the bottom. */ - readonly isAtBottom = computed(() => !this.verticalOverflows() || this.verticalAtBottom()); + readonly isBottomReached = computed(() => !this.verticalOverflows() || this.verticalAtBottom()); /** * Whether the horizontal axis is currently scrolled to its logical start — the right edge in * RTL, left in LTR. Matches `scrollStart()`/`reachStart`. */ - readonly isAtStart = computed( + readonly isStartReached = computed( () => !this.horizontalOverflows() || (this.rtl() ? this.horizontalAtRight() : this.horizontalAtLeft()) ); /** * Whether the horizontal axis is currently scrolled to its logical end — the left edge in * RTL, right in LTR. Matches `scrollEnd()`/`reachEnd`. */ - readonly isAtEnd = computed( + readonly isEndReached = computed( () => !this.horizontalOverflows() || (this.rtl() ? this.horizontalAtLeft() : this.horizontalAtRight()) ); @@ -311,7 +328,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { private dragContext: DragContext | null = null; /** - * Auto-created scroll wrapper, only when there's no explicit `kbqScrollbarViewport` and the + * Auto-created scroll wrapper, only when there's no explicit `kbqScrollbarVirtualViewport` and the * custom track/thumb are actually being rendered. The track/thumb are `position: absolute` * siblings of this wrapper on the (non-scrolling) host — if they were children of the element * that itself has `overflow: auto` (i.e. the host, when the host is also the scroll element), @@ -319,6 +336,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { * an overlay. */ private autoViewport: HTMLElement | null = null; + private autoViewportRef: ComponentRef | null = null; constructor() { inject(_CdkPrivateStyleLoader).load(KbqScrollbarStyleLoader); @@ -341,18 +359,18 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { if (buildingCustomUi) { this.applyNativeHiding(); - this.applyGutterReservation(); + this.wireGutterReservation(); this.buildDom(); this.wireGlobalDragListeners(); this.wireVisibility(); } - // Runs even under `native`/coarse pointer — `isAtTop`-family signals and `reach*` + // Runs even under `native`/coarse pointer — `isTopReached`-family signals and `reach*` // outputs still need an initial measurement, not just the custom track/thumb. this.recompute(); - // A `kbqScrollbarViewport` backed by `CdkVirtualScrollViewport` (or any scroll - // element whose own layout isn't settled yet) can make the very first recompute + // A `kbqScrollbarVirtualViewport` (or any scroll element whose own layout isn't + // settled yet) can make the very first recompute // under-measure — a 0-height track, or overflow that hasn't shown up yet. Its outer // box may never resize again afterwards, so there's no guaranteed later trigger to // correct it — retry once, next frame. @@ -363,20 +381,27 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { }); } - /** Moves the host's existing content into a new wrapper so the host itself can stay a - * non-scrolling, non-clipping positioning context for the track/thumb overlay. */ + /** + * Moves the host's existing content into a new wrapper so the host itself can stay a + * non-scrolling, non-clipping positioning context for the track/thumb overlay. A real + * component (via `createComponent`), not a bare `Renderer2`-created element, specifically so + * `KbqScrollbarViewport`'s `hostDirectives: [CdkScrollable]` registers this element (the + * real scroll element) with `ScrollDispatcher` through Angular's own directive lifecycle. + */ private createAutoViewport(): HTMLElement { - const wrapper = this.renderer.createElement('div') as HTMLElement; + this.autoViewportRef = createComponent(KbqScrollbarViewport, { + environmentInjector: this.appRef.injector, + elementInjector: this.injector, + projectableNodes: [Array.from(this.hostElement.childNodes)] + }); - this.renderer.addClass(wrapper, 'kbq-private-scrollbar-viewport'); + this.appRef.attachView(this.autoViewportRef.hostView); - for (const child of Array.from(this.hostElement.childNodes)) { - this.renderer.appendChild(wrapper, child); - } + const element = this.autoViewportRef.location.nativeElement as HTMLElement; - this.renderer.appendChild(this.hostElement, wrapper); + this.renderer.appendChild(this.hostElement, element); - return wrapper; + return element; } ngOnDestroy(): void { @@ -384,14 +409,19 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { this.tracks.forEach((track) => this.renderer.removeChild(this.hostElement, track)); this.tracks.clear(); this.thumbs.clear(); + + if (this.autoViewportRef) { + this.appRef.detachView(this.autoViewportRef.hostView); + this.autoViewportRef.destroy(); + } } - /** Scrolls the effective scroll element (or delegates to `kbqScrollbarViewport`'s virtual scroll viewport). */ + /** Scrolls the effective scroll element (or delegates to `kbqScrollbarVirtualViewport`'s viewport). */ scrollTo(options: KbqScrollbarScrollToOptions): void { - const virtualScrollViewport = this.viewport()?.virtualScrollViewport; + const viewport = this.viewport()?.viewport; - if (virtualScrollViewport) { - virtualScrollViewport.scrollToOffset(options.top ?? options.left ?? 0, options.behavior ?? 'auto'); + if (viewport) { + viewport.scrollToOffset(options.top ?? options.left ?? 0, options.behavior ?? 'auto'); return; } @@ -522,12 +552,31 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { this.renderer.setStyle(this.scrollElement, 'overflowX', 'auto'); } + /** Reacts to `kbqScrollbarFloating`/direction changing at runtime, not just their value at first render. */ + private wireGutterReservation(): void { + effect(() => this.applyGutterReservation(), { injector: this.injector }); + } + private applyGutterReservation(): void { - if (this.floating()) return; + // Always clear the *other* side first — otherwise flipping direction (or `floating` + // itself) can leave a stale gutter behind instead of moving/removing it: this only ever + // sets the side matching the current direction, so the opposite one would never get + // cleared on its own. + const activeSide = this.rtl() ? 'paddingLeft' : 'paddingRight'; + const inactiveSide = this.rtl() ? 'paddingRight' : 'paddingLeft'; + + this.renderer.removeStyle(this.scrollElement, inactiveSide); + + if (this.floating()) { + this.renderer.removeStyle(this.scrollElement, activeSide); + this.renderer.removeStyle(this.scrollElement, 'paddingBottom'); + + return; + } const dimension = 'var(--kbq-private-scrollbar-size-track-dimension)'; - this.renderer.setStyle(this.scrollElement, this.rtl() ? 'paddingLeft' : 'paddingRight', dimension); + this.renderer.setStyle(this.scrollElement, activeSide, dimension); this.renderer.setStyle(this.scrollElement, 'paddingBottom', dimension); } @@ -556,11 +605,22 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { this.wireTrackInteraction(axis, track, thumb); } + /** + * Whether a `pointerdown` should be treated as a drag/track-click start — the primary button + * (left click, or a single touch/pen contact) only. `isPrimary` is checked against `false` + * specifically, not falsiness: a plain `MouseEvent` (as opposed to a real `PointerEvent`) + * doesn't have the property at all, and treating that as "not primary" would reject events + * that never claimed to be secondary in the first place. + */ + private isPrimaryPointerDown(event: PointerEvent): boolean { + return event.button === 0 && event.isPrimary !== false; + } + private wireTrackInteraction(axis: Axis, track: HTMLElement, thumb: HTMLElement): void { fromEvent(thumb, 'pointerdown') .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((event) => { - if (this.disableInteraction()) return; + if (this.disableInteraction() || !this.isPrimaryPointerDown(event)) return; this.beginInteraction(axis, track, thumb, event, false); }); @@ -570,7 +630,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { fromEvent(track, 'pointerdown') .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((event) => { - if (this.disableInteraction() || event.target === thumb) return; + if (this.disableInteraction() || event.target === thumb || !this.isPrimaryPointerDown(event)) return; this.beginInteraction(axis, track, thumb, event, true); }); } @@ -596,6 +656,17 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { if (!this.dragContext || event.pointerId !== this.dragContext.pointerId) return; this.endDrag(); }); + + // A gesture can be interrupted outside the normal pointerup path — e.g. a touch drag cut + // short by an OS-level gesture, or the browser revoking pointer capture. Without this, + // `dragContext`/the `_dragging` class/`user-select: none` would be stuck until some later, + // unrelated pointerup happens to carry the same `pointerId`. + fromEvent(this.document, 'pointercancel') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((event) => { + if (!this.dragContext || event.pointerId !== this.dragContext.pointerId) return; + this.endDrag(); + }); } private beginInteraction( @@ -610,7 +681,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { const isVertical = axis === 'vertical'; const trackRect = track.getBoundingClientRect(); const thumbRect = thumb.getBoundingClientRect(); - // Inset by the same `cssTrackPadding` gap `updateAxis` positions the thumb within, so a + // Inset by the same `cssTrackPadding` gap `paintAxis` positions the thumb within, so a // drag maps 1:1 to the thumb's actual travel range instead of the track's full box. const trackStart = (isVertical ? trackRect.top : trackRect.left) + this.cssTrackPadding; const trackLength = (isVertical ? trackRect.height : trackRect.width) - 2 * this.cssTrackPadding; @@ -662,6 +733,20 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { this.dragContext = null; this.renderer.removeClass(this.hostElement, 'kbq-private-scrollbar_dragging'); + + // `beginInteraction()` force-shows the scrollbar regardless of mode — restore whatever the + // current mode's steady state actually is now that the drag is over, the same way a real + // scroll/pointerleave would. Without this it can stay visible forever: 'scroll' mode never + // got a `showTemporarily()` timer scheduled for this reveal (only a direct `setVisible`), + // and 'hover' mode never got a `pointerleave` if the pointer left while dragging (that + // listener skips hiding on purpose while a drag is in progress). + const mode = this.visibility(); + + if (mode === 'scroll') { + this.showTemporarily(); + } else if (mode === 'hover' && !this.isPointerOver) { + this.setVisible(false); + } } private wireVisibility(): void { @@ -687,10 +772,16 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { // moment; ongoing hover-driven toggling still comes from the listeners above. 'scroll' // mode's temporary reveal-on-scroll keeps coming from onUserScroll()/showTemporarily() — // 'hidden' starts from the same steady state but never gets revealed from anywhere. + // + // Clearing any pending auto-hide timeout here, before applying the new mode's steady + // state, matters: without it, a 'scroll'-mode reveal's timeout can outlive a switch to + // 'always' and fire later, hiding a scrollbar that's supposed to stay permanently visible. effect( () => { const mode = this.visibility(); + clearTimeout(this.autoHideTimeoutId); + if (mode === 'always') { this.setVisible(true); } else if (mode === 'hover') { @@ -726,16 +817,27 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { } private recompute(): void { + const measurements = new Map(); + for (const axis of AXES) { - this.updateAxis(axis); + const measurement = this.measureAxis(axis); + + if (measurement) measurements.set(axis, measurement); } // Corner-avoidance only makes sense between two custom tracks — a no-op anyway without - // them, but skip it outright under `native`/coarse pointer. + // them, but skip it outright under `native`/coarse pointer. Must run before `paintAxis()` + // below, not after: it can shrink a track's real size via CSS, and `paintAxis()` measures + // that size to place the thumb — sizing it first would size/position the thumb against a + // stale, pre-shrink track on the very frame content starts overflowing both axes. if (!this.native && !this.isCoarsePointer) { this.updateCornerAvoidance(); } + for (const [axis, measurement] of measurements) { + this.paintAxis(axis, measurement.scrollOffset, measurement.scrollRange); + } + this.emit(this.updated, undefined); } @@ -770,12 +872,18 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { } /** - * Measures overflow/edge state for `isAtTop`-family signals and `reach*` outputs, then — only - * when this axis actually has a track/thumb built (i.e. not `native`, not a coarse pointer) — - * paints the thumb's size and position. The measurement half runs unconditionally: those - * signals/outputs are meant to keep working even when there's no custom UI of our own to show. + * Measures overflow/edge state for `isTopReached`-family signals and `reach*` outputs, and shows or + * hides this axis's track accordingly — runs unconditionally, regardless of `native`/coarse + * pointer, since those signals/outputs are meant to keep working even when there's no custom + * UI of our own to show. Returns the scroll offset/range `paintAxis()` needs to size and + * position the thumb, or `null` when this axis doesn't overflow (nothing to paint). + * + * Deliberately doesn't measure/paint the thumb itself — `recompute()` runs + * `updateCornerAvoidance()` between this and `paintAxis()`, since corner avoidance can shrink + * a track's real size via CSS, and the thumb needs to be sized against that final size, not + * whatever it measured before corner avoidance ran. */ - private updateAxis(axis: Axis): void { + private measureAxis(axis: Axis): { scrollOffset: number; scrollRange: number } | null { const scrollEl = this.scrollElement; const isVertical = axis === 'vertical'; const viewportSize = isVertical ? scrollEl.clientHeight : scrollEl.clientWidth; @@ -783,20 +891,28 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { const rawScrollOffset = isVertical ? scrollEl.scrollTop : scrollEl.scrollLeft; const overflows = isVertical ? this.verticalOverflows : this.horizontalOverflows; const track = this.tracks.get(axis); - const thumb = this.thumbs.get(axis); if (contentSize <= viewportSize) { if (track) this.renderer.setStyle(track, 'display', 'none'); - // Nothing to scroll on this axis — `isAtTop()`/`isAtBottom()`-family signals short- + // Nothing to scroll on this axis — `isTopReached()`/`isBottomReached()`-family signals short- // circuit to `true` off this flag, without touching the real edge-position signals // below (those stay at whatever they last genuinely measured). overflows.set(false); - return; + return null; } overflows.set(true); + // Un-hide *before* `paintAxis()` measures, not after: a `CdkVirtualScrollViewport` (or any + // scroll element whose layout isn't settled on the very first pass) can make an earlier + // call land in the branch above and set `display: none`. A hidden element always measures + // a 0 clientHeight/Width, so if we measured first and only unhid on success, a stale + // `display: none` would make every subsequent pass measure 0 and bail out below, trapping + // the track hidden forever even once real content overflow is detected here. Also needs to + // happen before `updateCornerAvoidance()`, which checks each track's current visibility. + if (track) this.renderer.removeStyle(track, 'display'); + const scrollRange = contentSize - viewportSize; // Physical distance from the left/top edge, in [0, scrollRange] — normalizes RTL's negative // `scrollLeft` range (0 at the right edge, down to -scrollRange at the left) onto the same @@ -806,18 +922,21 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { this.checkReachedEdges(axis, scrollOffset, scrollRange); + return { scrollOffset, scrollRange }; + } + + /** Sizes and positions this axis's thumb, given the scroll offset/range `measureAxis()` already computed. */ + private paintAxis(axis: Axis, scrollOffset: number, scrollRange: number): void { + const track = this.tracks.get(axis); + const thumb = this.thumbs.get(axis); + // No custom track/thumb to paint for this axis — `native`/coarse pointer, or the DOM for - // it hasn't been built. Measurement above already ran either way. + // it hasn't been built. if (!track || !thumb) return; - // Un-hide *before* measuring, not after: a `CdkVirtualScrollViewport` (or any scroll - // element whose layout isn't settled on the very first pass) can make an earlier call - // land in the branch above and set `display: none`. A hidden element always measures a - // 0 clientHeight/Width, so if we measured first and only unhid on success, a stale - // `display: none` would make every subsequent pass measure 0 and bail out below, - // trapping the track hidden forever even once real content overflow is detected here. - this.renderer.removeStyle(track, 'display'); - + const isVertical = axis === 'vertical'; + const scrollEl = this.scrollElement; + const viewportSize = isVertical ? scrollEl.clientHeight : scrollEl.clientWidth; const trackLength = isVertical ? track.clientHeight : track.clientWidth; // Track not laid out yet (e.g. inserted this same tick) — a later pass will retry. @@ -830,7 +949,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { const travelLength = trackLength - 2 * this.cssTrackPadding; // Round the ratio up so the thumb never reads as undersized from float error. - const ratio = Math.min(1, Math.ceil((viewportSize / contentSize) * 100) / 100); + const ratio = Math.min(1, Math.ceil((viewportSize / (viewportSize + scrollRange)) * 100) / 100); const thumbSize = Math.min(travelLength, Math.max(ratio * travelLength, this.cssMinThumbSize)); const scrollRatio = scrollRange > 0 ? scrollOffset / scrollRange : 0; const thumbOffset = this.cssTrackPadding + scrollRatio * (travelLength - thumbSize); @@ -845,9 +964,10 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { } /** - * The writable `isAtTop`/`isAtStart`-family signals for one physical axis — `[start, end]`, - * both always physically named (top/bottom, left/right); the public `isAtStart`/`isAtEnd` - * computed signals are what re-map "left/right" onto RTL-aware "start/end". + * The writable `isTopReached`/`isStartReached`-family signals for one physical axis — + * `[start, end]`, both always physically named (top/bottom, left/right); the public + * `isStartReached`/`isEndReached` computed signals are what re-map "left/right" onto + * RTL-aware "start/end". */ private edgeSignals(axis: Axis): readonly [WritableSignal, WritableSignal] { return axis === 'vertical' diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts index c4f2316343..47f9f3869e 100644 --- a/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts @@ -10,16 +10,16 @@ import { KbqScrollbar } from '@koobiq/components/scrollbar/private'; imports: [KbqScrollbar, KbqButtonModule], template: `
- - - -
`, styles: ` @@ -87,7 +76,14 @@ import { KbqScrollbar } from '@koobiq/components/scrollbar/private'; } .example-scrollbar { + overflow: auto; + resize: both; height: 200px; + min-height: 200px; + max-height: 400px; + width: 100%; + min-width: 200px; + max-width: 100%; border-radius: var(--kbq-size-border-radius); background-color: var(--kbq-background-bg-secondary); } diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example.ts index d6364c450c..3423e5a346 100644 --- a/packages/docs-examples/components/scrollbar/private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example.ts +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example.ts @@ -1,19 +1,19 @@ import { ScrollingModule } from '@angular/cdk/scrolling'; import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { KbqButtonModule } from '@koobiq/components/button'; -import { KbqScrollbar, KbqScrollbarViewport } from '@koobiq/components/scrollbar/private'; +import { KbqScrollbar, KbqScrollbarVirtualViewport } from '@koobiq/components/scrollbar/private'; /** * @title Scrollbar with virtual scroll */ @Component({ selector: 'private-scrollbar-virtual-scroll-example', - imports: [KbqScrollbar, KbqScrollbarViewport, ScrollingModule, KbqButtonModule], + imports: [KbqScrollbar, KbqScrollbarVirtualViewport, ScrollingModule, KbqButtonModule], template: `
- +
{{ item }}
diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-visibility/private-scrollbar-visibility-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-visibility/private-scrollbar-visibility-example.ts index 1327c0f3fb..4f8e1c2f63 100644 --- a/packages/docs-examples/components/scrollbar/private-scrollbar-visibility/private-scrollbar-visibility-example.ts +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-visibility/private-scrollbar-visibility-example.ts @@ -60,7 +60,14 @@ import { KbqSelectModule } from '@koobiq/components/select'; } .example-scrollbar { + overflow: auto; + resize: both; height: 200px; + min-height: 200px; + max-height: 400px; + width: 100%; + min-width: 200px; + max-width: 100%; border-radius: var(--kbq-size-border-radius); background-color: var(--kbq-background-bg-secondary); } diff --git a/packages/e2e/routes.ts b/packages/e2e/routes.ts index 20d269da13..9c9e68cbc5 100644 --- a/packages/e2e/routes.ts +++ b/packages/e2e/routes.ts @@ -1,7 +1,12 @@ import { Routes } from '@angular/router'; import { E2eFormHorizontal } from 'packages/components/core/forms/e2e'; import { E2eTypographyStyles } from 'packages/components/core/styles/typography/e2e'; -import { E2ePrivateScrollbarStateAndStyle } from 'packages/components/scrollbar/private/e2e'; +import { + E2ePrivateScrollbarDrag, + E2ePrivateScrollbarHoverVisibility, + E2ePrivateScrollbarScrollTo, + E2ePrivateScrollbarStateAndStyle +} from 'packages/components/scrollbar/private/e2e'; import { E2eAccordionStates } from '../components/accordion/e2e'; import { E2eActionsPanelWithOverlayContainer } from '../components/actions-panel/e2e'; import { E2eAlertStateAndStyle } from '../components/alert/e2e'; @@ -192,6 +197,9 @@ const components = [ E2eSearchExpandableStates, E2eInputStateAndStyle, E2ePrivateScrollbarStateAndStyle, + E2ePrivateScrollbarDrag, + E2ePrivateScrollbarScrollTo, + E2ePrivateScrollbarHoverVisibility, E2eRadioStateAndStyle, E2eProgressBarStateAndStyle, E2eProgressSpinnerStates, diff --git a/tools/public_api_guard/components/scrollbar-private.api.md b/tools/public_api_guard/components/scrollbar-private.api.md index 7f49c92cc5..3efc66d46d 100644 --- a/tools/public_api_guard/components/scrollbar-private.api.md +++ b/tools/public_api_guard/components/scrollbar-private.api.md @@ -36,10 +36,10 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { readonly floating: _angular_core.InputSignalWithTransform; getScrollElement(): HTMLElement | null; readonly initialized: OutputEmitterRef; - readonly isAtBottom: _angular_core.Signal; - readonly isAtEnd: _angular_core.Signal; - readonly isAtStart: _angular_core.Signal; - readonly isAtTop: _angular_core.Signal; + readonly isBottomReached: _angular_core.Signal; + readonly isEndReached: _angular_core.Signal; + readonly isStartReached: _angular_core.Signal; + readonly isTopReached: _angular_core.Signal; // (undocumented) ngOnDestroy(): void; readonly onScroll: Observable; @@ -84,7 +84,7 @@ export class KbqScrollbarModule { // (undocumented) static ɵinj: _angular_core.ɵɵInjectorDeclaration; // (undocumented) - static ɵmod: _angular_core.ɵɵNgModuleDeclaration; + static ɵmod: _angular_core.ɵɵNgModuleDeclaration; } // @public @@ -108,13 +108,13 @@ export type KbqScrollbarScrollToOptions = Partial<{ }>; // @public -export class KbqScrollbarViewport { +export class KbqScrollbarVirtualViewport { readonly elementRef: ElementRef; - readonly virtualScrollViewport: CdkVirtualScrollViewport | null; + readonly viewport: CdkVirtualScrollViewport; // (undocumented) - static ɵdir: _angular_core.ɵɵDirectiveDeclaration; + static ɵdir: _angular_core.ɵɵDirectiveDeclaration; // (undocumented) - static ɵfac: _angular_core.ɵɵFactoryDeclaration; + static ɵfac: _angular_core.ɵɵFactoryDeclaration; } // @public From a4e0650b643cec4915f606ce2edd8ae5d6c1a060 Mon Sep 17 00:00:00 2001 From: Artem Belik Date: Thu, 30 Jul 2026 13:22:49 +0300 Subject: [PATCH 03/13] refactor: content-panel --- .../content-panel/content-panel.en.md | 13 ------------ .../content-panel/content-panel.ru.md | 13 ------------ .../components/content-panel/content-panel.ts | 20 +++++++++++-------- .../content-panel/e2e.playwright-spec.ts | 4 ++-- .../components/content-panel.api.md | 2 +- 5 files changed, 15 insertions(+), 37 deletions(-) diff --git a/packages/components/content-panel/content-panel.en.md b/packages/components/content-panel/content-panel.en.md index 4c809ca57b..9900829978 100644 --- a/packages/components/content-panel/content-panel.en.md +++ b/packages/components/content-panel/content-panel.en.md @@ -1,18 +1,5 @@ `KbqContentPanel` - a slide-out side panel that shifts adjacent content. Often used to implement a quick preview mode for entities from a table. -
-
Note
-
- -The component uses [Scrollbar](/en/components/scrollbar), so its dependencies must be installed: - -```bash -npm install overlayscrollbars@2.7.3 -``` - -
-
- ### Grid and content-panel diff --git a/packages/components/content-panel/content-panel.ru.md b/packages/components/content-panel/content-panel.ru.md index 9c23233c8f..1fe730172a 100644 --- a/packages/components/content-panel/content-panel.ru.md +++ b/packages/components/content-panel/content-panel.ru.md @@ -1,18 +1,5 @@ `KbqContentPanel` - выезжающая сбоку панель, которая сдвигает соседний контент. Часто используется, чтобы реализовать режим быстрого просмотра сущности из таблицы. -
-
Обрати внимание
-
- -Компонент использует [Scrollbar](/ru/components/scrollbar), поэтому необходимо установить его зависимости: - -```bash -npm install overlayscrollbars@2.7.3 -``` - -
-
- ### Грид и контент-панель diff --git a/packages/components/content-panel/content-panel.ts b/packages/components/content-panel/content-panel.ts index 9c8e5ad676..9a1c64da5c 100644 --- a/packages/components/content-panel/content-panel.ts +++ b/packages/components/content-panel/content-panel.ts @@ -24,7 +24,7 @@ import { } from '@koobiq/components/core'; import { KbqIconModule } from '@koobiq/components/icon'; import { KbqResizable, KbqResizer, KbqResizerSizeChangeEvent } from '@koobiq/components/resizer'; -import { KbqScrollbar, KbqScrollbarModule } from '@koobiq/components/scrollbar'; +import { KbqScrollbar } from '@koobiq/components/scrollbar/private'; import { SizeL } from '@koobiq/design-tokens'; const KBQ_CONTENT_PANEL_CONTAINER_CONTENT_ANIMATION = trigger('contentAnimation', [ @@ -135,15 +135,16 @@ export class KbqContentPanelHeader { @Component({ selector: 'kbq-content-panel-body', - imports: [KbqScrollbarModule, KbqOverflowShadowContainer], + imports: [KbqScrollbar, KbqOverflowShadowContainer], template: ` - - +
`, styleUrl: './content-panel-body.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -202,7 +203,10 @@ export class KbqContentPanelFooter { export class KbqContentPanel { private readonly contentPanelBody = contentChild(KbqContentPanelBody); - /** Current body overflow state. Read by the header and footer to render their `box-shadow`. */ + /** + * Current body overflow state. Read by the header and footer to render their `box-shadow`. + * @docs-private + */ readonly bodyOverflow = computed( () => this.contentPanelBody()?.overflowContainer().overflow() ?? { top: false, bottom: false } ); @@ -210,11 +214,11 @@ export class KbqContentPanel { @Component({ selector: 'kbq-content-panel-container', - imports: [KbqResizable, KbqResizer, KbqScrollbarModule], + imports: [KbqResizable, KbqResizer, KbqScrollbar], template: ` - +
- +
@if (openedState()) {
{ test('should show header shadow after scrolling down', async ({ page }) => { await page.goto('/E2eContentPanelScrollOverflow'); - await page.locator('.kbq-content-panel-body [data-overlayscrollbars-contents]').evaluate((el) => { + await page.locator('.kbq-content-panel-body .kbq-private-scrollbar-viewport').evaluate((el) => { el.scrollTop = 50; }); @@ -35,7 +35,7 @@ test.describe('KbqContentPanelModule', () => { test('should show both shadows when scrolled to the middle', async ({ page }) => { await page.goto('/E2eContentPanelScrollOverflow'); - await page.locator('.kbq-content-panel-body [data-overlayscrollbars-contents]').evaluate((el) => { + await page.locator('.kbq-content-panel-body .kbq-private-scrollbar-viewport').evaluate((el) => { el.scrollTop = Math.floor((el.scrollHeight - el.clientHeight) / 2); }); diff --git a/tools/public_api_guard/components/content-panel.api.md b/tools/public_api_guard/components/content-panel.api.md index a7c2b8f602..5949373cee 100644 --- a/tools/public_api_guard/components/content-panel.api.md +++ b/tools/public_api_guard/components/content-panel.api.md @@ -9,7 +9,7 @@ import { KbqButtonStyles } from '@koobiq/components/button'; import { KbqComponentColors } from '@koobiq/components/core'; import { KbqOverflowShadowContainer } from '@koobiq/components/core'; import { KbqResizerSizeChangeEvent } from '@koobiq/components/resizer'; -import { KbqScrollbar } from '@koobiq/components/scrollbar'; +import { KbqScrollbar } from '@koobiq/components/scrollbar/private'; import * as _koobiq_components_core from '@koobiq/components/core'; // @public (undocumented) From 692ac95b31fb758a2c3994b433f246e9704a634c Mon Sep 17 00:00:00 2001 From: Artem Belik Date: Thu, 30 Jul 2026 14:38:47 +0300 Subject: [PATCH 04/13] refactor: docs --- .../scrollbar/examples.scrollbar.en.md | 12 +- .../scrollbar/examples.scrollbar.ru.md | 12 +- .../private/examples.scrollbar.en.md | 5 - .../private/examples.scrollbar.ru.md | 5 - .../scrollbar/private/scrollbar.en.md | 5 - .../scrollbar/private/scrollbar.ru.md | 11 -- packages/components/scrollbar/scrollbar.en.md | 4 + packages/components/scrollbar/scrollbar.ru.md | 4 + .../private-scrollbar-scroll-to-example.ts | 7 ++ packages/docs-examples/example-module.ts | 108 ++++++++++++++++++ 10 files changed, 145 insertions(+), 28 deletions(-) delete mode 100644 packages/components/scrollbar/private/examples.scrollbar.en.md delete mode 100644 packages/components/scrollbar/private/examples.scrollbar.ru.md delete mode 100644 packages/components/scrollbar/private/scrollbar.en.md delete mode 100644 packages/components/scrollbar/private/scrollbar.ru.md diff --git a/packages/components/scrollbar/examples.scrollbar.en.md b/packages/components/scrollbar/examples.scrollbar.en.md index d6f3d9fc4e..62299dbc02 100644 --- a/packages/components/scrollbar/examples.scrollbar.en.md +++ b/packages/components/scrollbar/examples.scrollbar.en.md @@ -1 +1,11 @@ - +## Scrollbar visibility + + + +## Virtual scroll + + + +## Programmatic scrollbar control + + diff --git a/packages/components/scrollbar/examples.scrollbar.ru.md b/packages/components/scrollbar/examples.scrollbar.ru.md index d6f3d9fc4e..5c057d35aa 100644 --- a/packages/components/scrollbar/examples.scrollbar.ru.md +++ b/packages/components/scrollbar/examples.scrollbar.ru.md @@ -1 +1,11 @@ - +## Отображение скроллбара + + + +## Виртуальный скролл + + + +## Программное управление скроллом + + diff --git a/packages/components/scrollbar/private/examples.scrollbar.en.md b/packages/components/scrollbar/private/examples.scrollbar.en.md deleted file mode 100644 index 2faef3848c..0000000000 --- a/packages/components/scrollbar/private/examples.scrollbar.en.md +++ /dev/null @@ -1,5 +0,0 @@ -🚧 **Documentation in progress** 🚧 - -Unfortunately, the documentation for this section is not ready yet. We are actively working on its creation and plan to add it soon. - -If you would like to contribute to the documentation or have any questions, please feel free to [open an issue](https://github.com/koobiq/angular-components/issues) in our GitHub repository. diff --git a/packages/components/scrollbar/private/examples.scrollbar.ru.md b/packages/components/scrollbar/private/examples.scrollbar.ru.md deleted file mode 100644 index b7a203c6ab..0000000000 --- a/packages/components/scrollbar/private/examples.scrollbar.ru.md +++ /dev/null @@ -1,5 +0,0 @@ -🚧 **Документация в процессе написания** 🚧 - -К сожалению, документация для этого раздела еще не готова. Мы активно работаем над ее созданием и планируем добавить в ближайшее время. - -Если вы хотите помочь в написании документации или у вас есть вопросы, пожалуйста, [создайте issue](https://github.com/koobiq/angular-components/issues) в нашем репозитории на GitHub. diff --git a/packages/components/scrollbar/private/scrollbar.en.md b/packages/components/scrollbar/private/scrollbar.en.md deleted file mode 100644 index 2faef3848c..0000000000 --- a/packages/components/scrollbar/private/scrollbar.en.md +++ /dev/null @@ -1,5 +0,0 @@ -🚧 **Documentation in progress** 🚧 - -Unfortunately, the documentation for this section is not ready yet. We are actively working on its creation and plan to add it soon. - -If you would like to contribute to the documentation or have any questions, please feel free to [open an issue](https://github.com/koobiq/angular-components/issues) in our GitHub repository. diff --git a/packages/components/scrollbar/private/scrollbar.ru.md b/packages/components/scrollbar/private/scrollbar.ru.md deleted file mode 100644 index 29a28eb5bb..0000000000 --- a/packages/components/scrollbar/private/scrollbar.ru.md +++ /dev/null @@ -1,11 +0,0 @@ -`KbqScrollbar` - это компонент который используется для настройки параметров скроллбара. - - - -## Виртуальный скролл - - - -## Программное управление скроллом - - diff --git a/packages/components/scrollbar/scrollbar.en.md b/packages/components/scrollbar/scrollbar.en.md index 105897bd94..c9e60dee81 100644 --- a/packages/components/scrollbar/scrollbar.en.md +++ b/packages/components/scrollbar/scrollbar.en.md @@ -35,3 +35,7 @@ npm install overlayscrollbars@2.7.3 ... ``` + +## Programmatic scrollbar control + + diff --git a/packages/components/scrollbar/scrollbar.ru.md b/packages/components/scrollbar/scrollbar.ru.md index 7128e41a89..995e836982 100644 --- a/packages/components/scrollbar/scrollbar.ru.md +++ b/packages/components/scrollbar/scrollbar.ru.md @@ -35,3 +35,7 @@ npm install overlayscrollbars@2.7.3 ... ``` + +## Программное управление скроллом + + diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts index 47f9f3869e..c30b08917e 100644 --- a/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts @@ -57,6 +57,13 @@ import { KbqScrollbar } from '@koobiq/components/scrollbar/private'; work to test each guess. One of the measures of the strength of an encryption system is how long it would theoretically take an attacker to mount a successful brute-force attack against it.[3]

+

+ In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists + of an attacker submitting many possible keys or passwords with the hope of eventually guessing + correctly. This strategy can theoretically be used to break any form of encryption that is not + information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of + successfully guessing the key is negligible. +

`, styles: ` diff --git a/packages/docs-examples/example-module.ts b/packages/docs-examples/example-module.ts index c80b0ba7d9..22507f7c47 100644 --- a/packages/docs-examples/example-module.ts +++ b/packages/docs-examples/example-module.ts @@ -263,6 +263,19 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "ag-grid-copy-selected-example.ts", "importPath": "components/ag-grid" }, + "ag-grid-export": { + "packagePath": "components/ag-grid/ag-grid-export", + "title": "AG Grid with CSV, XLSX and PDF export", + "componentName": "AgGridExportExample", + "files": [ + "ag-grid-export-example.ts" + ], + "localImportFiles": [], + "selector": "ag-grid-export-example", + "additionalComponents": [], + "primaryFile": "ag-grid-export-example.ts", + "importPath": "components/ag-grid" + }, "ag-grid-external-filter-state": { "packagePath": "components/ag-grid/ag-grid-external-filter-state", "title": "AG Grid with `KbqAgGridExternalFilterState` directive", @@ -371,6 +384,45 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "ag-grid-row-dragging-example.ts", "importPath": "components/ag-grid" }, + "ag-grid-row-focus-state": { + "packagePath": "components/ag-grid/ag-grid-row-focus-state", + "title": "AG Grid with `KbqAgGridRowFocusState` directive", + "componentName": "AgGridRowFocusStateExample", + "files": [ + "ag-grid-row-focus-state-example.ts" + ], + "localImportFiles": [], + "selector": "ag-grid-row-focus-state-example", + "additionalComponents": [], + "primaryFile": "ag-grid-row-focus-state-example.ts", + "importPath": "components/ag-grid" + }, + "ag-grid-row-group": { + "packagePath": "components/ag-grid/ag-grid-row-group", + "title": "AG Grid with `KbqAgGridRowGroup` directive", + "componentName": "AgGridRowGroupExample", + "files": [ + "ag-grid-row-group-example.ts" + ], + "localImportFiles": [], + "selector": "ag-grid-row-group-example", + "additionalComponents": [], + "primaryFile": "ag-grid-row-group-example.ts", + "importPath": "components/ag-grid" + }, + "ag-grid-row-selection-state": { + "packagePath": "components/ag-grid/ag-grid-row-selection-state", + "title": "AG Grid with `KbqAgGridRowSelectionState` directive", + "componentName": "AgGridRowSelectionStateExample", + "files": [ + "ag-grid-row-selection-state-example.ts" + ], + "localImportFiles": [], + "selector": "ag-grid-row-selection-state-example", + "additionalComponents": [], + "primaryFile": "ag-grid-row-selection-state-example.ts", + "importPath": "components/ag-grid" + }, "ag-grid-skeleton-cell-renderer": { "packagePath": "components/ag-grid/ag-grid-skeleton-cell-renderer", "title": "AG Grid with skeleton cell renderer", @@ -1564,6 +1616,7 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "files": [ "dropdown-item-progress-example.ts" ], + "localImportFiles": [], "selector": "dropdown-item-progress-example", "additionalComponents": [], "primaryFile": "dropdown-item-progress-example.ts", @@ -1672,6 +1725,7 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "files": [ "dropdown-with-footer-example.ts" ], + "localImportFiles": [], "selector": "dropdown-with-footer-example", "additionalComponents": [], "primaryFile": "dropdown-with-footer-example.ts", @@ -2347,6 +2401,7 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "files": [ "filter-bar-locked-options-example.ts" ], + "localImportFiles": [], "selector": "filter-bar-locked-options-example", "additionalComponents": [], "primaryFile": "filter-bar-locked-options-example.ts", @@ -4594,6 +4649,45 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "resizer-overview-example.ts", "importPath": "components/resizer" }, + "private-scrollbar-scroll-to": { + "packagePath": "components/scrollbar/private-scrollbar-scroll-to", + "title": "Scrollbar scrollTo methods", + "componentName": "PrivateScrollbarScrollToExample", + "files": [ + "private-scrollbar-scroll-to-example.ts" + ], + "localImportFiles": [], + "selector": "private-scrollbar-scroll-to-example", + "additionalComponents": [], + "primaryFile": "private-scrollbar-scroll-to-example.ts", + "importPath": "components/scrollbar" + }, + "private-scrollbar-virtual-scroll": { + "packagePath": "components/scrollbar/private-scrollbar-virtual-scroll", + "title": "Scrollbar with virtual scroll", + "componentName": "PrivateScrollbarVirtualScrollExample", + "files": [ + "private-scrollbar-virtual-scroll-example.ts" + ], + "localImportFiles": [], + "selector": "private-scrollbar-virtual-scroll-example", + "additionalComponents": [], + "primaryFile": "private-scrollbar-virtual-scroll-example.ts", + "importPath": "components/scrollbar" + }, + "private-scrollbar-visibility": { + "packagePath": "components/scrollbar/private-scrollbar-visibility", + "title": "Scrollbar visibility", + "componentName": "PrivateScrollbarVisibilityExample", + "files": [ + "private-scrollbar-visibility-example.ts" + ], + "localImportFiles": [], + "selector": "private-scrollbar-visibility-example", + "additionalComponents": [], + "primaryFile": "private-scrollbar-visibility-example.ts", + "importPath": "components/scrollbar" + }, "scrollbar-overview": { "packagePath": "components/scrollbar/scrollbar-overview", "title": "Scrollbar with options", @@ -7404,6 +7498,8 @@ return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-column-state': return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-copy-selected': +return import('@koobiq/docs-examples/components/ag-grid'); + case 'ag-grid-export': return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-external-filter-state': return import('@koobiq/docs-examples/components/ag-grid'); @@ -7420,6 +7516,12 @@ return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-row-actions': return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-row-dragging': +return import('@koobiq/docs-examples/components/ag-grid'); + case 'ag-grid-row-focus-state': +return import('@koobiq/docs-examples/components/ag-grid'); + case 'ag-grid-row-group': +return import('@koobiq/docs-examples/components/ag-grid'); + case 'ag-grid-row-selection-state': return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-skeleton-cell-renderer': return import('@koobiq/docs-examples/components/ag-grid'); @@ -8053,6 +8155,12 @@ return import('@koobiq/docs-examples/components/radio'); return import('@koobiq/docs-examples/components/radio'); case 'resizer-overview': return import('@koobiq/docs-examples/components/resizer'); + case 'private-scrollbar-scroll-to': +return import('@koobiq/docs-examples/components/scrollbar'); + case 'private-scrollbar-virtual-scroll': +return import('@koobiq/docs-examples/components/scrollbar'); + case 'private-scrollbar-visibility': +return import('@koobiq/docs-examples/components/scrollbar'); case 'scrollbar-overview': return import('@koobiq/docs-examples/components/scrollbar'); case 'scrollbar-scroll-to-top': From 7a9feddadc77d7d40751cc4216141357ffd0de30 Mon Sep 17 00:00:00 2001 From: Artem Belik Date: Thu, 30 Jul 2026 18:08:53 +0300 Subject: [PATCH 05/13] refactor: examples --- packages/components-dev/scrollbar/module.ts | 12 +++ .../scrollbar/examples.scrollbar.en.md | 24 +++++ .../scrollbar/examples.scrollbar.ru.md | 24 +++++ .../components/scrollbar/index.ts | 11 ++- ...e-scrollbar-disable-interaction-example.ts | 77 +++++++++++++++ .../private-scrollbar-native-example.ts | 96 +++++++++++++++++++ .../private-scrollbar-rtl-example.ts | 89 +++++++++++++++++ 7 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts create mode 100644 packages/docs-examples/components/scrollbar/private-scrollbar-native/private-scrollbar-native-example.ts create mode 100644 packages/docs-examples/components/scrollbar/private-scrollbar-rtl/private-scrollbar-rtl-example.ts diff --git a/packages/components-dev/scrollbar/module.ts b/packages/components-dev/scrollbar/module.ts index ea6ada0f4c..9f82fca399 100644 --- a/packages/components-dev/scrollbar/module.ts +++ b/packages/components-dev/scrollbar/module.ts @@ -1,5 +1,8 @@ import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; import { + PrivateScrollbarDisableInteractionExample, + PrivateScrollbarNativeExample, + PrivateScrollbarRtlExample, PrivateScrollbarScrollToExample, PrivateScrollbarVirtualScrollExample, PrivateScrollbarVisibilityExample, @@ -15,6 +18,9 @@ import { DevThemeToggle } from '../theme-toggle'; PrivateScrollbarVisibilityExample, PrivateScrollbarVirtualScrollExample, PrivateScrollbarScrollToExample, + PrivateScrollbarRtlExample, + PrivateScrollbarNativeExample, + PrivateScrollbarDisableInteractionExample, ScrollbarOverviewExample, ScrollbarScrollToTopExample, ScrollbarWithCustomConfigExample @@ -26,6 +32,12 @@ import { DevThemeToggle } from '../theme-toggle';

+ +
+ +
+ +

diff --git a/packages/components/scrollbar/examples.scrollbar.en.md b/packages/components/scrollbar/examples.scrollbar.en.md index 62299dbc02..764864a225 100644 --- a/packages/components/scrollbar/examples.scrollbar.en.md +++ b/packages/components/scrollbar/examples.scrollbar.en.md @@ -1,11 +1,35 @@ ## Scrollbar visibility +The `kbqScrollbarVisibility` input controls when the custom track/thumb are shown — `hover`, `always`, `scroll`, or `hidden`; scrolling itself always stays functional. + ## Virtual scroll +The `kbqScrollbarVirtualViewport` directive marks a nested `cdk-virtual-scroll-viewport` as the element `kbqScrollbar` should measure, listen to, and scroll — instead of its own host. + ## Programmatic scrollbar control +Via `exportAs="kbqScrollbar"`, the directive exposes `scrollTo`, `scrollToElement`, `scrollToTop`/`scrollToBottom`, `scrollStart`/`scrollEnd`, and the `isTopReached`/`isBottomReached`/`isStartReached`/`isEndReached` signals. + + +## RTL support + +The scrollbar tracks ancestor direction (e.g. `Directionality`/`dir`) and adjusts drag, `scrollStart`/`scrollEnd`, and edge-reached state accordingly. + + + +## Native fallback + +Setting `native: true` via `kbqScrollbarConfigProvider` makes the directive fall back to the browser's native scrollbar entirely, bypassing the custom track/thumb — the same behavior applied automatically on coarse-pointer (touch) devices. + + + +## Disabling interaction + +The `kbqScrollbarDisableInteraction` input keeps scrolling functional while disabling drag-on-thumb and click-on-track. + + diff --git a/packages/components/scrollbar/examples.scrollbar.ru.md b/packages/components/scrollbar/examples.scrollbar.ru.md index 5c057d35aa..582925df94 100644 --- a/packages/components/scrollbar/examples.scrollbar.ru.md +++ b/packages/components/scrollbar/examples.scrollbar.ru.md @@ -1,11 +1,35 @@ ## Отображение скроллбара +Входной параметр `kbqScrollbarVisibility` управляет тем, когда показываются кастомные трек и ползунок — `hover`, `always`, `scroll` или `hidden`; скролл при этом всегда остаётся рабочим. + ## Виртуальный скролл +Директива `kbqScrollbarVirtualViewport` помечает вложенный `cdk-virtual-scroll-viewport` как элемент, который `kbqScrollbar` должен измерять, слушать и скроллить вместо собственного хоста. + ## Программное управление скроллом +Через `exportAs="kbqScrollbar"` директива открывает методы `scrollTo`, `scrollToElement`, `scrollToTop`/`scrollToBottom`, `scrollStart`/`scrollEnd`, а также сигналы `isTopReached`/`isBottomReached`/`isStartReached`/`isEndReached`. + + +## Поддержка RTL + +Скроллбар учитывает направление, заданное родительским элементом (например, через `Directionality`/`Dir`), и соответствующим образом изменяет перетаскивание, `scrollStart`/`scrollEnd` и состояние достижения краёв. + + + +## Нативный скроллбар + +Значение `native: true`, переданное через `kbqScrollbarConfigProvider`, полностью переключает директиву на нативный скроллбар браузера вместо пользовательского трека/ползунка — так же, как это происходит автоматически на устройствах с сенсорным вводом. + + + +## Отключение взаимодействия + +Входной параметр `kbqScrollbarDisableInteraction` оставляет скролл рабочим, но отключает перетаскивание ползунка и клик по треку. + + diff --git a/packages/docs-examples/components/scrollbar/index.ts b/packages/docs-examples/components/scrollbar/index.ts index 66be5e0f2b..32276b3442 100644 --- a/packages/docs-examples/components/scrollbar/index.ts +++ b/packages/docs-examples/components/scrollbar/index.ts @@ -1,5 +1,8 @@ import { NgModule } from '@angular/core'; import { KBQ_SCROLLBAR_OPTIONS_DEFAULT_CONFIG_PROVIDER } from '@koobiq/components/scrollbar'; +import { PrivateScrollbarDisableInteractionExample } from './private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example'; +import { PrivateScrollbarNativeExample } from './private-scrollbar-native/private-scrollbar-native-example'; +import { PrivateScrollbarRtlExample } from './private-scrollbar-rtl/private-scrollbar-rtl-example'; import { PrivateScrollbarScrollToExample } from './private-scrollbar-scroll-to/private-scrollbar-scroll-to-example'; import { PrivateScrollbarVirtualScrollExample } from './private-scrollbar-virtual-scroll/private-scrollbar-virtual-scroll-example'; import { PrivateScrollbarVisibilityExample } from './private-scrollbar-visibility/private-scrollbar-visibility-example'; @@ -8,6 +11,9 @@ import { ScrollbarScrollToTopExample } from './scrollbar-scroll-to-top/scrollbar import { ScrollbarWithCustomConfigExample } from './scrollbar-with-custom-config/scrollbar-with-custom-config-example'; export { + PrivateScrollbarDisableInteractionExample, + PrivateScrollbarNativeExample, + PrivateScrollbarRtlExample, PrivateScrollbarScrollToExample, PrivateScrollbarVirtualScrollExample, PrivateScrollbarVisibilityExample, @@ -22,7 +28,10 @@ const EXAMPLES = [ ScrollbarWithCustomConfigExample, PrivateScrollbarVisibilityExample, PrivateScrollbarVirtualScrollExample, - PrivateScrollbarScrollToExample + PrivateScrollbarScrollToExample, + PrivateScrollbarRtlExample, + PrivateScrollbarNativeExample, + PrivateScrollbarDisableInteractionExample ]; @NgModule({ diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts new file mode 100644 index 0000000000..49df866bd2 --- /dev/null +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts @@ -0,0 +1,77 @@ +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { KbqScrollbar } from '@koobiq/components/scrollbar/private'; +import { KbqToggleModule } from '@koobiq/components/toggle'; + +/** + * @title Scrollbar disable interaction + */ +@Component({ + selector: 'private-scrollbar-disable-interaction-example', + imports: [KbqScrollbar, KbqToggleModule, FormsModule], + template: ` + Disable interaction + +
+

+ Drag & click for the scrollbar are {{ disableInteraction() ? 'disabled' : 'enabled' }} +

+

+ In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists + of an attacker submitting many possible keys or passwords with the hope of eventually guessing + correctly. This strategy can theoretically be used to break any form of encryption that is not + information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of + successfully guessing the key is negligible. +

+

+ When cracking passwords, this method is very fast when used to check all short passwords, but for longer + passwords other methods such as the dictionary attack are used because a brute-force search takes too + long. Longer passwords, passphrases and keys have more possible values, making them exponentially more + difficult to crack than shorter ones due to the diversity of characters.[2] +

+

+ Brute-force attacks can be made less effective by implementing key stretching techniques making it more + difficult for an attacker to recognize when the code has been cracked or by making the attacker do more + work to test each guess. One of the measures of the strength of an encryption system is how long it + would theoretically take an attacker to mount a successful brute-force attack against it.[3] +

+
+ `, + styles: ` + :host { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--kbq-size-l); + overflow: hidden; + padding: var(--kbq-size-l); + } + + .example-scrollbar { + overflow: auto; + resize: both; + height: 200px; + min-height: 200px; + max-height: 400px; + width: 100%; + min-width: 200px; + max-width: 100%; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + margin: var(--kbq-size-l); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PrivateScrollbarDisableInteractionExample { + protected readonly disableInteraction = model(true); +} diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-native/private-scrollbar-native-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-native/private-scrollbar-native-example.ts new file mode 100644 index 0000000000..b83e8e6b0d --- /dev/null +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-native/private-scrollbar-native-example.ts @@ -0,0 +1,96 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { KbqButtonModule } from '@koobiq/components/button'; +import { KbqScrollbar, kbqScrollbarConfigProvider } from '@koobiq/components/scrollbar/private'; + +/** + * @title Scrollbar native fallback + */ +@Component({ + selector: 'private-scrollbar-native-example', + imports: [KbqScrollbar, KbqButtonModule], + template: ` +
+ + + + +
+ +
+

Native fallback scrollbar

+

+ In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists + of an attacker submitting many possible keys or passwords with the hope of eventually guessing + correctly. This strategy can theoretically be used to break any form of encryption that is not + information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of + successfully guessing the key is negligible. +

+

+ When cracking passwords, this method is very fast when used to check all short passwords, but for longer + passwords other methods such as the dictionary attack are used because a brute-force search takes too + long. Longer passwords, passphrases and keys have more possible values, making them exponentially more + difficult to crack than shorter ones due to the diversity of characters.[2] +

+

+ When cracking passwords, this method is very fast when used to check all short passwords, but for longer + passwords other methods such as the dictionary attack are used because a brute-force search takes too + long. Longer passwords, passphrases and keys have more possible values, making them exponentially more + difficult to crack than shorter ones due to the diversity of characters.[2] +

+
+ `, + styles: ` + :host { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--kbq-size-l); + overflow: hidden; + padding: var(--kbq-size-l); + } + + .example-buttons { + display: flex; + flex-wrap: wrap; + gap: var(--kbq-size-s); + } + + .example-scrollbar { + overflow: auto; + resize: both; + height: 200px; + min-height: 200px; + max-height: 400px; + width: 100%; + min-width: 200px; + max-width: 100%; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + margin: var(--kbq-size-l); + width: 150%; + } + `, + providers: [kbqScrollbarConfigProvider({ native: true })], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PrivateScrollbarNativeExample { + protected scrollbarChange(event: any): void { + console.log('scrollbarChange', event); + } +} diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-rtl/private-scrollbar-rtl-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-rtl/private-scrollbar-rtl-example.ts new file mode 100644 index 0000000000..f568a32f14 --- /dev/null +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-rtl/private-scrollbar-rtl-example.ts @@ -0,0 +1,89 @@ +import { Dir } from '@angular/cdk/bidi'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { KbqButtonModule } from '@koobiq/components/button'; +import { KbqScrollbar } from '@koobiq/components/scrollbar/private'; + +/** + * @title Scrollbar RTL + */ +@Component({ + selector: 'private-scrollbar-rtl-example', + imports: [KbqScrollbar, KbqButtonModule, Dir], + template: ` +
+ + + + +
+ +
+
+

+ In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that + consists of an attacker submitting many possible keys or passwords with the hope of eventually + guessing correctly. This strategy can theoretically be used to break any form of encryption that is + not information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of + successfully guessing the key is negligible. +

+

+ When cracking passwords, this method is very fast when used to check all short passwords, but for + longer passwords other methods such as the dictionary attack are used because a brute-force search + takes too long. Longer passwords, passphrases and keys have more possible values, making them + exponentially more difficult to crack than shorter ones due to the diversity of characters.[2] +

+

+ In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that + consists of an attacker submitting many possible keys or passwords with the hope of eventually + guessing correctly. This strategy can theoretically be used to break any form of encryption that is + not information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of + successfully guessing the key is negligible. +

+
+
+ `, + styles: ` + :host { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--kbq-size-l); + overflow: hidden; + padding: var(--kbq-size-l); + } + + .example-buttons { + display: flex; + flex-wrap: wrap; + gap: var(--kbq-size-s); + } + + .example-scrollbar { + overflow: auto; + resize: both; + height: 200px; + min-height: 200px; + max-height: 400px; + width: 100%; + min-width: 200px; + max-width: 100%; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + margin: var(--kbq-size-l); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PrivateScrollbarRtlExample {} From 0f895ea096d7537760ef4e3502c345665dcc055e Mon Sep 17 00:00:00 2001 From: Artem Belik Date: Thu, 30 Jul 2026 18:29:55 +0300 Subject: [PATCH 06/13] refactor: e2e --- .../private/__screenshots__/01-dark.png | Bin 77129 -> 9253 bytes .../private/__screenshots__/01-light.png | Bin 74388 -> 8539 bytes packages/e2e/routes.ts | 4 +++- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/components/scrollbar/private/__screenshots__/01-dark.png b/packages/components/scrollbar/private/__screenshots__/01-dark.png index b6f51bf31e1f0bbcf69d6b032f2cbfef90dd3208..1a318c2531c8c056c81580623d07264aa637040d 100644 GIT binary patch literal 9253 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

{{ content }}

W*Z^hpN26xw`oy&^+j|?yt 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 GLK6V%n9iR7 diff --git a/packages/e2e/routes.ts b/packages/e2e/routes.ts index 9c9e68cbc5..18247fa6fd 100644 --- a/packages/e2e/routes.ts +++ b/packages/e2e/routes.ts @@ -70,6 +70,7 @@ import { E2ePopoverPositioning, E2ePopoverStates } from '../components/popover/e import { E2eProgressBarStateAndStyle } from '../components/progress-bar/e2e'; import { E2eProgressSpinnerStates } from '../components/progress-spinner/e2e'; import { E2eRadioStateAndStyle } from '../components/radio/e2e'; +import { E2eScrollbarStateAndStyle } from '../components/scrollbar/e2e'; import { E2eSearchExpandableStates } from '../components/search-expandable/e2e'; import { E2eMultilineSelectStates, @@ -257,7 +258,8 @@ const components = [ E2eOverflowItemsHorizontal, E2eOverflowItemsVertical, E2eOverflowItemsOrdered, - E2eOverflowItemsDynamic + E2eOverflowItemsDynamic, + E2eScrollbarStateAndStyle ]; export const e2eRoutes: Routes = components.map((component) => { From b1fd2c0166660aabef87c11f519c852512eed1af Mon Sep 17 00:00:00 2001 From: artembelik <24925224+artembelik@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:40:35 +0000 Subject: [PATCH 07/13] test: updated e2e snapshots --- .../icon/__screenshots__/02-light.png | Bin 101960 -> 101966 bytes .../private/__screenshots__/01-dark.png | Bin 9253 -> 7944 bytes .../private/__screenshots__/01-light.png | Bin 8539 -> 7343 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/packages/components/icon/__screenshots__/02-light.png b/packages/components/icon/__screenshots__/02-light.png index ee5afbb1ff30b2cee01fdd743be1d50a652c455f..360868b8c68ba5e2f7e8bcf79d992615934adf7a 100644 GIT binary patch delta 28714 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 G;lBVpccxGP delta 28707 zcmZ7dcQ{;+7d;A(NFpL4i9~dtL_+lFo#;W-Xdy_5-s|Xx2%-#0^xnJC>nJ07bVg@J zXD~*a(V6S}yYGFU``q*Id7e}D*?X_G*V<<|?QUP%-N0I+Fh)SQ>fxNQ%s^9fMjO*2gDs_J(8fAkf@BweQ-9YHQcE4VI{w$hGXzS|Y^8A{6mo z%W`2BsZZ*vt4M&9N#nbA7F@!}h*H$|nukAo*F8xo_#Q%|$|c)rtVL)ZQw0__bFHB9 z(-nN)x*q*!51@eF=JGNX6J59r3n&ZERucc5+Y$kD^l@|kC@XB}X!}{mmE2)%8%g~M zi2ar_L09E(d#oBIlx}k+7nr74kR4wX* z(h!C2bphk;wcTgWDPI(2Uc2wN&lmifPkB3Nw&YMSD?-2Az%jb*Zn4f6f8Sv^o9*TA z_J-K!jJw#t!9!w}h9NG!l8M{xE)tRp9dcJ^5{yBL&a|ksF;QHP@Q&?^i2iWsn&Cs9%Re5LUET_(26`glcQ(+5`?s<$~ zO};*P5O@*Ro+`NvQJ7OxTDk|HPeY9w=4qFVE`Q~<4%Bm3?ZQ$^ml2N(AF>Vssujwr z1MH;lA`iS4vOr9R7T=#1*rfGf#|3D2CtFr7JNSTa%b-y)>kft|Yty2fqo=o1lA~$7 zcIZABm_P%}9C^ou%&ELmtEj__Xabmb95}>UO;{=t^e3~{MJv&QJL_94$F9B1i3||I z*Y&oLW5bZL7TJQyl*L_Uaw5A?SxG~gojnejkF<8qDXr!G<+=%Ry0KKwry$THkg~jt z4m(g{CtwRT%pKcuX1En-?%YH#m8ntMC@HaU>$2s_uQk;KPJ9QD+(R4~Pr8}^Q_GQw7~5?R#@w~%ku43Wqm)}=ER--19Q34+g% zmM_4aDtj=|G?=KR)22bnr};Xr-oT{WyOwUWWN>`wNa@Mn(d|brMD8E-g#Nt5yFJnX z|1MP4FDOvevL{h{M*~qCb)f|X4%i$I7l;3v7eEO$xm0VeLWS27dp_5E9jZ5}UFV!B@8)e)X?7Hm4y%`uTbE}7yEZX&I(gH?x`UQ9xvP-TGTo%&2N{F+180k z9%Z8TJv-s9ArU=8)$Fh7RF0DB&FP7b0{vTQowT0^xshu)3qF9;MYfXWID!JF#_cB@ z{Pq7M&-`*LFhOpK9JC~x(&olzcL7*kB3`Xc(XBd2E*DK2Li98U%sqxql%Z1MI9fhkGGx2eu{dUM>bXx-G|irUxT@FApR}?01~qrePEyyDK>m2im^nas5AeFh7&Edt z4l>RkAGLKX;9!uX4ASSA^zfhGwh44VONNY01<`dndG0lOTqUOHMHxl4j!oZ0_2aC* znJTUOopE>Phaj#W?Q|eV>RqY*DPi#Dx8j^;lRmXvhkTVnz0%p{H`#%E%r1Mc4zhx7 zx*hbD{zTCSW@kgZTBnl~Fd}g&1Zh>S=?r8(zKQp(om{LUquRKzL4VaL70CLmEEr#Ho3GLty2yx?HH;jd@}`?l`rjj4 zQ=TVCrpQOy*&I6lsd4_W=tu=4en)nbJ%MzJ7Y|z;!eOG*=1%G5iuqc_$tm~2!bIdv zl9KXWW9W1masY1gNS)dC_peQsC!=5HUu{nrG5Iq8FZ1s0@IM@18hd5fYHXZq>9Z;@ zSXc~8%~qmFspT&gCs7s*T>ZcA;Y;4K`x6H7Kt|BUPmM~v9o2a9WJxV8ya!cXLhB>f zv+2gp!Etm%X49BHu~%^X)Yqjf51FcZqRLG8MNzrjfsOc+Y#I&YF? z9mJs_`ITz#vU97%4->|h@^c_4C`cHz@K#J`3HfbeXmD)H<9>)?DPrtAeE$m*lM}^? zUxcl0la7q^)%+fCj)~;4x%2M;01Acer+QJ`j9#pnBK%v$1oJ||#pbj+>d7t8k`E>V zfRSFsu&mNrMkz-ZHfye|hyJ3=nsg=wfod5zeEFO>w>OR&d5v!wv2GVgEQAELnQ`&0>^55<&Q!j*>w{Jd$ zYUjhrC}n{wedt(UXxt+)f`@ARDsat5ER(<1XC)6 zT+sB)I!fLm;_1_PLcP@cW7LNm$swm89G3otkbm)hZ*wLcUgd~+WmaJ^9Zn3sc3odb*eag$Z(yFg@al z5rS$%Af$jW`DW6#E`j^z`r$=%S!!85bfByugO3(6Nt*TLQ?i90ZQR-GLWi+WHde9R zuN+>z8Fe~a5#@m&lxyb$*)ENN8V|10+-A3**7u|$36^#(TU8d8>!1G(4iEn$CnFD! z+?{&;G7WLs-}^^n{qs<)zAhf~ICYeNsuPKH55cL@JbR`MV>=#MWgXr>`=CC}@gll> z7N_lL+22GqIVng)6kyEE90i1i-u8G%kq?;k_esS5VUHc~?do|2;NjuD4z-tt#o7## zo`3W*e0{udKO?hwe7>uwRE#m_tCGCJFV|vpK<|3<8gja(a7~V4&?#$O_5!A1km-a6 zaV?E|`CX83l;>2Fp}DYzr7l$+g~yJql7F#w5!yW~Q$(PW(#DR-uO*;Ej$-uv{J_U9 zLH8=ffx8c{E;0>){5JDbsBBRCOt+zVYgxKv=JDJmnA_@G3U@?AR1#sX>_x+?tLp`^Qh2*9?@GtOKoG!rs0)28GQHH z3LFR>b@Z{JL#$&u`EXs<#<*M3XC4?e=-ll$SLi>Y$ntQ$5hRfAZzh^|0}6? z2wkeKH6ka^(CjJM3QIA*=@61L%IWr17ynm`-4=ZD5%=y$JXFebt83Scg%)NP+XMq{ z<5`Jmi_g#h%a`R9dYM2km znZ%b$U-YjWFIJuFfItF~&;v{HCCy?%96<`ji0-7%a`-qrWRiM#&uWe}%wSmpMfYe`t!1 zvu67r;mzwdD?rwd-Z;$8YwwUES+m|wClRtEb$D3jz^+_coEfmqo2|J&Tpcr#u5n?$ zw+N1DNf#l3z95{1RhXIQK-?I;t?hf_F=|F_g+T`)xB*GC=BlGbVR_{Bb(@`(kMNty z`k!6MA&34oCGOCI&^NZ<<*=395QAGcwZFFD-o%nfqlB02*q1s}qPjWe;ay)f-nU>L& zo1L3?erZVAA3bnt61E7$C4DI>6=s<1p^q8Zb)z(f*e@Ooz$)6f*jlG%jtQz#-RcWc7^=v5Y9%BZ<4%x?`%w9UNZ(lmkrZVq?0(%4o#nC^e-p=JV z8>Ny55rGunfQUiX4nn$Mgb$gVkqkr304eD{3pemjY2`&lNw#>gg^x)^>!x|n^HS4p zwQYT$k$~kbO4i|`xc(FA&vK?;zrJYo?MB>3jh#fjh0bzwm6FT*;^|ObQaU;+;fTuI zXu%{KDRvDPQUpX4+U97095U;DWrxmxo+;|Wr!c#4Q@Rh!t7x4N*D_I)$WT%ix5uP+ zZUdk>+W*1f=bzo|WOXS`k*?t9v*&Sp-BFkVrKrLFf78}=dEM3`YPp)QSlhZ`_Yp`u zifVDP)M`PPPX9z5^Y1!2QGb6P>+i&E!fW3}%v3R}Y5cKsI8g;ktEkQr8(XiZo1aDS z@l~ZY`a7id&802A@D?bG6k|Z`0q_Pt6|yDQ@Its6-2yp~3*ICgbf}*_ft%<*BDcgB z+(hocmwtiiBD$bMvHJ1Ul><%0-O=MmUc{iIq#6eWr?X zu9k4RNWiuPBw(-AuILlS|F!sDq3JOq9H;te9(*ktfJx_yYQ5oq)9j@JwCO0#A#LG# ziJ2)Q+S&P$X{(8!^D{C{d3r|(gcClOvY$Mkfu|dBsCsoV5e+$H5V9u39Yx%M#K@l?=PyPNu`?D<2gByOAr1yy$ zo0oKcpSFKo!SQ8VDi^Y(yIcMSm8%iG>S^=%3!<85VqteYc4+^q7ZzCfHLOx}t^%{i zl>6R(Qi)hWD83x}t=NmMic(!kl9(LCULZMQQW- z)5p_q|0|A3Ok_=;X*|0KRkfM8TC9qtTC`p-27%sMq|{cEIw0v;#;0hsJ&Xs&JweV- z|4+G>eHC{@u(l?DZNZbI7$LTA6ndS+6mdp^RV;3h-15K}r-OrAOh}ru3i`5xvY2a%dr~0{sn`u-kRj#GOcAIz ziF44rDtEC~3o$YGYM<{M+K{ptGTS3WXF7--7vNC>$5UIoa1F})UZv7kuGmX3%3yyu zhx2plum9hK4(Q}_>{z;S>VZ$TXM?98i15Np0KW&=mR8%W5$2gL5`o50btTWO1-Pk` z=QW+12@a2M@`Uoi?5_SbgM;ZOtgc;+Pwj}50xX6mg2-S7n z`}rys>*G{D2WQ1da^?UY|GnHzt0AMKGsW}srCeYHOGkJGO=j{Rxi@}V?9~(Tkq9(; z53!+8mu@F8wR8_;$aM3jpu{gNMtt@B75=g#?QJBLvoqpvMq{pGdr-|MS9Y*lbiQE7 z11mya)d__(!3K;`+wpZfik!&_DNFl|nA*sbN&j$zKi{XV#=>)Q{#a#Z@_U`mC}k$` z0$=(hYfSxXT3R{=2Hd)R$?kXrd)>Kv>TrtbTv}>hSs72n2ZV8y&3-o zII=hfQ^jlaM?_Y8C~tb!^Yqj0oxM<$X!&ARvwFIe)y?a=Qie|f=R|bAgeJn062VBx zG_zvygWa_B=W9#Zk(p4Z>_W3;x*8}T@eBka|5S!!p(nH90o6YHUzS4qF%SDNq>>1m z1ijwDr~A8@v#1`FhLmR}@A35s96@fr*UJc-r}oX11pZ-XQU}&iLhg@zOTj0`4XNDK zUP-IH$Y#e~lCPoJP*v56s&p^J)^Y>+*>&oO8nI0W&S=VEeq)3Yuy2tEUMBoo>(xlh z-nqEAh^Fd@DEYGL&&21TuS(vmuZcQ$*M9;p{APs~wWFmZG~V z+ALC2SOnPx?TEeNf6xpVivOY>v-Qvww1p@lDy&{*Isp}{x)IwIZD&nIjZs%_=%oPg z!{%A{JMo5BLnm<4eS>NrPbQ~*EG*)WQeC(q(U+ZzJJg8GhPjh{#q)S&gCE?n!!$+#wHt-x z-MgJuNY%+!%@%+&H8OXC#k%X8!xWSyeuOeFr_itBU0U#XrKh?05f@}Gl<*X$`ZfD6 z&SI!vD!pZEpe^4sH=i1pFpV67F6VG0i+1e~)^}6d?fj3uept*hABK_M0&(9{8@j>0 zsAVE2ZNi3fq1{&cQ%GxXOe?D9Ii%Iil}t@Pt4bI!miez}w;R%f+SKNuG(vO5yq>SZ z5)mYQ{4l45CSEqzb%*zB6bgsa<_RY+#KtO|UJ`+vYgz980R;+e@(H5G%W_Gg8AZH# znF+L8PoJcZg4Zg8{9hXS;+|TJbx+tQ|50xW>FNzr*-mG)P@cUpV<)x z(6Rc;aU(6`qs|e4mO#%}q#^Iss$yQJD5tMkI*G}=ArUNHVCMQ!>*RiK?nEQ-5xBJ{ z6}l)`x+7FF9eY8J9fkLnf3rkPpY~=Xp95XF0zJ_Yn6!9*kC9B0DWI+n2Af-41o!Z3 zJOY7~DwF)tcCo1xAZv^oDL?#<>i zP_9^PNWjMsT}x47nUpcvACUE!5Q>;e$$#%*c)FxVvWFv#k_lX|OMnSu4pYs0!rTlD zyE192J}wvi7LfssMfPS4x}O}`h(Ok);woaZ5oGa-a+rMWQemfyNotQ1)^hguD1XtX+q>-k%4u8q?z zb3a~Y#!`!2C9}3j0O{%ZMDLU~#kdCHXV}6zQ+rNsA$ZvE5OjEGNZN(Z0-3mY`_7%q zMK=vKFXsnD*3+CMC~3?b`1;n7H52rOx9!SIkC_v#2jW>o!~j~geKUqmGHvF4auaw` zOm2cf$bLDZiPCpU(1}~R;p_6H1xdfkG1N%BWiNtP9TTMm_yqLk+$jYc>y3#WOHpPp z>eYKpwpE9gvRPQX{$P>nJkx^Dl%Nm4JANbM&(zO)lAvrM8}qx(HExHq(!M7V48BgS zL2cF(SF1c1Bc^>{2vO17mwmzDgT`Y4yBl(Q`+*~aL}1%J(7Sn?`F-(SvmU}w!0Pto zrR6jyJNqYK|M-xct|8!Zc?FAvJqh&eKZNSK{1f!d$*TN@m@-z0hb@b_Z}d0;1ss(C z)nscoRsVtu zQW0%k=4t%NM6;t-_BqMz@W&p+?@GUxelLA`oEHojraSIX0N=iuSP(F?%s!ukqx-?v z=hHNDOdC3hKSz~%V{+~kcX11<2-+xlOoKpQZf^QeN@7dYF2^xhXT7SYAKmM*O$cu7CBi-QmvFZ!6{Nu6`PIxk5=54I**jC6Q%qq zbFNY^Xa%Yl;@k@PYTO3GyQE*itQLcK>)pqva5hAZ2QKzb4#UUAdM3q{2mTx6eXB4) zNzne?^+jfwMt)uuBE2)1RjqMaptvD^K~wBYuh;6&$s64R0)KdY(Sh;q@|>KU+3XkKCq^wSR?#RV7mYqPsa3r%?{F#@G(Wid>58| zQ~k2!iT7!E+6*BrEv0d zKKr>NvP_1l_b-0(ffoxDI2suHw33I(srTE<^a~&{c2bv)*R>$`_kN6&y(JSVCEK(b zU}26$i>W{f7T5sr!-aW>_SfFoPTy~HtC_9QU+uk2j9gFVm^}2Siv&JQ@6gxh1$N2^ z+;~UJd8YkI`2N(qJ0=;;=nE$)H|^9Tr?!i&-F&9e@VM0rd0FxAiFAtpw=_+oU+u`? zTEh$SJYF`s)O>3zkdq&Ti;aO?ZKtN&W76H~0`Jai022M~zVBGknHbxf$d}IgRc2c98Gfr}>NS$&ylMR0&*jn~xXeI9SKGQM?c{)L{}J!A z8ihNrYZ)8e-ZysD|I$pU#>dZ=&bEvE=Q3I;B5iTqO&rFpa-?47?-AsRKWqxVe6P94 zD<|uD1!(>!dA1pp_L$OqQ2OP`3@Z0SQ`lmAGl?Q1#6wLp{!B@qYfeU(n{?+%7F*vN zZC(ewv`G`Y?Q*+Zq{&%O@#&MZu=h{xYrJ*0DI!e{4v-?bnP+dTBQVAso|Rg{$T<0A z2ed)?QakKSBaVAP#7RDQ#wht5x$-=LvCo2V2<($lJW=5%v({4hSH*BtU@~Z2^=;}u zctQqMAYG=KAj)w#Uwv&IruO6bK|S4h`gu!_QRN{|J~hJ6m1F#i%v5u48C>6 z2%MTU_iI!%C;d+SlQ6XDCb1yz4sL3h8y*y;Kj(3KW3RD+kx9neqHvTVV=fZ$a->=7)DJit$w{W{RW691mlkj6}qVO5J>1fEOS^b+a{nyku z3zHAA&9-A$C_`-rOdTOVM0CF^sqi;brnEkFuNR7 zLl-Z^FQW^k3F{4USMv`U&W{>mSSbXe5DDj>_NzM|9Ejs zh`sFmvlW~qblfEE--r(5tPy@6$F+F77M>RMKSwZ>o#B( z<`hc7>ug3ohM+kf^Q%f_1)!?!SmR;Xh=el6O1+E7N+NH0 zul-*#gZH?v1EgY^XjZqr5)3TLT5{~%S!efAmDAH?G#{d)K_Kcacb|L}u8Imt!*^b?3&=` z8=Iv~AvlfIn(tlC{K7Yygy+fMkE(y(UZsbO3!~=$xPBSSC7J5M)6MY9d&e`VM{Suo z^v}Do^fbNWFprmL7~MYwqk_m6Cya79VU3$R7QV+!#*MBrN6qZb?g9|frB$XhiK;z3 z`)92|bkk9J_;Ftc;M-dn()uT(Q}K8KcHFW(v8cq);vOSxL$1DxmF!J|$eUZCX8#{M zpMp+ow2XEpcN#al8pWKcx}rGfbFffMBhGhNt-ONZKxHh1A;nCe`p>WB*VOcKN&LqJ z_hMHPl3E${{|)4N8u=T7#BS3TZYLK_QWqSaeWv}_ zUt(Kp$T0t_`BQE5Sr9MyHpw|Fes|X30NpoF;&Dg?j={b>w%r2h4?2HJ4{lZrjc8ps z4K)VJ7TAPOVe+4u#M3j9-8K>2 zd8QF)de4t$8q%hjmM6#XZw1PRU$Ow5#9_tr=^crh99PFXz?l~Io&FaX7aWDc;`vIz_!pM zg$JqEFcsXvHn~#2%D6g4rPSz=fc1GPqToUG`7&ypxfF%^c4%MoPsyD|2#jSu;37o| z$Ug-}U$-+icQJpxTqEgu)h!Qx_*mDfFhiutYIBN9so(lto4s=4sYV7Cfy>X+VkgJ9 zy*|66t@hpKDeYV9Lv4i6-k+r3;8*q{fO@j)Qv15gL3oc`tm%B@OXQQDm_0kjR~Hm1 zQVv}i)5i&G=>q-&@4U~@8BaHwu%}fNF*c*@QeQ?J!-J1G#a@m`!cKH%vv!tY{)Kn4 zkY97`TT!PKRk^(N<4NWQ?XT$>V{#{dr(KR04F~BtCR++M&~DeJzRhyFC(F?A-7_@ur{HVj$s_ z)kTj#|3&$e3=DAm+-)>+b+MVSL{cg>Ah5My^xq?3LD*-lw5IUF{ZPm_b{Aql_b~=?BLhVxSI~uV z6z;&!$8oGIQh8&x`T>_`(~FmyzJ+%Cr{=4RNf$30{*^hOh1pq`sShvmt9;qttcCuy>% zCi8Rw5o;xJ@dMe66q^$h{rJtnRK%t)H`01S?Wex`N)|6gG$a437im*%PZ>U4^iRBq zsaS}&vEaZ~PsDDj=Nou2NCF=<$2;^?|6o7rIZCs;qRYJ_%BJu zaDHyGU1s=jujIp-M`!kiqT2qJR3*vLQ;UD)lSfkXsfH6>+2J{1kS}vKcHG#rc};?; zZ_=#qaeBTJGt#)MZ8IeibrwL<-2$68@U;i?0GM>3z=Wl9uGC)$l-Nn~I_PTGaXraL($VJbB`H z%s!t(YuUl4L#)qyzsm}aBdMRuIfnFKOvqDNO&YnC8Q2ZdtM44MR;)_ zjl>jgSW2o!uA>+)W=h7BNs9LYLF!_%!TMCi+}Gz+O6o(yxVQHmTrCE5AzU?}(R}UY z)5Br^l!2aprot&FF}u`T%xaL;9ae1!>Jcx-lDGs9BAt>?i2@aC~fl8saDsev$zl9nW~88%{rT3b+|WMe34Yh+N;XN`S^@gzM+=EVpZa0??_V1=1hNXb@fM* z;Y}0+Evk#{04;T$tmK(K8&|?9zCW8ooH>@V@kN(2`~QbOxPAQF-o^o6T{+U1JuLo4+2DO(HzZQ-mUWJnO5QWq{Lrg2>gM#wsE5@v z9!+olD^V2@{Ug)w8cC}It+5y70tt)Nyp4nI)QdR$G{IxeZ!v0gPomw^1{cP$T7SFZ zpC;%5u|}N7IH{W%T8^n~cssRCe+i!Laxuv`e}75)1t1Js7*tN6Cwa(-@RXnJ+s zU-z$9Bc{!&OO;28lB)mPg3`74!T3T$g)s-g6#@R3;Z++RimRPE2khW^yvJkatRG19 z#a(e-_$`9^1NzaMrHEzbf0!k;J8E`q?-Z1Dx~WH6+zT5F9#4+DPt zV}&=10gCqS$9{8{4GCNBVm^9{DFnWGS(C!)y|bhC4uw5^Jr&b{-QCOvV0{NsyoDz$ z5GbxZ(X+k>>vyCB8c;^sJnv>sreDCq9xgaoSr_ye6>B?PdM5I2M~7pdFz8<75v4cT z?C{*k2@QvC^MJ(PE_+E<9svjp2wy%}s^X`T8Wwokt~kdu4<`&$(a! zCPX8E%nZKYZ|fEdw|UH_E`ek0ZSE4+J>paT!S=v=5%$&hQX(RNq99dGDjVfwvy_CQ zCKIDFPOj!DyV=Rq6I;WS1ih4tj0DRKawlUpvWeTz^W;foj&-dJ=%-GA;ZAxA{?6h5 zw)ZYxNVo|NM+pt?U1sJxF`4T7+qAk=SG!J?`2l@$C#wG>Fk7sw68tGtMLE~K-2-Jo z=)1S`P1$8Cu1*&LZMge+?YpmDN9Vfi@e$WKF$7QTIYMyERLX+%+V3#EfPD&8J%kLSQo7J@zK&a# zwvTmwaBtU}J(GUC!t<>uuc#|t6*wgdT%^pX1LEdmHFgYyqamw#%wJF!s@sk*M zc&VFg68mg7ojCLkS@69)r60YPAG?NPD5R-)%9tcLXVUnM>}m}W zJYLCq%&1SA^YglOHihy-1pntYf`XT?w;ZiK%;skdH`#652W~kVl}h^43JmJ~jCJ94 z5#S@c6CW3{1vFj5Qo0QxQJ5VwRj=j4JXL5$6^C8S!p~LEmnamCkNe(40i;gRDDGyY z&YBz;9_BY`ay`q*YA5R1VVga?LFiG63c?jr!Empu>yZJOin3bFW4eZsG>)p zdhlbWe`q-QnBd&4w&JpuH2sz{Ej8XO0M3YPrY-Z6_;%0qz3Rvp^54nvonb?%Plb9; zqXV5rup9~864wZsVt#4K-dM8%x7dr1A%yI|K_Bd2YS|1A2*uI{3qRw{Q*KF4f}K%s z;1tLJ?c)Ll>jpX)Y_)*bSRF=PKrbmtuNL~gF1Dd?=I4Ln{Q7QozJj*nX6hNyVl=5= zZ1;uq$`Nak*@)q>Ce#poQug^lp=GNmA#!M5gTl6Nm8FS`kpljPbKk1J ze&-hFRi>968$;A#6<6^-pFmj7yK>QNPph?gE1pH}a6P5c)01W0E^FDLnT61_7tF33 zbBsQHI`+C4%GoVWtQ}EaW^zFF<(nYL`caAR35I)usl33lu49?2J@8;&{F!g2mpvM= zvPd&G};J00xH z$c5_0gn6bX<1-io-ZwrdnKBupP`EaV)5J0g?3-8>K9n-+i%Ut&6!n$i2z3t7`RL`- zNK=8^))Bn^6mab^^+>z>x{8+8mx2PIk=0Q0d2_`zQ7dh#q2R>sGB7Q?XOSAdwWKEF zvwBRdy?u;x&adQxq$-i&e(4>C)za-Q{KP;#UDsX@#jSmgt>e< zuOHlc?!t+Z{{(v@$u3Jx9_@`gfER8oAuAK_EDgUB9854wIVx_S`L@Kfj6>2kV&IY9(Z;{`Fv{^P!Y$jkJK@F%Lh!FvEx7cPF6van?1%c|Na za^W*%liKc0eDRBCI_$4+Rt!0_o9_Fwzhx9}b-6g%FJ`y1vfR*tcWkuS^Yb#au)ViJ zXFj-tDW+7UiPF=cu*nbeoh%coJt|-z`9wVHd*`-iwzO)Frz5$BkczH?{;|WUn4-Zh z74^i~l=^C!=iV;BGx(s_>>)#RUs;=4B30X0yC0gQV6x)UN9nHC2h72Y4D{!7CDy?P zrHF44H3x6&+%9Z8V@jLvgx)nEg$~7B(=)!V2`k$`To2W%BdS)cIfA)A~Qzr8M!bT&r&ez)Tw@hAhnnnSh#e(UZudh!9;G2Tt9VV~Tr%J&Wy_;o8LDFG4ZPS`WQh%E>46L>Q^ zcuoV2u?IH9K6=oqwbJU_)^WK{r;byN%(U=;^F=T;lrI}br_TS9{8;keKB@7uGBMaN z&u(D9)ODCHZ&NNGzS`X_>Gz`D-JPbMa{u7IhaL=WXigo7tJJKE&p(i={ZlQxzKeUX zJp3couJhtR{7w`dle@J|9?KcJ<=>VTrP6H*+!3(1(Z{!Ho|D^2;phJ|@po`1%bi*@ z!HY%YG$eWpzM6}Gt?g|L^)m?D8uSqpT_YKaAGMmVS6eL03VV%=Zd%@+bV(mZ`b#)O z7g)y%wc$1J-xDowgQ-|nrq+8HW?^4!e*Ajdd^?O9o1zla}E`w&aeGAH(Yf_x%i+Ir2ms%#DxqVhetf3p5>*6q!?xc4dPT|o$aTa@J3&ZrZaDp26SI_F!YP@So%8~ca zqikMW>a#)DcA#Z(&z_b189%H6UXi2SVt{`Q)-~*0T+L~(bTZ4zI4AYk$v!ounr5GN zcb*Qh_I=2A%yR}6F(BvMT*bj&{$r#wnp_)YwGJ1$coq z3%IURPhV(8B9ev3>798dJ9$`N735pZ&DKFgDF;$=)4gj}AH%M}9NP<~{quKoQx>T{ zknD{yhoV(f++2?e)#xpZkA51|+MMUH!WWJqvAM(y9kd^I#XX1rz+E?YA=B%<*b4#A z%mc>O&V-huI2u;sQl7fKAMlDkzzd8T_Cl`rF))qk*9{H-<*7&#E*q{wOiK0+0;0!f z`HyEsfA4H4mVXrYfmPcV=lj#aKRKz93bh1hV`)V=N7?v3+kgMU^p>+Ra#j^?oayKN zc?@eoq8G|wV6(T~`h!$37MLZ!}7SL@R!ZSCWWv#tQi z*10*ungneFcV^z#g*V=~Epo~>K1Ad)RR>$1QN}?I^Ckufd~Z$Xy0h0}M|*p33Nw~X z;W%$|`6=V-VDoi9%18+C8;l)KH1hP{UpCaEaF?C>PA`7XBEPztkK!bnMf^d71ZqC* zP-xv&s#KH*%wI((`m8nPa-VV%HeXwnt#L1|b5B?>8VT*bAR8^E1z$HNT*p@E#9ai;E0%IAgy#uHQtkIBPB9bPp{-R=54N&DgLIy;t& znN*muIn!HgORkE;{eX4%Ynn$0-pk`FU;#maf{1x z6a=(6V>aK%ifcL~*OfhdxVuNGlr7HShIPk@8hZU_omCqzEMNF8U>ir%_AkrM+3v0N zyU91$9z>;!Y6A2c+Xs-{FBNN4;g6lZAr*Ve%Tu~#my0HjTfZ!HCx(fbHxbWsX>ANu zEQxB|0L(Vl%TDp*N6}DDQD+xrwbaRs;0(j}rWPaaf`NPrO;F_Ny2KRl1uAr zT^j3Y2t^yPp|X(w*>VSs!ES76Dy``I-Wl=&c&vMF8G9OP*C!OGFThghdlqn$LK`=s z_orzPsxSojQ&5~fki)TRQn)m1j;l_rw$7slZh zFcMtG4<=!39;I>TQBWSR{>WVX;XB8}AMcSMO{Ro|jp1|U_(IVDF0ocEp{3U(mcf>4rx7nlYm_tyI zi`gBcqofq3Z<2Ps*4JyGn*HP?Ja)`#*d!|us$G1G1fM8$;E1vs>HZWhIh<$a#bxV# zZn?6xOeOYmZFG5`p&9P&y0(gYIRG>;5_Tu17)+}e?Ee~7s3nU;lOofM?PduxJiq+{ z>eym?isPVmOSR|L`7CR4|Iy?Un5b zTvsIwViy$KgS0)`;L1AgRw5ux!pRn3#7`oZp zwVMxh+DD)!SPAR<0j~niQBeyVqn5*GyjHhSqk4O+{VU#W*;u zIwn2=sY1)eteMt}!);|PmxvJ|r`fhdPxMsX073&2cT9l8M-q;KOi}-pB&|*xLRVgC z^D+HQ)rB3)hoTK>i*W=-=-c0{Iz%_a8H+@`Y_SHKGT<1xqelu7(B`74^+|86W*^w5 zOH<`)wvl0bG?#{W?vN96d#oxPmp?{TW%<9pEX8}JZFWv>OnP;X&Q3Kuq-k9C^sm|W zhW<;ZZ{?Y?QImiP*7AQn;%a}N8O}oror=gOgF;gUF!va)m#a^bym7m$s;0G--t!q@ zj0^~AG_MpV&E^WG@!%*%%+o!4;-u$NA&3H!CU6x``q9+KXXU84SeD%W@8z_iS6@T9 zaZ9uN@ydYA361A+f|$n%1$t++%{bkv^lB?L+?*r~;E^)MuB^nx#x~e@7Y2t#XIwVu zGcgIe>~5xRnS|EVtg@CS%=BFkxjS3)?84p8SZfJIE(k`7^!IG7Wn~ytKmb#PfxU|_ zs$Xf}vCf_Cw`qk@YryQq-tdh{rnHV-{u+CZ3g|o`dZ%W)w*~#B`v;$DFd4X9jTu_y zjt>Ux=Zl~d3AJfJ>weel54Y#Bsnl>bS5x)=Q|1@Yto^&`deC}%J3DB7b?vW7*!r}l zAigo+jR=dKPz(wQ0)fL#Bw_wInOHM^2Go!}61>GS+)r`M`|6a)hFrI1U3?V3hrB*p z3tz?T?$*wIU)0EiM2qgNO$3!JiZ_(`1D+ip%es~I2gE%Fl;HI5q4PeU2x(FS(ET$*yG&oHjN6swFQ?9MLwU6~Di;?&hqzL{HgJ5rtF>wE>_@n? zn-+t?8ew9K*apnw7NgS383>2a57w=u{$c*5Y-LKNb8{)K#vSC>zo|6-5i3zO4M~m9 z>a3iuZB(PsPvIPnad8QW@nlNjg*pE7^Yis}RrPwMb2A3>a|UBz!(y&CZJw=5u=>&L zR{yGUc}_>NZriG1z(g7T96Fi6&TFIE1UsHkQ~K8{Y9)ArbzCPem+e*0rQ2<8n6kqm zSs3q(o~K7eLr(Dm>$`@>RSixT`%6T`SiFU0;L?y|>5>|^iHa8<53YFie--zhQB7@O z*JwNlC<>^6Cb zF=~3aqXE(S)j;e9WTM^aCLzflQTMkdFr&E~CujVTVt4{E)Y+E`cN$|5-3&|p^)3eP zoRNKA3mb+P62zw z_vlauDMBf##fyI;^JTbt2Ew6n|Q`Gpr-@SJ$ zWl9F^!J_4{e6fuQu|MAZ;8BR)?+9~$g&Xy^Xz(c4y)TpvVj1R^j_G zN4eDrfk9YwbZEOC)T%P^E0CBF+m%W`5MKkrk9nW;+9Ds+R-v4=COz|J1~);A|v)gdotA!l)tSoy=P1THg zHAiK`O7&)o6K5(`K2G8_chN+xvy}@Gy3zg$=l`<$yhLmT)dm6q{>LX^I-zfm&Fj(8 zXwR2L>)A)8?(QDDj<}2rnp4ZA5e#xG_)Y&{7X&HsSe!nH(3(6lm^9QofHpw5cK_gD z5%jTf&)u)8^R0S|l~|l?5Na05CJ8>cAns~t;<)ubHSUWF>C>l>$V8e`)C%F9(T-Hp zzGhp(voy1gQJ^&!9cfVtpJlg#LvI!iIqm;Ccd8t5!Pouyi+~bn))))FO?T3#d7i-~ zdr2v;@61lj=NZ>V+U~EY1Tzu2M2q2X0tz z{o!=W{Z*vv-PNB3FMp$4b}I`=(+hL~oA67gERUXH2DqsEbKA50=@~u3fk^H=6J7U} zY*6VI00 zsE;m`MacYEE-CRyMJ;zV3$>Tsnj-N}5#>e-oo{O=X=!R=c>EMG$BT{7n@?F@znsfY8fGRHq|UJ;;gwK`h$UrXhb*woO*kM0gV zBc)6VNO8)q?u3P9;pNBboaKS+1M)8Cm0oN#rl!VB#p2_GJ9lJ61t{3xBmvjJt!ZE! zL_>-wOQ&`Ty5hW|9~T$FuDO6pjc@JmnWHN_@ECqAWuWZnlgHw0ptQID0Wp}REebxP z(=}tolfRIQ+HQnqMSh_TQKDkENb~)?min{MK7hfW!9H%0XYs88T5WQYrP8di{MXl_a48M+vNRW)~!sMOtrOL;p zSoa2;jucsJOiwE_BiGKVmsP|qtow)V$Y0Q`!)RZOW>y&-uITDNpca)%@~*TB*W7$+ z1IF;Kt1>h0+#3&~Q+xsI@n#(`{uM4?8GkPGL?-b3`u&2=j&7NN3O>7wp!>n;f59(= z&JGdKU?Sg~5ID7s-bQQx)ykvJJE3snPJ}?q;|GO>KTHR@1x;Al=}k)?ewgqF1Oj)r z5TC2r#Id>Aj49mkfxGgGc%!21meZ?0XS!aPaswfHaPTT1Lttv+Tij7Xv6A|;aRw=nJa49&gghfPJy{yojWMKO%_ z!_m@+h^|4vKEy~;df|kJT?7wJ{&X6w_0?ZI;^Xdsa&q{YJHAkpK)#Xm#xm8W{yCgj zha^EmMNY0pz4~`Uq55loYM`9$Pu*LBZ#DgnE~dhXt)VYhvNikR?o`?92K}FlLEYAd zzJn7J#C3I>Zt*S}jGGJQ;dKUG-61DkW?#(o%Nc-0n^J-1KKxDKUZih#PrF&l?yvlO zj%QtOt=`MYm#!q&P(C;t?89<>DP1(HU#clcNAt3>y>KGVKa;Cs?QlhYnwk*;)EykS z*(m??tF>0Yjx#Veey$>Hy3>cri@UMO!in6TRN}eu&k1>*fZLhz6imSz?bz#BT3QZ7 zWdQE70XOqqnss>m%OuE+sRxV zYW)FKO(Q*)OQu>Q^@-)U{Rol9c^K9Kbah1!?`sfm+SAro+V$CLqMU+j@dW9D{W4o6|WAlxVxsH9Bm*24n` z?oB*hD_^STwO^9hY7^f!ruRTyFd?GaO!1hFwWKEua|qmVkrbj%rF^Wu@E`lvc15Sw zcrckH$f5K;pKWT(Q@I1Wr;c34VFu(`o0-!v_pN}P1F>3pn46m@EEZ9n_*FR;9d3#oT&xbD}Zxt9LTs za^X*OU;{;83?c$~Zr(Qcor!(T9enD0WBl zO!xC!$FF!u9A1wSnOR4$QJZgyBu+hFi{PyJ11WIggo%BR#b||xJSb%})JkdJe}dZe zo&^Ky+Y)4eE|g{SyC@{6hWjlP@^n=fMYdBmF(RIrF|^WMG{LLC$Am-uCpg$%chn_)8dUEEJRZ8zQ(sHa(jEZ%{b>&o{n=~$~{48 zvu`4aiKRyw2N+H6&6$}p=bm%Hy8}9Vr`Q~lx~;)jdiqRXTGT+)A$apzBLfvo5e0@K zCXZ7|;*w_sRmT7oqeUz!Npsap8sMTm{yx=vsD3eqpxu^Kl!bD-6S*=_!_VW^^|yse z%3*SyJk;hI3;Kf*>7O-K-*5Gjw6q~dsT;bxAs5k)O+m|xf#eV6c~sfCv}4%`8A00u zW<$_Oqi#T`mOKlnGY-P?3sTW?+v_0O0F?`sv3&MSM-zR|6hZL#cIIma{LqiVWewuI z4$=~Jv967c)^u2fWX3O$?|i&}+OTgkj3HUfY<fD}$`{QWgvOyl)6muFqwED} zMrGsQ2+pLjLkxZvDXh)U58Z4T+oc}vUK0+L6V=Gtbk!PK9u8VNa*>1q zWaW`dvppw*h83rN2T5Y6>@xeEA^qeVFX_pfZ6mMk=5L*D+nX96_0YmxTry?X*T;9B z_02CS_`ZRP389BV{`ysseC*Nuz9>Z5pz_zVb1F=564vC+wi6+(|Qwp-k*s2 z?L@UjqMz6+M$Hs-MMvuu{bQv0QDfc@IFfUi!m;}K9=L!hE}@jY%=*Hj%2gqCp}Lpm z{=q{*2;IDbaHy2D^o^n?xtokICJtAT;2OGmH$C7kJ^b@*hLi$6dLv?{ z2=ZP0SIz<$$RZ!tR<*lzC!Ec)Qt!9eJhWQ4lZwZ7L_Rdn%@MTvWP@H^?KuJHcOG{^ z+ky1-_X*XE28Jn;H@m_&xO(aLz|e2?kSfmU?CfX@a@Kbx-o)LVmqy7Irl-H{m?Tf#-JrH@5JQ^)k8#U+~J zyDXUW(9p}Tj_xkO;e`QvveoPB1Vh=HbPn|9byfxuC3-zur)a>r_OM7dH#7T;?r>tt zHQvFQaJOjAPty$CTWMarR9=#Q1W`S(U78-A4EsrFXLkn&Q1NG*h53PqvXcD# ziT-P;lyh*=6YUF-UXE>M%~)}JgdJ;r!WqlA29CN?x0YbCE!3VVS}W(_nE;(*L8Nl{ z40F2lPu7K8|BacoxuN#i_AgOYUXRxj@`bMmRu2z5IWiW#7~pMP%>M0ETauai?xJnA z6D=`A{`BOmKT7NoP;%Kj&}Fm<#E$Jpr1!CzDhGv|b>fQ&#c8FBb+=M0=+ko!%!UAh$rK)Pqd+ z_U9EZXl=zUFE^ILN4isndiGa2C12LEnXY17~Mo+U`YxHz1yE{6B?sE=Do`WRv{1?~xx;DF3*y|JS^Q()y zQs6tCE@@L@KAeTns#5;|VI!gPXzJ_vYsW`f;+Cgz(4+<4iPuUab841TFn)Z*qR0sa z>{Ji;QY@c0J}u?Tuhr5!Qobr4DZp1=9!GYZ(3$xBFYgOI zVzzk~Az{}Y%2JcB*N6UCzA;;$oN50|>g4Pfx~V%irUOEi({T76I6lX$`9cH47BL~` z&Yc>cN`_esEZbK300IqjOn_x==Tu=I9`k8i4Mcf*{}KtvD|olIzC7*ZpO1(_-T0Q! z@G`#NtU~wJcg0U{=Fgd)`!C>X>1-Z$IbCR{t2$aZZ++eWow(Aviug!*!k{JTwlu}b zAZKF)i_OZiRa4_An&{YW_@!x8m*X8WD3w$x;6E1Z zhu|6k)@e9wHeXY2dP$-EjsOC94g4rJ96uebc zQ}yAr8VCVGLC+wK$y>4$+d~H$skq6!5v+y9E!-2CM-OQ-po?m=`*Cp!@u}&YSvRa0 zr{WYr^SVme$B$=*SuyC9Tc-a#To z2P_r#$m@ME8UNHa2P~}7GK>nw3cB`oeeU$wAMg~XE?I-BQUdLVMe+j-sbS*{G#kN3|CZvZg@Z|hsg)o35KeZzy? zL=krn&`J~}D@~_pEjL;gvvY==1Xtw1Ooyf(oQ;cAoY=I7irIIep1l50KFa?GE{=Rh zMRZk1WENSTNc{=w+=}iC2wT*-PZrA3Uw-h*v|xe7VR2zW zGl8;FhhW-`n>HDfy4scz7f|Uv@oo{6I68EN=f2XdMNH^iBaWYd6p=W`dO+~WJWWMa zwIqeSQ{_o1y5z>5>)N&X>~~+cr`BD=Nq!CA<;7Y4dx4nDY{WQFv6wYNEX>LIo?#~| zA1mJ6h_%9&!$9l_22x0#+;U%hblgO7J9}YOhDD=ITt LjBx0?(9rEIFYgr>UAsoc^k9+7+Nvz8MAk6vrf^o~X;WHq z|IfwvYu89tM{Y6s;$9#V!E>^jEKI03tvb5o$j7zcn#7*13Cw?<-@^%{wZ2aibgwdvMmU;-I|7F2(-LrE|itl@cFRQEL zM+f8q(Mp+2`vsTLqlj8F6~#XJZ|vDoUROEaKb{o%qJRmPCgpCp#400p`~#oMCC~~R z843#i=xyo%tGe;}vfA38FF+cY*jJTogOZ_xU@s_((?cpP41{3%RyIHD1o(>T8Ml?vcIr9i=%|5NG@CW1(J(gX(CQ@S{t^K}*^ zYdx(1ew8L|vVP>5OV=am#Wx75;wyBWLl%e$po{AFu#POs;VCXZ>-!sLW|`T6a%v5T zp*I`x{M*^mvfMzwfwFU1!Udq9c%84j4a{3pT_DTU$H842K}hh36CA3)jsuihwqo-} z^kC+u@+ZY98(`v;w!79 zWo7+FwTi*It9QC9wmR)DLrCMlD-5!)vO+>=CwVfFg5|M4YHJ^fsn|H%zzSNMtsz)C zLC|lM@U*2yOW0%^Zb=2C{MO2$?|$R9sFta$67R)XV9$7qP(raCma;V?QnXMO)1hq8 zEvdHD;pRN0oQFzC3y?8n1`$}3*Nbym_OJUfjvWMlc*R}8d-v02{!&tEu}ppvzGx9z z)4{X-@tBEQSXkJ(SH}~-kRZw{TkTuA5F7?I(m4gydt$|VT>yKFDbk>?rZ$Ocq6%D} zyf?J077O-OC!60F*72dKq}`I|hWtv-pcN$A?Hv9LZHv5FOhq?sidcX`!tW&#&P?7g z*e2%^>APZ1n3mPBlk?F9GwR7iWkzC1sUH2JU4c+VIcxxf)TaAeOPG98=2mk+13>T;mu^MCCYUxU{BO-enljO0bPZp@=`u8eC3p$EVjChT{{dr7euL& zM*6I`d`xQ!USD%@CJhkl)^qr|vy;D6BiKX()scXCo+)}4>v<{UZ3#9cq$IO!tuZU6 zF`e@=#Pm9d!~cTOXm+8Ef)f+5;UKEaEN5PGoEPl>L8Ko?khPFTx_f(ju1!5HPXrgO zb$9c1c6Ka+v@t^E(ieyYPtjqen!6nzt=s>od|W{;*20V+!TMWv(juVR7nFoHbkYHu zT~f2#L*ZuVPmJA+X6Q?d-Kt8qI&O}tsuv4Zdg}^mJ_W0VAa#PSzXe@=v5e<87ls&u zp(;E}QD=v{)Z2lwXN2~~m0f)?w_odEwxyw`)kt_;Xj1){=ILtaRM*h8TA}^J&kIMz zUHhb#HR0#mVPV#3_1Mq2T!oF5!ET_Xk6XCsoGIi8tahKSDu|bfW^iMQzV`QfEK+}i zKrFr{Tln73;^K{g8Y7Y%E*#SWreDr`;cv12|9Ut^jQ&dcXj5#m^`daUw z+?De7oNtE;-X!R#rlr9wnu904gAh6HOWxZqD_RYD1XDBj4;$UrhOI6Tnk>lQ{c6eJrDJP_hrb_ z)(*9-f!Mg1m~mrL+@>qUwM|Ii>VjAXRETc>4jZmTrz?S$d19y(tDy6$z%aK!Kg)LB z)|PD#SE74^c2eP$j({@K@*pm;gyt#Bm6%rl|V8}fIi5@lf zJrf*lnCLZG+k?d(3PBysR7BuGHyU?j%f3;hx$A|<*8Z%dI_=C}bQ~=eT1V9+{kurm zmaSjvuT%2)k*cE^TjlDETc#Cv&tYWNI2fa3)Bk2+j?OMLSA70SV_)VvTko$5& z!~)$xyk15F6#UdTG5!&=!oVithQBm<^xLAK;2EDH6TL$?hvH}oq7hX1Q6+rC%g4ip z*4`J+;bA068L1~?((lAoRW*E`Pl(B#m9ye<4>gWs5{Nk)*RG7NuLi^?cs%`HziMHO z?c;Mn%iB>LlP!9`TJVJLT0ImF$dP7ajkFKs5yl3lJa z0T5RqPX|QmmxTCe)FdwB4(MVS;Jz!qH<_y_LwB^|A*_U0uF>DHYkWr>Dnas*BBI_t z_(UqBi5i+UfpEk#LKtb=FkP{amd#Py!q_MHXt6 z(KtVr+58n){f$O-tc_VElQweqkwNjW#Yai^MBTc8r#}Ho;e}bQ@X#v*Ma2hO+xu6B z9Ude{N+Jbnes@%vJ^lCTqTP+40uA&PF0S}*DHi?1rJ+&vsZkzi4v~D(c%7K*9yQi%UQ>_1wo@*kDkbfw{}t^j@kp>K46b-# zb|}F~di0Px&gDbl_9}pSUt-At2yAIy z*Y| z-Sie8aw}-mXRpo~)L`-sgh8bJ#aFFyQUjvDma?lT1C{VvxmHK|>C?55qfhCmsB7Zy z1ibvehmIdMa(DwbZO6Zzg3t5W11*$Cay8*+2!7!x)$I5r{Y8XgZ&)(VJKYegc+R?= z@|HIBy%Dy0w+0fFL0zs?q9~E&CG3WW1$?bFD)GPz5=2NrVj>evC{k4;44=$j?$HGe zgYs`l=%%#o@9t#jM>(IKc;VGZPn0fwF*9r{WEiskXa-0IHCE{6&V}weSPs3wt^8BD zdahSroT*UxncsLbn5dupE=BGPc`^|6_}}$E67E&3f;y(lfa}RTPH(w(aZ~!|TMy6$;qgm-lXa zD>XKsXBZB^eHJqHYfn>4<-_uw>`ASC10QYq7gvm*O?=tc%dlZD^hN4w*R)zGCrGT| zk@kR}sg3IyUWHwx3{7=aEgujZN|`)5c6Jv}*uL1mxl+T>bKppVwB;2Iq)=i3Z0R7kXqijm$NW- zwFX*Eh6bE7tN(a&d};)wXm03hRtMs~|NME_b2vwol@WsknT4fugjUY zM&k%4pyoVh>tKy^IeZXuJ?ZUr-~n1p;Hmrz6dIJCpLlekPls&ti=}&v2mSwqoM$m= zpNP0>V-u5wdFPo2cVqE)zk;4*ulBC99xSr?$&^%7q+{=j-h0cd#JFDX8*7%hH(gK7C6|{kk3`;y<0YZRLd}6zk_^Yu+jk=$UQ?GSRq%8Xwdj z<{M_+r7|htVE)sr`ORL;hbA4dV1=yhc6@(&n7jrwrIH+puFtA!H46YAD>8fxhz|LT7+MCS73i~ZH1d6%{MiP}~B?p1%3XGJl* zV~Ps`iO|u{KfWXP(P>gJwnV!Kb>BdZPF!!WcdGX2inE5i;y20cLFj-DRN z=Vz2BHX#mL1-VV(9ipy2FGpys_~(-3JWteqAO3jIR-LC4dK(CVx1aGg)RFhTiO1mb z(4~DCmGa;_Y=JCcWgG>DZ7cHo*av(P63_U%orZfRo5^IkH?mv@e2aGcR;mKl-DF1G z<>?bQ&%ko+si%nY++F$umhK*oLK7>n4y75ZDCTDEU2RPL$N}qH7GsyG_TJj@ELRv& zgE(B}%d%B90>u6_&Fq#VMk?Z?unf61h768#k7qTr?Z|cpS({GV=w-8SLO-H13{48E zq#%zCCV4Pw_zsE96S9-qdw)KIdPhEkMl^N_MnW0C*en{cxc5PN$3wxv-YQM0>;do0 z;yq#7Nx8HJVnld|V6+li+0fl{#65&KGX9(P4v#tT5+A5Uo9I6L0pI(2f#Kl#+AY~+ z4Ot5VGi{{cG4!qvbaNHl5d3rWjnGqMrff)zoeSR!H$MVHWkqmsGA_9wD~mzg72=>g z&&I~bXA6RT9I#iGb>!;oppP(N8I)HhCvbVC;q^*gRrygTQDdb=xD_E_w0m_VGqhy? z9^gmw7%gpG3hq@1DVv#tlej$SB=78Gt>ZkH6vca{oUR>j@J8*Ee5T7b%%2~o+W6C~ zeOu5yWX1@5kfxXASR3N#DCSE%IU1VqGPRI$uDq74^`w4W&0;{)Qe5uvuOMG9&*iDL zQ8_6-G2uJ$atyoL;EvBOOWzbMp#6M->i)li{h}L-+t0%%){@%QCUjrb9$jUiz#+Z& zoh}h$sMp!|#m zQkubBXHh%3BqU-Lo$OJNW2|D33qmYm4E{_sK?|>5rF}oD=+Jd^wppJWB=$9=JY%`% z)J@FyL%9n?aOTe)z~=t)Dck!gUki;t%KHzR#35ESN)(=`W(yl+r^T(~{w2^f{+Rc(SaC zOBh+L)2eE=Hax{k$8RFUlMd3WD=j&S{DpZ1Isj`V4%fuREz-5Xd`II#T@7&(k;4m+ zt`<*qZna5HEYD|MEGASa1SW+4Q6d4RZ_{?3et4M}S7Ty`WOE`vApDQ=`_!!^MI~A7(b$rCbO!7Y;tG{IU;sJ0@x86ns~44(HrJLij?+a? zBq89Te)Ljm6ep zy!~Eg*4vz+($?BapRtwwJi{U@@|nyl7Sb4UNXGRux*cDF^kRW)vnG?Uiw+t;w2U%` z{tlvn01(K8CM)Adl0nVQSCHNbje~7&S?dplnUDFQz`qpB>EwaN66H%6fTlT}|U zarj2^gA6q9tYMMaj;r%BC-a|)7FJx@?^@v?PwY+qS@8v}4MVL_7hml^9Xp;+m^Jax zLxGKv*73G9YX0I|**+BKpYakemOz6j-=mOu`kwQ>JIIA~?MJwwJ3iB4 zlxV*UBIBRWKwLSWZovyW$}VDV-@Yx+jFedEpO(_J6$usfPcKU*UhWcmMys`9I_T?-=R-{U`l5{{Ok*AxQAD{y!Xk qi3&~_mD}u2E1>&}%$X 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 literal 9253 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 Date: Fri, 31 Jul 2026 15:36:24 +0300 Subject: [PATCH 08/13] refactor: review --- .../scrollbar/examples.scrollbar.en.md | 2 +- .../scrollbar/examples.scrollbar.ru.md | 2 +- .../scrollbar/private/e2e.playwright-spec.ts | 151 ++++++++- packages/components/scrollbar/private/e2e.ts | 158 +++++++++- .../scrollbar/private/scrollbar.module.ts | 1 + .../scrollbar/private/scrollbar.scss | 92 ++++-- .../scrollbar/private/scrollbar.spec.ts | 165 +++++++++- .../components/scrollbar/private/scrollbar.ts | 294 +++++++++++++----- ...e-scrollbar-disable-interaction-example.ts | 21 +- packages/e2e/routes.ts | 6 + .../components/scrollbar-private.api.md | 11 +- 11 files changed, 785 insertions(+), 118 deletions(-) diff --git a/packages/components/scrollbar/examples.scrollbar.en.md b/packages/components/scrollbar/examples.scrollbar.en.md index 764864a225..b03207bc14 100644 --- a/packages/components/scrollbar/examples.scrollbar.en.md +++ b/packages/components/scrollbar/examples.scrollbar.en.md @@ -30,6 +30,6 @@ Setting `native: true` via `kbqScrollbarConfigProvider` makes the directive fall ## Disabling interaction -The `kbqScrollbarDisableInteraction` input keeps scrolling functional while disabling drag-on-thumb and click-on-track. +The `kbqScrollbarDisableDrag` and `kbqScrollbarDisableClick` inputs keep scrolling functional while independently disabling drag-on-thumb and click-on-track — e.g. keep drag for quick scanning while disabling accidental track clicks, or the other way around. diff --git a/packages/components/scrollbar/examples.scrollbar.ru.md b/packages/components/scrollbar/examples.scrollbar.ru.md index 582925df94..b40f185ae0 100644 --- a/packages/components/scrollbar/examples.scrollbar.ru.md +++ b/packages/components/scrollbar/examples.scrollbar.ru.md @@ -30,6 +30,6 @@ ## Отключение взаимодействия -Входной параметр `kbqScrollbarDisableInteraction` оставляет скролл рабочим, но отключает перетаскивание ползунка и клик по треку. +Входные параметры `kbqScrollbarDisableDrag` и `kbqScrollbarDisableClick` оставляют скролл рабочим, но независимо друг от друга отключают перетаскивание ползунка и клик по треку — например, можно оставить перетаскивание для быстрой прокрутки, но отключить случайные клики по треку, или наоборот. diff --git a/packages/components/scrollbar/private/e2e.playwright-spec.ts b/packages/components/scrollbar/private/e2e.playwright-spec.ts index 25f383a0e0..3eed696dc0 100644 --- a/packages/components/scrollbar/private/e2e.playwright-spec.ts +++ b/packages/components/scrollbar/private/e2e.playwright-spec.ts @@ -60,22 +60,77 @@ test.describe('KbqScrollbar (private)', () => { await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBeGreaterThan(0); }); - test('kbqScrollbarDisableInteraction blocks a real thumb drag', async ({ page }) => { + test('kbqScrollbarDisableDrag blocks a real thumb drag but leaves track click-to-jump working', async ({ + page + }) => { await page.goto('/E2ePrivateScrollbarDrag'); const container = page.getByTestId('drag-disabled'); const viewport = getViewport(container); - const thumb = container.locator('.kbq-private-scrollbar-track_vertical .kbq-private-scrollbar-thumb'); + const track = container.locator('.kbq-private-scrollbar-track_vertical'); + const thumb = track.locator('.kbq-private-scrollbar-thumb'); - const box = await thumb.boundingBox(); + const thumbBox = await thumb.boundingBox(); - if (!box) throw new Error('bounding box is null'); + if (!thumbBox) throw new Error('bounding box is null'); - await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await page.mouse.move(thumbBox.x + thumbBox.width / 2, thumbBox.y + thumbBox.height / 2); await page.mouse.down(); - await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2 + 40, { steps: 5 }); + await page.mouse.move(thumbBox.x + thumbBox.width / 2, thumbBox.y + thumbBox.height / 2 + 40, { + steps: 5 + }); + await page.mouse.up(); + + expect(await viewport.evaluate((el) => el.scrollTop)).toBe(0); + + const trackBox = await track.boundingBox(); + + if (!trackBox) throw new Error('bounding box is null'); + + // The thumb starts at the track's top — pressing down near the bottom lands on the + // track itself, triggering "jump to click", which disableDrag must leave untouched. + await page.mouse.move(trackBox.x + trackBox.width / 2, trackBox.y + trackBox.height - 5); + await page.mouse.down(); + + const afterJump = await viewport.evaluate((el) => el.scrollTop); + + expect(afterJump).toBeGreaterThan(0); + + // But holding the mouse down past the jump and moving it must NOT keep following the + // pointer — that would be a real, continued drag, which disableDrag is supposed to + // block regardless of how the gesture started. + await page.mouse.move(trackBox.x + trackBox.width / 2, trackBox.y + trackBox.height / 2, { steps: 5 }); await page.mouse.up(); + expect(await viewport.evaluate((el) => el.scrollTop)).toBe(afterJump); + }); + + test('kbqScrollbarDisableClick blocks track click-to-jump but leaves thumb drag working', async ({ page }) => { + await page.goto('/E2ePrivateScrollbarDrag'); + const container = page.getByTestId('click-disabled'); + const viewport = getViewport(container); + const track = container.locator('.kbq-private-scrollbar-track_vertical'); + const thumb = track.locator('.kbq-private-scrollbar-thumb'); + + const trackBox = await track.boundingBox(); + + if (!trackBox) throw new Error('bounding box is null'); + + await page.mouse.click(trackBox.x + trackBox.width / 2, trackBox.y + trackBox.height - 5); + expect(await viewport.evaluate((el) => el.scrollTop)).toBe(0); + + const thumbBox = await thumb.boundingBox(); + + if (!thumbBox) throw new Error('bounding box is null'); + + await page.mouse.move(thumbBox.x + thumbBox.width / 2, thumbBox.y + thumbBox.height / 2); + await page.mouse.down(); + await page.mouse.move(thumbBox.x + thumbBox.width / 2, thumbBox.y + thumbBox.height / 2 + 40, { + steps: 5 + }); + await page.mouse.up(); + + await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBeGreaterThan(0); }); }); @@ -102,6 +157,58 @@ test.describe('KbqScrollbar (private)', () => { }); }); + test.describe('E2ePrivateScrollbarContentMutation', () => { + test( + "appending content that grows scrollHeight without resizing the scroll element's own " + + 'box does not shrink the thumb until update() is called — there is no MutationObserver ' + + 'watching content changes', + async ({ page }) => { + await page.goto('/E2ePrivateScrollbarContentMutation'); + const container = page.getByTestId('content-mutation'); + const thumb = container.locator('.kbq-private-scrollbar-track_vertical .kbq-private-scrollbar-thumb'); + + const initialHeight = (await thumb.boundingBox())?.height; + + await page.getByTestId('append').click(); + + // Real browser, real ResizeObserver — the scroll element's own clientHeight never + // changed (only its scrollHeight did), so no resize fires, no scroll happened, and + // nothing else re-measures on its own: the thumb stays exactly as it was. + expect((await thumb.boundingBox())?.height).toBe(initialHeight); + + await page.getByTestId('update').click(); + + // Only once update() is explicitly called does the thumb reflect the new, + // much-larger content — proving the staleness above wasn't a fluke of a + // still-settling layout, but the actual absence of auto-detection. + expect((await thumb.boundingBox())?.height).toBeLessThan(initialHeight!); + } + ); + }); + + test.describe('E2ePrivateScrollbarHostPadding', () => { + test("insets the track by the host's own padding instead of sitting flush with its border edge", async ({ + page + }) => { + await page.goto('/E2ePrivateScrollbarHostPadding'); + const container = page.getByTestId('host-padding'); + const track = container.locator('.kbq-private-scrollbar-track_vertical'); + + const containerBox = await container.boundingBox(); + const trackBox = await track.boundingBox(); + + if (!containerBox || !trackBox) throw new Error('bounding box is null'); + + // The host has 20px of its own padding on every side — the track's right edge should + // align with the padding box (the real content edge), not the host's border edge 20px + // further out. + const expectedRight = containerBox.x + containerBox.width - 20; + + expect(trackBox.x + trackBox.width).toBeCloseTo(expectedRight, 0); + expect(trackBox.y).toBeCloseTo(containerBox.y + 20, 0); + }); + }); + test.describe('E2ePrivateScrollbarHoverVisibility', () => { test("'hover' mode (the default) reveals the custom track on real mouse hover and hides it again on mouse leave", async ({ page @@ -119,4 +226,36 @@ test.describe('KbqScrollbar (private)', () => { await expect(container).not.toHaveClass(visibleClass); }); }); + + test.describe('E2ePrivateScrollbarKeyboard', () => { + // Same reasoning as `getViewport` above: the auto-created `.kbq-private-scrollbar-viewport` + // wrapper is the real scrolling element here (no explicit `kbqScrollbarVirtualViewport`), so + // it's the element that must be focusable and respond to keyboard scrolling. + const getViewport = (container: Locator) => container.locator('.kbq-private-scrollbar-viewport'); + + test('the scroll region is reachable via Tab and operable with the keyboard, like the native scrollbar it replaces', async ({ + page + }) => { + await page.goto('/E2ePrivateScrollbarKeyboard'); + const container = page.getByTestId('keyboard'); + const viewport = getViewport(container); + + // No other focusable element on the page — a single Tab from the top must land here. + await page.keyboard.press('Tab'); + await expect(viewport).toBeFocused(); + + await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBe(0); + + await page.keyboard.press('ArrowDown'); + await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBeGreaterThan(0); + + await page.keyboard.press('End'); + const maxScrollTop = await viewport.evaluate((el) => el.scrollHeight - el.clientHeight); + + await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBe(maxScrollTop); + + await page.keyboard.press('Home'); + await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBe(0); + }); + }); }); diff --git a/packages/components/scrollbar/private/e2e.ts b/packages/components/scrollbar/private/e2e.ts index 86a7cca95c..ed5be643e8 100644 --- a/packages/components/scrollbar/private/e2e.ts +++ b/packages/components/scrollbar/private/e2e.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { KbqScrollbar, kbqScrollbarConfigProvider } from './scrollbar'; @Component({ @@ -61,11 +61,21 @@ export class E2ePrivateScrollbarStateAndStyle {} class="e2e-scrollbar" kbqScrollbar kbqScrollbarVisibility="always" - kbqScrollbarDisableInteraction + kbqScrollbarDisableDrag data-testid="drag-disabled" >

+ +
+

{{ content }}

+
`, styles: ` :host { @@ -181,3 +191,147 @@ export class E2ePrivateScrollbarScrollTo { export class E2ePrivateScrollbarHoverVisibility { readonly content = `In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists of an attacker submitting many possible keys or passwords with the hope of eventually guessing correctly. This strategy can theoretically be used to break any form of encryption that is not information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of successfully guessing the key is negligible.`; } + +/** + * Demonstrates that appending content which grows `scrollHeight` without changing the scroll + * element's own box size (`clientHeight`) is invisible to `KbqScrollbar` until something calls + * `update()` — there's no `MutationObserver` watching content changes. + */ +@Component({ + selector: 'e2e-private-scrollbar-content-mutation', + imports: [KbqScrollbar], + template: ` + + + +
+ @for (item of items(); track item) { +
{{ item }}
+ } +
+ `, + styles: ` + :host { + display: block; + padding: var(--kbq-size-xs); + } + + .e2e-scrollbar { + width: 200px; + height: 300px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + .row { + height: 30px; + margin: 0; + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + 'data-testid': 'e2ePrivateScrollbarContentMutation' + } +}) +export class E2ePrivateScrollbarContentMutation { + // 15 rows * 30px = 450px vs. a 300px-tall viewport — overflows, but the thumb ratio (300/450) + // stays well above the min-thumb-size clamp, so a later shrink is actually observable. + protected readonly items = signal(Array.from({ length: 15 }, (_, i) => i)); + + protected append(): void { + const current = this.items(); + + // Pushes scrollHeight from 450px to 1050px while the container itself stays 300px tall — + // the box size never changes, only the content that overflows it does. + this.items.set([...current, ...Array.from({ length: 20 }, (_, i) => current.length + i)]); + } +} + +/** + * Demonstrates that the track insets by the host's own padding instead of sitting flush with its + * border edge — without `syncHostPadding()`, a host with its own CSS padding would leave the track + * floating over the empty padding area instead of overlaying the real content edge. + */ +@Component({ + selector: 'e2e-private-scrollbar-host-padding', + imports: [KbqScrollbar], + template: ` +
+

{{ content }}

+
+ `, + styles: ` + :host { + display: block; + padding: var(--kbq-size-xs); + } + + .e2e-scrollbar { + box-sizing: border-box; + width: 200px; + height: 100px; + padding: 20px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + margin: var(--kbq-size-l); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + 'data-testid': 'e2ePrivateScrollbarHostPadding' + } +}) +export class E2ePrivateScrollbarHostPadding { + readonly content = `In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists of an attacker submitting many possible keys or passwords with the hope of eventually guessing correctly. This strategy can theoretically be used to break any form of encryption that is not information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of successfully guessing the key is negligible.`; +} + +/** + * No focusable descendants inside the scrollable content — a keyboard-only user must be able to + * reach and operate the scroll region itself (Tab to focus it, arrow/paging keys to scroll it), + * the same way the native scrollbar it replaces would allow. + */ +@Component({ + selector: 'e2e-private-scrollbar-keyboard', + imports: [KbqScrollbar], + template: ` +
+

{{ content }}

+
+ `, + styles: ` + :host { + display: block; + padding: var(--kbq-size-xs); + } + + .e2e-scrollbar { + width: 200px; + height: 100px; + border-radius: var(--kbq-size-border-radius); + background-color: var(--kbq-background-bg-secondary); + } + + p { + width: 150%; + height: 400%; + margin: var(--kbq-size-l); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + 'data-testid': 'e2ePrivateScrollbarKeyboard' + } +}) +export class E2ePrivateScrollbarKeyboard { + readonly content = `In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists of an attacker submitting many possible keys or passwords with the hope of eventually guessing correctly. This strategy can theoretically be used to break any form of encryption that is not information-theoretically secure.[1] However, in a properly designed cryptosystem the chance of successfully guessing the key is negligible.`; +} diff --git a/packages/components/scrollbar/private/scrollbar.module.ts b/packages/components/scrollbar/private/scrollbar.module.ts index dff0156223..15f2ac37da 100644 --- a/packages/components/scrollbar/private/scrollbar.module.ts +++ b/packages/components/scrollbar/private/scrollbar.module.ts @@ -6,6 +6,7 @@ const COMPONENTS = [ KbqScrollbarVirtualViewport ]; +/** NOTE! Intended exclusively for use within this repository. */ @NgModule({ imports: COMPONENTS, exports: COMPONENTS diff --git a/packages/components/scrollbar/private/scrollbar.scss b/packages/components/scrollbar/private/scrollbar.scss index 11c6f6ddfb..571c7673a8 100644 --- a/packages/components/scrollbar/private/scrollbar.scss +++ b/packages/components/scrollbar/private/scrollbar.scss @@ -16,6 +16,15 @@ --kbq-private-scrollbar-track-default-background: transparent; --kbq-private-scrollbar-track-hover-background: transparent; --kbq-private-scrollbar-track-active-background: transparent; + --kbq-private-scrollbar-transition-duration: 0.15s; + + // Overwritten inline by `KbqScrollbar.syncHostPadding()` with the host's own real computed + // padding on every recompute — these `0px` defaults only apply before the first recompute, or + // when `syncHostPadding()` doesn't run at all (`native`/coarse pointer, no track to position). + --kbq-private-scrollbar-host-padding-top: 0px; + --kbq-private-scrollbar-host-padding-right: 0px; + --kbq-private-scrollbar-host-padding-bottom: 0px; + --kbq-private-scrollbar-host-padding-left: 0px; } :where(.kbq-dark .kbq-private-scrollbar) { @@ -38,7 +47,7 @@ } // Hides the native scrollbar on the effective scroll element (host, or the delegated -// kbqScrollbarViewport child) cross-browser. Applied only when overlaying with our own +// kbqScrollbarVirtualViewport child) cross-browser. Applied only when overlaying with our own // track/thumb — i.e. not on coarse pointers and not under the `native` config option. .kbq-private-scrollbar_hide-native { scrollbar-width: none; @@ -51,7 +60,7 @@ } // Auto-created wrapper for the host's own content when there's no explicit -// kbqScrollbarViewport — holds `overflow`, so the track/thumb (siblings on the host) stay a +// kbqScrollbarVirtualViewport — holds `overflow`, so the track/thumb (siblings on the host) stay a // fixed overlay instead of scrolling away with the content. .kbq-private-scrollbar-viewport { // The auto-created wrapper is a real component (`kbq-scrollbar-viewport`, an @@ -72,15 +81,20 @@ opacity: 0; visibility: hidden; pointer-events: auto; + // Prevents the browser's own touch-scroll/pan gesture recognition from fighting our + // pointer-driven drag on hybrid-input devices (e.g. touchscreen laptops) that can still route + // a touch pointer here despite `KbqScrollbar` building custom UI only when + // `matchMedia('(pointer: coarse)')` is false at init. + touch-action: none; transition: - opacity 0.15s, - visibility 0.15s, - background-color 0.15s; + opacity var(--kbq-private-scrollbar-transition-duration), + visibility var(--kbq-private-scrollbar-transition-duration), + background-color var(--kbq-private-scrollbar-transition-duration); // The synthetic `.kbq-hover`/`.kbq-active` forms match on an ANCESTOR (the host), not on this // element itself — the track is created via `Renderer2`, not template-authored, so nothing - // outside this directive can put a class directly on it. Matches how `kbq-scrollbar`'s own - // `.os-scrollbar-handle` is driven the same way (`_scrollbar-component-theme.scss`). + // outside this directive can put a class directly on it. Matches the same ancestor-class + // pattern already used in `kbq-scrollbar`'s own theme (`_scrollbar-component-theme.scss`). &:hover, .kbq-private-scrollbar.kbq-hover & { background: var(--kbq-private-scrollbar-track-hover-background); @@ -92,7 +106,17 @@ } } -.kbq-private-scrollbar_disable-interaction .kbq-private-scrollbar-track { +// Only when BOTH drag and click are disabled does the whole track (and, via inheritance, its +// thumb) go fully inert. Deliberately not split into two single-flag rules: `pointer-events: none` +// on just the thumb makes a click landing on it pass through to the track underneath, which the +// track's own pointerdown listener then reads as `event.target !== thumb` — i.e. an ordinary track +// click — and still runs the full `beginInteraction()`/drag-follow flow through `_disable-click` +// alone, silently un-disabling drag. Same problem mirrored on `_disable-click` alone. With only one +// of the two flags set, blocking is left entirely to the `disableDrag()`/`disableClick()` checks in +// `scrollbar.ts` (`wireTrackInteraction`) instead — the thumb's own listener no-ops, and the +// pointerdown event bubbles to the track listener which excludes `event.target === thumb`, so +// nothing happens on either path once even one side is genuinely disabled. +.kbq-private-scrollbar_disable-drag.kbq-private-scrollbar_disable-click .kbq-private-scrollbar-track { pointer-events: none; } @@ -101,38 +125,53 @@ visibility: visible; } +// The custom track/thumb are a purely interactive, screen-only affordance — nothing on a printed +// page can be dragged or clicked, so keep them out of print output entirely rather than rendering +// an inert (and `aria-hidden`) bar of unused affordance space. +@media print { + .kbq-private-scrollbar-track { + display: none; + } +} + +// Inset by the host's own padding (see `--kbq-private-scrollbar-host-padding-*` above) instead of +// flush `0` — the track is `position: absolute` directly on the host, so without this it would +// sit at the host's border edge even when the host's own padding pushes the real content further +// in, leaving the track floating over empty padding instead of overlaying the content it belongs +// to. .kbq-private-scrollbar-track_vertical { - top: 0; - right: 0; - bottom: 0; + top: var(--kbq-private-scrollbar-host-padding-top); + right: var(--kbq-private-scrollbar-host-padding-right); + bottom: var(--kbq-private-scrollbar-host-padding-bottom); width: var(--kbq-private-scrollbar-size-track-dimension); } .kbq-private-scrollbar_rtl .kbq-private-scrollbar-track_vertical { right: auto; - left: 0; + left: var(--kbq-private-scrollbar-host-padding-left); } .kbq-private-scrollbar-track_horizontal { - left: 0; - right: 0; - bottom: 0; + left: var(--kbq-private-scrollbar-host-padding-left); + right: var(--kbq-private-scrollbar-host-padding-right); + bottom: var(--kbq-private-scrollbar-host-padding-bottom); height: var(--kbq-private-scrollbar-size-track-dimension); } // `axis: 'both'`, both tracks visible: each track stops short of the shared corner instead of -// running underneath the other track's thumb. +// running underneath the other track's thumb — added on top of (not instead of) the host-padding +// inset above. .kbq-private-scrollbar-track_vertical.kbq-private-scrollbar-track_has-horizontal { - bottom: var(--kbq-private-scrollbar-size-track-dimension); + bottom: calc(var(--kbq-private-scrollbar-host-padding-bottom) + var(--kbq-private-scrollbar-size-track-dimension)); } .kbq-private-scrollbar-track_horizontal.kbq-private-scrollbar-track_has-vertical { - right: var(--kbq-private-scrollbar-size-track-dimension); + right: calc(var(--kbq-private-scrollbar-host-padding-right) + var(--kbq-private-scrollbar-size-track-dimension)); } .kbq-private-scrollbar_rtl .kbq-private-scrollbar-track_horizontal.kbq-private-scrollbar-track_has-vertical { - right: 0; - left: var(--kbq-private-scrollbar-size-track-dimension); + right: var(--kbq-private-scrollbar-host-padding-right); + left: calc(var(--kbq-private-scrollbar-host-padding-left) + var(--kbq-private-scrollbar-size-track-dimension)); } .kbq-private-scrollbar-thumb { @@ -141,7 +180,18 @@ border-radius: var(--kbq-private-scrollbar-size-thumb-border-radius); background: var(--kbq-private-scrollbar-thumb-default-background); cursor: default; - transition: background-color 0.15s; + touch-action: none; + transition: background-color var(--kbq-private-scrollbar-transition-duration); + + // Expands the thumb's interactive hit area 3px beyond its rendered box without changing how it + // looks — a pseudo-element isn't a separate event target, so a pointerdown landing in this + // margin still reports `event.target` as the thumb itself, which `KbqScrollbar`'s own + // `pointerdown` listener on the thumb already handles like any other hit. + &::before { + content: ''; + position: absolute; + inset: -3px; + } &:hover, .kbq-private-scrollbar.kbq-hover & { diff --git a/packages/components/scrollbar/private/scrollbar.spec.ts b/packages/components/scrollbar/private/scrollbar.spec.ts index 041f788041..2038e18942 100644 --- a/packages/components/scrollbar/private/scrollbar.spec.ts +++ b/packages/components/scrollbar/private/scrollbar.spec.ts @@ -65,16 +65,44 @@ class TestScrollbarHost { } @Component({ - selector: 'test-scrollbar-disable-interaction-host', + selector: 'test-scrollbar-padding-host', imports: [KbqScrollbar], template: ` -
+
content
`, changeDetection: ChangeDetectionStrategy.OnPush }) -class TestScrollbarDisableInteractionHost { +class TestScrollbarPaddingHost { + readonly scrollbar = viewChild.required(KbqScrollbar); +} + +@Component({ + selector: 'test-scrollbar-disable-drag-host', + imports: [KbqScrollbar], + template: ` +
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarDisableDragHost { + readonly scrollbar = viewChild.required(KbqScrollbar); +} + +@Component({ + selector: 'test-scrollbar-disable-click-host', + imports: [KbqScrollbar], + template: ` +
+
content
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarDisableClickHost { readonly scrollbar = viewChild.required(KbqScrollbar); } @@ -372,11 +400,20 @@ describe(KbqScrollbar.name, () => { expect(bottomSpy).toHaveBeenCalled(); }); - it('adds the disable-interaction class when kbqScrollbarDisableInteraction is set', () => { - const fixture = createComponent(TestScrollbarDisableInteractionHost); + it('adds the disable-drag class when kbqScrollbarDisableDrag is set, independent of disable-click', () => { + const fixture = createComponent(TestScrollbarDisableDragHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + expect(host.classList.contains('kbq-private-scrollbar_disable-drag')).toBe(true); + expect(host.classList.contains('kbq-private-scrollbar_disable-click')).toBe(false); + }); + + it('adds the disable-click class when kbqScrollbarDisableClick is set, independent of disable-drag', () => { + const fixture = createComponent(TestScrollbarDisableClickHost); const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; - expect(host.classList.contains('kbq-private-scrollbar_disable-interaction')).toBe(true); + expect(host.classList.contains('kbq-private-scrollbar_disable-click')).toBe(true); + expect(host.classList.contains('kbq-private-scrollbar_disable-drag')).toBe(false); }); it('does not create track/thumb on a coarse pointer', () => { @@ -519,6 +556,51 @@ describe(KbqScrollbar.name, () => { expect(thumb.style.height).toBe('60px'); }); + + it("publishes the host's own computed padding as CSS custom properties consumed by the track positioning rules", () => { + const fixture = createComponent(TestScrollbarPaddingHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + fixture.componentInstance.scrollbar().update(); + + expect(host.style.getPropertyValue('--kbq-private-scrollbar-host-padding-top')).toBe('10px'); + expect(host.style.getPropertyValue('--kbq-private-scrollbar-host-padding-right')).toBe('20px'); + expect(host.style.getPropertyValue('--kbq-private-scrollbar-host-padding-bottom')).toBe('30px'); + expect(host.style.getPropertyValue('--kbq-private-scrollbar-host-padding-left')).toBe('40px'); + }); + + it('re-reads the host padding on every recompute(), picking up a runtime change with no resize of its own', () => { + const fixture = createComponent(TestScrollbarPaddingHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + fixture.componentInstance.scrollbar().update(); + expect(host.style.getPropertyValue('--kbq-private-scrollbar-host-padding-top')).toBe('10px'); + + host.style.padding = '5px'; + fixture.componentInstance.scrollbar().update(); + + expect(host.style.getPropertyValue('--kbq-private-scrollbar-host-padding-top')).toBe('5px'); + }); + + it('does not publish host padding CSS custom properties under native: true — there is no track to position', () => { + const fixture = createComponent(TestScrollbarPaddingHost, [ + { provide: KBQ_SCROLLBAR_CONFIG, useValue: { native: true } } + ]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + fixture.componentInstance.scrollbar().update(); + + expect(host.style.getPropertyValue('--kbq-private-scrollbar-host-padding-top')).toBe(''); + }); + + it('does not publish host padding CSS custom properties on a coarse pointer — there is no track to position', () => { + const fixture = createComponent(TestScrollbarPaddingHost, [coarsePointerWindowProvider]); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + + fixture.componentInstance.scrollbar().update(); + + expect(host.style.getPropertyValue('--kbq-private-scrollbar-host-padding-top')).toBe(''); + }); }); describe('drag interaction', () => { @@ -580,8 +662,8 @@ describe(KbqScrollbar.name, () => { expect(scrollEl.scrollTop).toBe(200); }); - it('disableInteraction blocks both thumb drag and track jump-to-click', () => { - const fixture = createComponent(TestScrollbarDisableInteractionHost); + it('disableDrag blocks thumb drag but leaves track jump-to-click working as a one-shot, not a continued drag', () => { + const fixture = createComponent(TestScrollbarDisableDragHost); const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; const scrollEl = getAutoViewport(host); const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; @@ -592,10 +674,39 @@ describe(KbqScrollbar.name, () => { thumb.dispatchEvent(new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true })); expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); + expect(scrollEl.scrollTop).toBe(0); + + track.dispatchEvent(new MouseEvent('pointerdown', { clientY: 53, bubbles: true, cancelable: true })); + // The jump itself still happens... + expect(scrollEl.scrollTop).toBe(200); + // ...but, unlike an ordinary track click, it must not arm a continued drag — otherwise + // holding the mouse down past the jump would keep following the pointer exactly like a + // real drag, despite disableDrag. + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); + + document.dispatchEvent(new MouseEvent('pointermove', { clientY: 90, buttons: 1, cancelable: true })); + expect(scrollEl.scrollTop).toBe(200); + }); + + it('disableClick blocks track jump-to-click but leaves thumb drag working', () => { + const fixture = createComponent(TestScrollbarDisableClickHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 100, scrollHeight: 500 }); + mockRects(track, thumb, 'vertical'); track.dispatchEvent(new MouseEvent('pointerdown', { clientY: 53, bubbles: true, cancelable: true })); expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(false); expect(scrollEl.scrollTop).toBe(0); + + thumb.dispatchEvent(new MouseEvent('pointerdown', { clientY: 3, bubbles: true, cancelable: true })); + expect(host.classList.contains('kbq-private-scrollbar_dragging')).toBe(true); + + document.dispatchEvent(new MouseEvent('pointermove', { clientY: 37, buttons: 1, cancelable: true })); + expect(scrollEl.scrollTop).toBe(200); }); it('ignores pointermove events from a different pointerId than the one that started the drag', () => { @@ -1584,5 +1695,43 @@ describe(KbqScrollbar.name, () => { expect(updateSpy).toHaveBeenCalled(); expect(thumb.style.top).not.toBe('0px'); }); + + it( + 'does NOT pick up a content-only growth automatically — this directive only recomputes on ' + + "scroll, on a resize of the scroll element's own box, or on an explicit update() call. " + + 'A `scrollHeight` change with no box-size change (e.g. appending rows to a list) fires ' + + 'neither, so the thumb is left stale until something calls update() for it', + () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 200, scrollHeight: 500 }); + setMetrics(track, { clientHeight: 206 }); + fixture.componentInstance.scrollbar().update(); + + // ratio = ceil(200/500*100)/100 = 0.4; thumbSize = max(0.4*200, 32) = 80. + expect(thumb.style.height).toBe('80px'); + + // Simulate content being appended: scrollHeight grows, clientHeight (the scroll + // element's own box size) does not — this is exactly the shape of change a real + // ResizeObserver never reports, since it only fires on box-size changes, not on + // scrollHeight/scrollWidth. No scroll event happens either. + setMetrics(scrollEl, { scrollHeight: 1000 }); + + // Nothing re-measures on its own: no scroll fired, no resize fired, and no manual + // update() was called — by design, there's no MutationObserver watching content + // mutations here, so the thumb stays stale until update() is called explicitly. + expect(thumb.style.height).toBe('80px'); + + fixture.componentInstance.scrollbar().update(); + + // ratio = ceil(200/1000*100)/100 = 0.2; thumbSize = max(0.2*200, 32) = 40 — only + // reached once something explicitly calls update(). + expect(thumb.style.height).toBe('40px'); + } + ); }); }); diff --git a/packages/components/scrollbar/private/scrollbar.ts b/packages/components/scrollbar/private/scrollbar.ts index 6fdb051050..6aff2426fc 100644 --- a/packages/components/scrollbar/private/scrollbar.ts +++ b/packages/components/scrollbar/private/scrollbar.ts @@ -30,6 +30,7 @@ import { OutputEmitterRef, Provider, Renderer2, + RendererStyleFlags2, signal, ViewEncapsulation, WritableSignal @@ -83,14 +84,16 @@ export type KbqScrollbarConfig = Partial<{ * could change per instance at runtime wouldn't have anything to react to anyway. */ native: boolean; - /** Default for the `kbqScrollbarDisableInteraction` input. */ - disableInteraction: boolean; + /** Default for the `kbqScrollbarDisableDrag` input. */ + disableDrag: boolean; + /** Default for the `kbqScrollbarDisableClick` input. */ + disableClick: boolean; }>; /** * Default values for `kbqScrollbar` inputs. `autoHideDelay` mirrors * `KBQ_SCROLLBAR_OPTIONS_DEFAULT_CONFIG` (`../scrollbar.types.ts`) to keep UX parity with the - * `overlayscrollbars`-based `kbq-scrollbar`. + * existing `kbq-scrollbar`. * * @docs-private */ @@ -99,7 +102,8 @@ export const KBQ_SCROLLBAR_DEFAULT_CONFIG: Required = { autoHideDelay: 100, floating: true, native: false, - disableInteraction: false + disableDrag: false, + disableClick: false }; /** @@ -135,6 +139,12 @@ type DragContext = { scrollRange: number; }; +/** What `measureAxis()` hands `paintAxis()` for one axis — see both. */ +type AxisMeasurement = { + scrollOffset: number; + scrollRange: number; +}; + /** * Component used to load the `.kbq-private-scrollbar` styles. */ @@ -142,6 +152,7 @@ type DragContext = { selector: 'scrollbar-style-loader', template: '', styleUrl: 'scrollbar.scss', + changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None }) class KbqScrollbarStyleLoader {} @@ -171,9 +182,11 @@ export class KbqScrollbarVirtualViewport { class KbqScrollbarViewport {} /** + * NOTE! Intended exclusively for use within this repository. + * * Dependency-free custom scrollbar directive. Draws its own track/thumb via `Renderer2` on top of - * the host's content — no third-party library, unlike `kbqScrollbar`/`kbq-scrollbar` - * (`../scrollbar.directive.ts`/`../scrollbar.component.ts`), which wrap `overlayscrollbars`. + * the host's content — no external dependency, unlike `kbqScrollbar`/`kbq-scrollbar` + * (`../scrollbar.directive.ts`/`../scrollbar.component.ts`), which wrap an external library. * * Usable as a plain attribute (`
`) or composed via `hostDirectives: * [KbqScrollbar]` in another component. @@ -187,7 +200,8 @@ class KbqScrollbarViewport {} host: { class: 'kbq-private-scrollbar', '[class.kbq-private-scrollbar_rtl]': 'rtl()', - '[class.kbq-private-scrollbar_disable-interaction]': 'disableInteraction()' + '[class.kbq-private-scrollbar_disable-drag]': 'disableDrag()', + '[class.kbq-private-scrollbar_disable-click]': 'disableClick()' }, // Redundant (but harmless) whenever `scrollElement` resolves to a `kbqScrollbarVirtualViewport` // or the auto-created viewport — both already provide their own `CdkScrollable`. Needed here @@ -246,11 +260,17 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { transform: booleanAttribute }); - /** Keeps scrolling working but disables drag-on-thumb and click-on-track. */ - readonly disableInteraction = input( - this.config.disableInteraction ?? KBQ_SCROLLBAR_DEFAULT_CONFIG.disableInteraction, - { alias: 'kbqScrollbarDisableInteraction', transform: booleanAttribute } - ); + /** Keeps scrolling working but disables drag-on-thumb — click-on-track keeps working independently, see `disableClick`. */ + readonly disableDrag = input(this.config.disableDrag ?? KBQ_SCROLLBAR_DEFAULT_CONFIG.disableDrag, { + alias: 'kbqScrollbarDisableDrag', + transform: booleanAttribute + }); + + /** Keeps scrolling working but disables click-on-track (jump-to-click) — drag-on-thumb keeps working independently, see `disableDrag`. */ + readonly disableClick = input(this.config.disableClick ?? KBQ_SCROLLBAR_DEFAULT_CONFIG.disableClick, { + alias: 'kbqScrollbarDisableClick', + transform: booleanAttribute + }); /** Emits the scroll position on every scroll event. */ readonly scrollChange = output({ alias: 'kbqScrollbarScrollChange' }); @@ -321,11 +341,18 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { // `--kbq-private-scrollbar-size-thumb-min-size`/`--kbq-private-scrollbar-size-track-padding`. private cssMinThumbSize = 32; private cssTrackPadding = 3; + // The scroll element's own box/content size — refreshed only on a "full" `recompute()` (see + // its doc comment), not on every scroll tick: scrolling alone can never change either, only a + // resize or a content mutation can. + private readonly cachedViewportSize: Record = { vertical: 0, horizontal: 0 }; + private readonly cachedContentSize: Record = { vertical: 0, horizontal: 0 }; private isCoarsePointer = false; private isVisible = false; private isPointerOver = false; private autoHideTimeoutId: ReturnType | undefined; - private dragContext: DragContext | null = null; + // Keyed by axis (not a single field) — the vertical and horizontal thumbs are independent, so + // a two-finger drag (one per axis) must track both without one overwriting the other. + private readonly dragContexts: Record = { vertical: null, horizontal: null }; /** * Auto-created scroll wrapper, only when there's no explicit `kbqScrollbarVirtualViewport` and the @@ -487,12 +514,17 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { * are handled automatically via `SharedResizeObserver`; call this manually when content grows * `scrollHeight`/`scrollWidth` without the scroll element's own box size changing — a resize * observer can't see that on its own (same caveat as `core/overflow-shadow/overflow-shadow.ts`). + * + * @docs-private */ update(): void { this.recompute(); } - /** @docs-private Implementation of `KbqOverflowShadowSource`. */ + /** + * Implementation of `KbqOverflowShadowSource`. + * @docs-private + */ getScrollElement(): HTMLElement | null { return this.scrollElement; } @@ -518,7 +550,10 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.scrollSubject.next(); - this.recompute(); + // `syncPadding: false` — see `recompute()`'s doc comment. `scroll` is this + // directive's highest-frequency, least-throttled event; re-reading the host's own + // (effectively static) padding on every tick isn't worth the forced style read. + this.recompute(false); this.onUserScroll(); }); } @@ -620,7 +655,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { fromEvent(thumb, 'pointerdown') .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((event) => { - if (this.disableInteraction() || !this.isPrimaryPointerDown(event)) return; + if (this.disableDrag() || !this.isPrimaryPointerDown(event)) return; this.beginInteraction(axis, track, thumb, event, false); }); @@ -630,42 +665,62 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { fromEvent(track, 'pointerdown') .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((event) => { - if (this.disableInteraction() || event.target === thumb || !this.isPrimaryPointerDown(event)) return; + if (this.disableClick() || event.target === thumb || !this.isPrimaryPointerDown(event)) return; this.beginInteraction(axis, track, thumb, event, true); }); } + // The vertical/horizontal thumbs drag independently, so a pointer event is matched back to + // whichever axis armed it, not just "the" drag. + private axisForPointerId(pointerId: number): Axis | null { + if (this.dragContexts.vertical?.pointerId === pointerId) return 'vertical'; + if (this.dragContexts.horizontal?.pointerId === pointerId) return 'horizontal'; + + return null; + } + + private get isDragging(): boolean { + return !!this.dragContexts.vertical || !!this.dragContexts.horizontal; + } + private wireGlobalDragListeners(): void { - fromEvent(this.document, 'pointermove') + // None of these three ever call `preventDefault()` (only the `pointerdown` handlers in + // `wireTrackInteraction` do, via `beginInteraction()`) — marking them passive lets the + // browser treat them as non-blocking for compositor/touch-scroll purposes. + fromEvent(this.document, 'pointermove', { passive: true }) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((event) => { - if (!this.dragContext || event.pointerId !== this.dragContext.pointerId) return; + const axis = this.axisForPointerId(event.pointerId); + + if (!axis) return; if (!event.buttons) { - this.endDrag(); + this.endDrag(axis); return; } - this.applyDrag(this.dragContext.axis === 'vertical' ? event.clientY : event.clientX); + this.applyDrag(axis, axis === 'vertical' ? event.clientY : event.clientX); }); - fromEvent(this.document, 'pointerup') + fromEvent(this.document, 'pointerup', { passive: true }) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((event) => { - if (!this.dragContext || event.pointerId !== this.dragContext.pointerId) return; - this.endDrag(); + const axis = this.axisForPointerId(event.pointerId); + + if (axis) this.endDrag(axis); }); // A gesture can be interrupted outside the normal pointerup path — e.g. a touch drag cut // short by an OS-level gesture, or the browser revoking pointer capture. Without this, - // `dragContext`/the `_dragging` class/`user-select: none` would be stuck until some later, + // `dragContexts`/the `_dragging` class/`user-select: none` would be stuck until some later, // unrelated pointerup happens to carry the same `pointerId`. - fromEvent(this.document, 'pointercancel') + fromEvent(this.document, 'pointercancel', { passive: true }) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((event) => { - if (!this.dragContext || event.pointerId !== this.dragContext.pointerId) return; - this.endDrag(); + const axis = this.axisForPointerId(event.pointerId); + + if (axis) this.endDrag(axis); }); } @@ -694,7 +749,15 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { ? scrollEl.scrollHeight - scrollEl.clientHeight : scrollEl.scrollWidth - scrollEl.clientWidth; - this.dragContext = { + // Author CSS may set `scroll-behavior: smooth` on the scroll element — without overriding + // it here, every `applyDrag()` write below would animate toward the new position instead of + // jumping straight there, so the thumb would visibly lag behind the pointer for the whole + // drag. Only touched on the transition into the very first concurrently-active drag (a + // second axis joining an already-active one must not stomp on it), and restored once + // `endDrag()` sees every axis has ended. + if (!this.isDragging) this.renderer.setStyle(scrollEl, 'scrollBehavior', 'auto'); + + this.dragContexts[axis] = { axis, pointerId: event.pointerId, trackStart, @@ -704,12 +767,28 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { }; this.setVisible(true); + this.applyDrag(axis, pointerCoord); + + // `centerOnThumb` is only ever `true` for a track click (never a thumb grab — the thumb's + // own pointerdown handler already excludes calling this at all when `disableDrag()` is + // set). Without this, a track click while dragging is disabled would still perform its + // one-shot jump above, but then keep this axis' drag context armed — the always-on global + // `pointermove` listener (`wireGlobalDragListeners`) would then keep following the pointer + // for as long as the button stays down, behaving exactly like a real drag despite + // `disableDrag()`. Clearing it back out right after the jump keeps the click a discrete + // action, the same way `disableClick` alone already keeps drag a discrete action. + if (centerOnThumb && this.disableDrag()) { + this.dragContexts[axis] = null; + this.restoreScrollBehaviorIfIdle(); + + return; + } + this.renderer.addClass(this.hostElement, 'kbq-private-scrollbar_dragging'); - this.applyDrag(pointerCoord); } - private applyDrag(pointerCoord: number): void { - const ctx = this.dragContext; + private applyDrag(axis: Axis, pointerCoord: number): void { + const ctx = this.dragContexts[axis]; if (!ctx || ctx.trackTravel <= 0) return; @@ -717,7 +796,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { // Physical ratio: 0 at the track's physical start (left/top), 1 at its physical end. const ratio = clamp((pointerRelative - ctx.grabOffset) / ctx.trackTravel, 0, 1); - if (ctx.axis === 'vertical') { + if (axis === 'vertical') { this.scrollElement.scrollTop = ratio * ctx.scrollRange; } else if (this.rtl()) { // RTL `scrollLeft` runs 0 (physical right) to -scrollRange (physical left) — invert @@ -728,27 +807,39 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { } } - private endDrag(): void { - if (!this.dragContext) return; + private endDrag(axis: Axis): void { + if (!this.dragContexts[axis]) return; + + this.dragContexts[axis] = null; + + // The other axis can still be mid-drag (two-finger drag of both thumbs) — keep the + // `_dragging` state and defer visibility restoration until every axis has ended. + if (this.isDragging) return; - this.dragContext = null; this.renderer.removeClass(this.hostElement, 'kbq-private-scrollbar_dragging'); + this.restoreScrollBehaviorIfIdle(); // `beginInteraction()` force-shows the scrollbar regardless of mode — restore whatever the - // current mode's steady state actually is now that the drag is over, the same way a real - // scroll/pointerleave would. Without this it can stay visible forever: 'scroll' mode never - // got a `showTemporarily()` timer scheduled for this reveal (only a direct `setVisible`), - // and 'hover' mode never got a `pointerleave` if the pointer left while dragging (that - // listener skips hiding on purpose while a drag is in progress). - const mode = this.visibility(); - - if (mode === 'scroll') { + // current mode's steady state actually is now that every axis' drag has ended. 'scroll' + // mode gets its usual timed reveal via `showTemporarily()` (not an immediate hide); every + // other mode goes through the same steady-state logic the mode-change effect above uses + // (and skips while `isDragging`). + if (this.visibility() === 'scroll') { this.showTemporarily(); - } else if (mode === 'hover' && !this.isPointerOver) { - this.setVisible(false); + } else { + this.applySteadyVisibility(); } } + // Reverts the `scroll-behavior: auto` override `beginInteraction()` applies — a no-op guard + // since this is called from two places (the disableDrag "jump" early-return above, and the + // normal end-of-drag path below), only one of which is guaranteed to be the actual last axis. + private restoreScrollBehaviorIfIdle(): void { + if (this.isDragging) return; + + this.renderer.removeStyle(this.scrollElement, 'scrollBehavior'); + } + private wireVisibility(): void { fromEvent(this.hostElement, 'pointerenter') .pipe(takeUntilDestroyed(this.destroyRef)) @@ -763,7 +854,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { .subscribe(() => { this.isPointerOver = false; - if (this.visibility() === 'hover' && !this.dragContext) this.setVisible(false); + if (this.visibility() === 'hover' && !this.isDragging) this.setVisible(false); }); // Reacts to `kbqScrollbarVisibility` changing at runtime, not just its value at first @@ -773,27 +864,39 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { // mode's temporary reveal-on-scroll keeps coming from onUserScroll()/showTemporarily() — // 'hidden' starts from the same steady state but never gets revealed from anywhere. // - // Clearing any pending auto-hide timeout here, before applying the new mode's steady - // state, matters: without it, a 'scroll'-mode reveal's timeout can outlive a switch to - // 'always' and fire later, hiding a scrollbar that's supposed to stay permanently visible. + // Skipped entirely while a drag is in progress — forcing the new mode's steady state here + // would otherwise yank the track/thumb visibility out from under an active drag (e.g. + // hiding it mid-gesture on a switch to 'hidden') even though the drag itself keeps + // scrolling regardless of visibility. `endDrag()` applies the (possibly now-different) + // steady state once the drag actually ends instead. effect( () => { - const mode = this.visibility(); + this.visibility(); - clearTimeout(this.autoHideTimeoutId); + if (this.isDragging) return; - if (mode === 'always') { - this.setVisible(true); - } else if (mode === 'hover') { - this.setVisible(this.isPointerOver); - } else { - this.setVisible(false); - } + this.applySteadyVisibility(); }, { injector: this.injector } ); } + // Shared by the mode-change effect above and by `endDrag()` — applies whatever the current + // mode's non-drag, non-transient visibility should be. + private applySteadyVisibility(): void { + const mode = this.visibility(); + + clearTimeout(this.autoHideTimeoutId); + + if (mode === 'always') { + this.setVisible(true); + } else if (mode === 'hover') { + this.setVisible(this.isPointerOver); + } else { + this.setVisible(false); + } + } + private onUserScroll(): void { this.emit(this.scrollChange, { top: this.scrollElement.scrollTop, left: this.scrollElement.scrollLeft }); @@ -804,7 +907,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { this.setVisible(true); clearTimeout(this.autoHideTimeoutId); this.autoHideTimeoutId = setTimeout(() => { - if (!this.dragContext) this.setVisible(false); + if (!this.isDragging) this.setVisible(false); }, this.autoHideDelay()); } @@ -816,21 +919,32 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { this.emit(this.visibilityChange, value); } - private recompute(): void { - const measurements = new Map(); + /** + * @param fullRefresh Whether to also re-read the two comparatively expensive, effectively- + * static-during-a-pure-scroll things: the host's own padding (`syncHostPadding()`) and the + * scroll element's own box/content size (`cachedViewportSize`/`cachedContentSize`, consumed by + * `measureAxis()`). Defaults to `true` — the one caller that opts out is `wireScroll()`, since + * `scroll` is by far this directive's highest-frequency, un-throttled event, and neither the + * host's author-set padding nor the scroll element's own box/content size has any real chance + * of changing from scrolling alone between one tick and the next. + */ + private recompute(fullRefresh = true): void { + const measurements = new Map(); for (const axis of AXES) { - const measurement = this.measureAxis(axis); + const measurement = this.measureAxis(axis, fullRefresh); if (measurement) measurements.set(axis, measurement); } - // Corner-avoidance only makes sense between two custom tracks — a no-op anyway without - // them, but skip it outright under `native`/coarse pointer. Must run before `paintAxis()` - // below, not after: it can shrink a track's real size via CSS, and `paintAxis()` measures - // that size to place the thumb — sizing it first would size/position the thumb against a - // stale, pre-shrink track on the very frame content starts overflowing both axes. + // Both only make sense with a custom track/thumb to position — a no-op anyway without + // them, but skip outright under `native`/coarse pointer. Must run before `paintAxis()` + // below, not after: both can shrink a track's real size via CSS (host padding insets it, + // corner-avoidance shrinks it further), and `paintAxis()` measures that size to place the + // thumb — sizing it first would size/position the thumb against a stale, pre-shrink track + // on the very frame content starts overflowing, or the very frame host padding changes. if (!this.native && !this.isCoarsePointer) { + if (fullRefresh) this.syncHostPadding(); this.updateCornerAvoidance(); } @@ -841,6 +955,34 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { this.emit(this.updated, undefined); } + /** + * Publishes the host's own computed padding as CSS custom properties consumed by + * `scrollbar.scss`'s track positioning rules — the track is `position: absolute` directly on + * the host, so without this it insets by 0 regardless of any padding the host itself carries, + * leaving the track sitting flush with the host's border edge instead of its actual content + * edge (a gap of empty padding between the track and the real content it's supposed to overlay). + * Read fresh on every `recompute()` rather than once at init, since author CSS can change the + * host's padding at runtime (e.g. a responsive class) with no resize of the host's own box to + * otherwise trigger a re-read. + */ + private syncHostPadding(): void { + const style = this.window.getComputedStyle(this.hostElement); + + this.setHostPaddingVar('top', style.paddingTop); + this.setHostPaddingVar('right', style.paddingRight); + this.setHostPaddingVar('bottom', style.paddingBottom); + this.setHostPaddingVar('left', style.paddingLeft); + } + + private setHostPaddingVar(side: 'top' | 'right' | 'bottom' | 'left', value: string): void { + this.renderer.setStyle( + this.hostElement, + `--kbq-private-scrollbar-host-padding-${side}`, + value, + RendererStyleFlags2.DashCase + ); + } + /** * Marks each track with whether the other one is currently also visible, so `scrollbar.scss` * can shorten its length by the other's thickness — otherwise they'd overlap by one @@ -882,12 +1024,22 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { * `updateCornerAvoidance()` between this and `paintAxis()`, since corner avoidance can shrink * a track's real size via CSS, and the thumb needs to be sized against that final size, not * whatever it measured before corner avoidance ran. + * + * @param fullRefresh Whether to re-read the scroll element's own box/content size + * (`clientHeight`/`clientWidth`/`scrollHeight`/`scrollWidth`) or reuse whatever `cachedViewportSize`/ + * `cachedContentSize` already hold from the last time it was — see `recompute()`'s doc comment. */ - private measureAxis(axis: Axis): { scrollOffset: number; scrollRange: number } | null { + private measureAxis(axis: Axis, fullRefresh: boolean): AxisMeasurement | null { const scrollEl = this.scrollElement; const isVertical = axis === 'vertical'; - const viewportSize = isVertical ? scrollEl.clientHeight : scrollEl.clientWidth; - const contentSize = isVertical ? scrollEl.scrollHeight : scrollEl.scrollWidth; + + if (fullRefresh) { + this.cachedViewportSize[axis] = isVertical ? scrollEl.clientHeight : scrollEl.clientWidth; + this.cachedContentSize[axis] = isVertical ? scrollEl.scrollHeight : scrollEl.scrollWidth; + } + + const viewportSize = this.cachedViewportSize[axis]; + const contentSize = this.cachedContentSize[axis]; const rawScrollOffset = isVertical ? scrollEl.scrollTop : scrollEl.scrollLeft; const overflows = isVertical ? this.verticalOverflows : this.horizontalOverflows; const track = this.tracks.get(axis); diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts index 49df866bd2..4deaff5a0f 100644 --- a/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts @@ -10,16 +10,23 @@ import { KbqToggleModule } from '@koobiq/components/toggle'; selector: 'private-scrollbar-disable-interaction-example', imports: [KbqScrollbar, KbqToggleModule, FormsModule], template: ` - Disable interaction +
+ Disable drag + Disable click +

- Drag & click for the scrollbar are {{ disableInteraction() ? 'disabled' : 'enabled' }} + Drag for the scrollbar is {{ disableDrag() ? 'disabled' : 'enabled' }} +

+

+ Click for the scrollbar is {{ disableClick() ? 'disabled' : 'enabled' }}

In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists @@ -52,6 +59,11 @@ import { KbqToggleModule } from '@koobiq/components/toggle'; padding: var(--kbq-size-l); } + .example-toggles { + display: flex; + gap: var(--kbq-size-l); + } + .example-scrollbar { overflow: auto; resize: both; @@ -73,5 +85,6 @@ import { KbqToggleModule } from '@koobiq/components/toggle'; changeDetection: ChangeDetectionStrategy.OnPush }) export class PrivateScrollbarDisableInteractionExample { - protected readonly disableInteraction = model(true); + protected readonly disableDrag = model(true); + protected readonly disableClick = model(false); } diff --git a/packages/e2e/routes.ts b/packages/e2e/routes.ts index 18247fa6fd..f40a158f01 100644 --- a/packages/e2e/routes.ts +++ b/packages/e2e/routes.ts @@ -2,8 +2,11 @@ import { Routes } from '@angular/router'; import { E2eFormHorizontal } from 'packages/components/core/forms/e2e'; import { E2eTypographyStyles } from 'packages/components/core/styles/typography/e2e'; import { + E2ePrivateScrollbarContentMutation, E2ePrivateScrollbarDrag, + E2ePrivateScrollbarHostPadding, E2ePrivateScrollbarHoverVisibility, + E2ePrivateScrollbarKeyboard, E2ePrivateScrollbarScrollTo, E2ePrivateScrollbarStateAndStyle } from 'packages/components/scrollbar/private/e2e'; @@ -201,6 +204,9 @@ const components = [ E2ePrivateScrollbarDrag, E2ePrivateScrollbarScrollTo, E2ePrivateScrollbarHoverVisibility, + E2ePrivateScrollbarContentMutation, + E2ePrivateScrollbarHostPadding, + E2ePrivateScrollbarKeyboard, E2eRadioStateAndStyle, E2eProgressBarStateAndStyle, E2eProgressSpinnerStates, diff --git a/tools/public_api_guard/components/scrollbar-private.api.md b/tools/public_api_guard/components/scrollbar-private.api.md index 3efc66d46d..fde0c180e9 100644 --- a/tools/public_api_guard/components/scrollbar-private.api.md +++ b/tools/public_api_guard/components/scrollbar-private.api.md @@ -22,7 +22,8 @@ export const KBQ_SCROLLBAR_CONFIG: InjectionToken>; // @public @@ -32,7 +33,8 @@ export const KBQ_SCROLLBAR_DEFAULT_CONFIG: Required; export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { constructor(); readonly autoHideDelay: _angular_core.InputSignalWithTransform; - readonly disableInteraction: _angular_core.InputSignalWithTransform; + readonly disableClick: _angular_core.InputSignalWithTransform; + readonly disableDrag: _angular_core.InputSignalWithTransform; readonly floating: _angular_core.InputSignalWithTransform; getScrollElement(): HTMLElement | null; readonly initialized: OutputEmitterRef; @@ -60,7 +62,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { readonly visibility: _angular_core.InputSignal; readonly visibilityChange: OutputEmitterRef; // (undocumented) - static ɵdir: _angular_core.ɵɵDirectiveDeclaration; + static ɵdir: _angular_core.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: _angular_core.ɵɵFactoryDeclaration; } @@ -71,7 +73,8 @@ export type KbqScrollbarConfig = Partial<{ autoHideDelay: number; floating: boolean; native: boolean; - disableInteraction: boolean; + disableDrag: boolean; + disableClick: boolean; }>; // @public From 56aa4da8d786ed5b4bf8c7313159ad9ea47bbf77 Mon Sep 17 00:00:00 2001 From: Artem Belik Date: Fri, 31 Jul 2026 17:03:34 +0300 Subject: [PATCH 09/13] refactor: review v2 --- .../private/__screenshots__/01-light.png | Bin 7343 -> 8539 bytes .../scrollbar/private/e2e.playwright-spec.ts | 93 ++++++++++++++--- packages/components/scrollbar/private/e2e.ts | 31 +++++- .../scrollbar/private/scrollbar.spec.ts | 58 +++++++++++ .../components/scrollbar/private/scrollbar.ts | 97 ++++++++++++------ 5 files changed, 231 insertions(+), 48 deletions(-) diff --git a/packages/components/scrollbar/private/__screenshots__/01-light.png b/packages/components/scrollbar/private/__screenshots__/01-light.png index 1eea95b593d47818c0617225f41541ad0a92faeb..d539b74011e85c165c75e4c26fd4acfab1fd5ba7 100644 GIT binary patch literal 8539 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 z { await expect.poll(() => viewport.evaluate((el) => el.scrollTop)).toBeGreaterThan(0); }); + + test('kbqScrollbarDisableDrag + kbqScrollbarDisableClick together make the whole track inert', async ({ + page + }) => { + await page.goto('/E2ePrivateScrollbarDrag'); + const container = page.getByTestId('both-disabled'); + const viewport = getViewport(container); + const track = container.locator('.kbq-private-scrollbar-track_vertical'); + const thumb = track.locator('.kbq-private-scrollbar-thumb'); + + const trackBox = await track.boundingBox(); + + if (!trackBox) throw new Error('bounding box is null'); + + // Click on the track away from the thumb — with only one flag set this would jump-to- + // click ([scrollbar.scss] makes the track `pointer-events: none` entirely once both + // flags are set, not just individually), so nothing should happen here at all. + await page.mouse.click(trackBox.x + trackBox.width / 2, trackBox.y + trackBox.height - 5); + + expect(await viewport.evaluate((el) => el.scrollTop)).toBe(0); + + // A real drag directly on the thumb too — same result: `pointer-events: none` on the + // ancestor track means the thumb never receives the pointerdown either. + const thumbBox = await thumb.boundingBox(); + + if (!thumbBox) throw new Error('bounding box is null'); + + await page.mouse.move(thumbBox.x + thumbBox.width / 2, thumbBox.y + thumbBox.height / 2); + await page.mouse.down(); + await page.mouse.move(thumbBox.x + thumbBox.width / 2, thumbBox.y + thumbBox.height / 2 + 40, { + steps: 5 + }); + await page.mouse.up(); + + expect(await viewport.evaluate((el) => el.scrollTop)).toBe(0); + }); }); test.describe('E2ePrivateScrollbarScrollTo', () => { @@ -187,25 +223,58 @@ test.describe('KbqScrollbar (private)', () => { }); test.describe('E2ePrivateScrollbarHostPadding', () => { - test("insets the track by the host's own padding instead of sitting flush with its border edge", async ({ - page - }) => { + // Host padding is asymmetric (10/25/15/35, see `E2ePrivateScrollbarHostPadding`'s styles) — + // deliberately, so a bug that swaps two sides wouldn't go unnoticed the way it could with a + // single uniform padding value. Both tracks overflow here, so corner-avoidance is active; + // every assertion below picks the one edge per track corner-avoidance never touches (only + // the vertical track's `bottom` and the horizontal track's `right` — or `left` in RTL — are + // ever adjusted to dodge the other track). + test("insets the vertical and horizontal tracks by the host's own asymmetric padding", async ({ page }) => { await page.goto('/E2ePrivateScrollbarHostPadding'); const container = page.getByTestId('host-padding'); - const track = container.locator('.kbq-private-scrollbar-track_vertical'); + const verticalTrack = container.locator('.kbq-private-scrollbar-track_vertical'); + const horizontalTrack = container.locator('.kbq-private-scrollbar-track_horizontal'); const containerBox = await container.boundingBox(); - const trackBox = await track.boundingBox(); + const verticalBox = await verticalTrack.boundingBox(); + const horizontalBox = await horizontalTrack.boundingBox(); + + if (!containerBox || !verticalBox || !horizontalBox) throw new Error('bounding box is null'); + + expect(verticalBox.y).toBeCloseTo(containerBox.y + 10, 0); + expect(verticalBox.x + verticalBox.width).toBeCloseTo(containerBox.x + containerBox.width - 25, 0); + + expect(horizontalBox.x).toBeCloseTo(containerBox.x + 35, 0); + expect(horizontalBox.y + horizontalBox.height).toBeCloseTo(containerBox.y + containerBox.height - 15, 0); + }); + + test("flips the vertical track to the host's logical-left padding under RTL", async ({ page }) => { + await page.goto('/E2ePrivateScrollbarHostPadding'); + + // `host-padding-rtl` is wrapped in CDK's `[dir]` directive (`

`), which + // provides `Directionality` locally via DI from its own attribute — global + // `document.documentElement.dir` isn't an option here, since `Directionality` only ever + // reads it once, at its own construction (app bootstrap), so setting it later (e.g. via + // `page.evaluate()`, or even `addInitScript`, which this dev server's own bootstrapping + // ends up clobbering before Angular reads it) wouldn't be picked up at all. + const container = page.getByTestId('host-padding-rtl'); + const verticalTrack = container.locator('.kbq-private-scrollbar-track_vertical'); + const horizontalTrack = container.locator('.kbq-private-scrollbar-track_horizontal'); + + const containerBox = await container.boundingBox(); + const verticalBox = await verticalTrack.boundingBox(); + const horizontalBox = await horizontalTrack.boundingBox(); - if (!containerBox || !trackBox) throw new Error('bounding box is null'); + if (!containerBox || !verticalBox || !horizontalBox) throw new Error('bounding box is null'); - // The host has 20px of its own padding on every side — the track's right edge should - // align with the padding box (the real content edge), not the host's border edge 20px - // further out. - const expectedRight = containerBox.x + containerBox.width - 20; + // RTL moves the vertical track to the logical-left (host-padding-left) edge instead of + // the right. + expect(verticalBox.x).toBeCloseTo(containerBox.x + 35, 0); - expect(trackBox.x + trackBox.width).toBeCloseTo(expectedRight, 0); - expect(trackBox.y).toBeCloseTo(containerBox.y + 20, 0); + // The horizontal track's corner-avoidance-adjusted side mirrors too (becomes `left` + // instead of `right`) — its `right`/`bottom` stay the safe, untouched edges here. + expect(horizontalBox.x + horizontalBox.width).toBeCloseTo(containerBox.x + containerBox.width - 25, 0); + expect(horizontalBox.y + horizontalBox.height).toBeCloseTo(containerBox.y + containerBox.height - 15, 0); }); }); diff --git a/packages/components/scrollbar/private/e2e.ts b/packages/components/scrollbar/private/e2e.ts index ed5be643e8..caf9d7195f 100644 --- a/packages/components/scrollbar/private/e2e.ts +++ b/packages/components/scrollbar/private/e2e.ts @@ -1,3 +1,4 @@ +import { Dir } from '@angular/cdk/bidi'; import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { KbqScrollbar, kbqScrollbarConfigProvider } from './scrollbar'; @@ -76,6 +77,17 @@ export class E2ePrivateScrollbarStateAndStyle {} >

{{ content }}

+ +
+

{{ content }}

+
`, styles: ` :host { @@ -260,11 +272,23 @@ export class E2ePrivateScrollbarContentMutation { */ @Component({ selector: 'e2e-private-scrollbar-host-padding', - imports: [KbqScrollbar], + imports: [KbqScrollbar, Dir], template: `

{{ content }}

+ + +
+
+

{{ content }}

+
+
`, styles: ` :host { @@ -276,9 +300,12 @@ export class E2ePrivateScrollbarContentMutation { box-sizing: border-box; width: 200px; height: 100px; - padding: 20px; + /* Deliberately asymmetric (not a single uniform value) — otherwise a bug that swaps + two sides (e.g. left/right, or physical/logical in RTL) could go unnoticed. */ + padding: 10px 25px 15px 35px; border-radius: var(--kbq-size-border-radius); background-color: var(--kbq-background-bg-secondary); + margin-bottom: var(--kbq-size-l); } p { diff --git a/packages/components/scrollbar/private/scrollbar.spec.ts b/packages/components/scrollbar/private/scrollbar.spec.ts index 2038e18942..7bc54bc28c 100644 --- a/packages/components/scrollbar/private/scrollbar.spec.ts +++ b/packages/components/scrollbar/private/scrollbar.spec.ts @@ -482,6 +482,22 @@ describe(KbqScrollbar.name, () => { expect(thumb.style.top).toBe('3px'); }); + it('clamps the thumb to 0 rather than a negative size when the track is shorter than 2 * cssTrackPadding', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const thumb = host.querySelector('.kbq-private-scrollbar-thumb') as HTMLElement; + + setMetrics(scrollEl, { scrollTop: 0, clientHeight: 200, scrollHeight: 500 }); + // 4px track: shorter than 2 * the 3px cssTrackPadding default (6px), so travelLength + // would go negative (4 - 6 = -2) without the clamp. + setMetrics(track, { clientHeight: 4 }); + fixture.componentInstance.scrollbar().update(); + + expect(thumb.style.height).toBe('0px'); + }); + it("keeps the thumb inset from the track's own start/end edges by cssTrackPadding at both scroll extremes", () => { const fixture = createComponent(TestScrollbarHost); const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; @@ -1410,6 +1426,48 @@ describe(KbqScrollbar.name, () => { expect(bottomSpy).toHaveBeenCalled(); }); + it('fires reachTop and reachBottom together when content shrinks to no longer overflow, mid-scroll', () => { + const fixture = createComponent(TestScrollbarHost); + const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; + const scrollEl = getAutoViewport(host); + const track = host.querySelector('.kbq-private-scrollbar-track_vertical') as HTMLElement; + const topSpy = jest.fn(); + const bottomSpy = jest.fn(); + + fixture.componentInstance.scrollbar().reachTop.subscribe(topSpy); + fixture.componentInstance.scrollbar().reachBottom.subscribe(bottomSpy); + + // Mid-scroll — neither edge reached yet. + setMetrics(scrollEl, { scrollTop: 200, clientHeight: 100, scrollHeight: 500 }); + setMetrics(track, { clientHeight: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(topSpy).not.toHaveBeenCalled(); + expect(bottomSpy).not.toHaveBeenCalled(); + + // Content shrinks to fit the viewport — nothing left to scroll, trivially "at both + // edges" at once, even though scrollTop itself never went anywhere near 0 or the max. + setMetrics(scrollEl, { scrollHeight: 100 }); + fixture.componentInstance.scrollbar().update(); + + expect(topSpy).toHaveBeenCalled(); + expect(bottomSpy).toHaveBeenCalled(); + }); + + it('does not fire reachTop/reachBottom on construction, before this axis has ever overflowed', () => { + const fixture = createComponent(TestScrollbarHost); + const topSpy = jest.fn(); + const bottomSpy = jest.fn(); + + fixture.componentInstance.scrollbar().reachTop.subscribe(topSpy); + fixture.componentInstance.scrollbar().reachBottom.subscribe(bottomSpy); + + fixture.componentInstance.scrollbar().update(); + + expect(topSpy).not.toHaveBeenCalled(); + expect(bottomSpy).not.toHaveBeenCalled(); + }); + it('reachStart / reachEnd fire for the horizontal axis in LTR, matching the physical left/right edges', () => { const fixture = createComponent(TestScrollbarDualOverflowHost); const host = fixture.debugElement.nativeElement.querySelector('[data-testid="host"]') as HTMLElement; diff --git a/packages/components/scrollbar/private/scrollbar.ts b/packages/components/scrollbar/private/scrollbar.ts index 6aff2426fc..d02df87b92 100644 --- a/packages/components/scrollbar/private/scrollbar.ts +++ b/packages/components/scrollbar/private/scrollbar.ts @@ -341,11 +341,6 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { // `--kbq-private-scrollbar-size-thumb-min-size`/`--kbq-private-scrollbar-size-track-padding`. private cssMinThumbSize = 32; private cssTrackPadding = 3; - // The scroll element's own box/content size — refreshed only on a "full" `recompute()` (see - // its doc comment), not on every scroll tick: scrolling alone can never change either, only a - // resize or a content mutation can. - private readonly cachedViewportSize: Record = { vertical: 0, horizontal: 0 }; - private readonly cachedContentSize: Record = { vertical: 0, horizontal: 0 }; private isCoarsePointer = false; private isVisible = false; private isPointerOver = false; @@ -722,6 +717,19 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { if (axis) this.endDrag(axis); }); + + // Mirrors `pointercancel` above for the rarer case pointer capture itself gets revoked + // mid-gesture (e.g. another element calls `setPointerCapture` for the same pointerId, or + // the browser does so on focus change) without a `pointerup`/`pointercancel` ever firing. + // Bubbles like the others, so `document` still sees it even though capture itself was set + // on the thumb/track (`beginInteraction()`). + fromEvent(this.document, 'lostpointercapture', { passive: true }) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((event) => { + const axis = this.axisForPointerId(event.pointerId); + + if (axis) this.endDrag(axis); + }); } private beginInteraction( @@ -733,6 +741,20 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { ): void { event.preventDefault(); + // Captures all further events for this pointerId to `captureTarget` regardless of where + // the pointer travels afterward (even outside the window, or over an iframe/native + // control) — without this, the pointer leaving the document before it's released can mean + // the eventual `pointerup` never reaches `document` at all, leaving the drag stuck open + // forever. `lostpointercapture` (handled in `wireGlobalDragListeners()`) is the safety net + // for the rarer case capture itself gets revoked mid-gesture. Feature-detected: some + // environments (this repo's jsdom-based Jest setup among them) don't implement + // `Element.setPointerCapture` — same caveat as `scrollTo()`. + const captureTarget = centerOnThumb ? track : thumb; + + if (typeof captureTarget.setPointerCapture === 'function') { + captureTarget.setPointerCapture(event.pointerId); + } + const isVertical = axis === 'vertical'; const trackRect = track.getBoundingClientRect(); const thumbRect = thumb.getBoundingClientRect(); @@ -920,19 +942,17 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { } /** - * @param fullRefresh Whether to also re-read the two comparatively expensive, effectively- - * static-during-a-pure-scroll things: the host's own padding (`syncHostPadding()`) and the - * scroll element's own box/content size (`cachedViewportSize`/`cachedContentSize`, consumed by - * `measureAxis()`). Defaults to `true` — the one caller that opts out is `wireScroll()`, since - * `scroll` is by far this directive's highest-frequency, un-throttled event, and neither the - * host's author-set padding nor the scroll element's own box/content size has any real chance - * of changing from scrolling alone between one tick and the next. + * @param syncPadding Whether to also re-read the host's own padding into the CSS custom + * properties `scrollbar.scss`'s track positioning rules consume (`syncHostPadding()`). + * Defaults to `true` — the one caller that opts out is `wireScroll()`, since `scroll` is by far + * this directive's highest-frequency, un-throttled event, and the host's own author-set + * padding has no real chance of changing on its own between one scroll tick and the next. */ - private recompute(fullRefresh = true): void { + private recompute(syncPadding = true): void { const measurements = new Map(); for (const axis of AXES) { - const measurement = this.measureAxis(axis, fullRefresh); + const measurement = this.measureAxis(axis); if (measurement) measurements.set(axis, measurement); } @@ -944,7 +964,7 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { // thumb — sizing it first would size/position the thumb against a stale, pre-shrink track // on the very frame content starts overflowing, or the very frame host padding changes. if (!this.native && !this.isCoarsePointer) { - if (fullRefresh) this.syncHostPadding(); + if (syncPadding) this.syncHostPadding(); this.updateCornerAvoidance(); } @@ -1024,31 +1044,32 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { * `updateCornerAvoidance()` between this and `paintAxis()`, since corner avoidance can shrink * a track's real size via CSS, and the thumb needs to be sized against that final size, not * whatever it measured before corner avoidance ran. - * - * @param fullRefresh Whether to re-read the scroll element's own box/content size - * (`clientHeight`/`clientWidth`/`scrollHeight`/`scrollWidth`) or reuse whatever `cachedViewportSize`/ - * `cachedContentSize` already hold from the last time it was — see `recompute()`'s doc comment. */ - private measureAxis(axis: Axis, fullRefresh: boolean): AxisMeasurement | null { + private measureAxis(axis: Axis): AxisMeasurement | null { const scrollEl = this.scrollElement; const isVertical = axis === 'vertical'; - - if (fullRefresh) { - this.cachedViewportSize[axis] = isVertical ? scrollEl.clientHeight : scrollEl.clientWidth; - this.cachedContentSize[axis] = isVertical ? scrollEl.scrollHeight : scrollEl.scrollWidth; - } - - const viewportSize = this.cachedViewportSize[axis]; - const contentSize = this.cachedContentSize[axis]; + const viewportSize = isVertical ? scrollEl.clientHeight : scrollEl.clientWidth; + const contentSize = isVertical ? scrollEl.scrollHeight : scrollEl.scrollWidth; const rawScrollOffset = isVertical ? scrollEl.scrollTop : scrollEl.scrollLeft; const overflows = isVertical ? this.verticalOverflows : this.horizontalOverflows; const track = this.tracks.get(axis); if (contentSize <= viewportSize) { if (track) this.renderer.setStyle(track, 'display', 'none'); - // Nothing to scroll on this axis — `isTopReached()`/`isBottomReached()`-family signals short- - // circuit to `true` off this flag, without touching the real edge-position signals - // below (those stay at whatever they last genuinely measured). + + // Nothing to scroll on this axis — trivially "at both edges" at once. The public + // `isTopReached()`/`isBottomReached()`-family signals already short-circuit to `true` + // off `overflows` alone regardless of what's passed here, but the discrete `reach*` + // outputs only ever fire from `checkReachedEdges()` — without calling it on a genuine + // overflowing→not-overflowing transition too, a consumer listening to the outputs (not + // the signals) would never hear about content shrinking to fit. Gated on the *previous* + // `overflows()` value specifically: this axis simply never having overflowed yet (its + // state from construction, before any real measurement) must NOT also count as such a + // transition — that would fire `reach*` before anyone's had a chance to subscribe, and + // permanently mark both edges "already reached" so a later, genuine first overflow + // never fires them at all. + if (overflows()) this.checkReachedEdges(axis, 0, 0); + overflows.set(false); return null; @@ -1097,8 +1118,11 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { // The thumb travels within the track minus a `cssTrackPadding` gap at *each* end, so it // never sits flush against the track's own start/end edge — matching the CSS insets // already applied on its cross-axis (`scrollbar.scss`'s `left`/`right`/`top`/`bottom` - // rules), just computed here since this axis is otherwise entirely JS-driven. - const travelLength = trackLength - 2 * this.cssTrackPadding; + // rules), just computed here since this axis is otherwise entirely JS-driven. Clamped to + // `0` (not left negative) for a track shorter than `2 * cssTrackPadding` — `Math.min(travelLength, + // ...)` below already collapses `thumbSize` to `0` correctly once this is non-negative; + // left negative, it would instead flow through as a bogus negative thumb size. + const travelLength = Math.max(0, trackLength - 2 * this.cssTrackPadding); // Round the ratio up so the thumb never reads as undersized from float error. const ratio = Math.min(1, Math.ceil((viewportSize / (viewportSize + scrollRange)) * 100) / 100); @@ -1129,7 +1153,12 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { private checkReachedEdges(axis: Axis, scrollOffset: number, scrollRange: number): void { const atPhysicalStart = scrollOffset <= 0; - const atPhysicalEnd = scrollRange > 0 && Math.round(scrollOffset) >= Math.round(scrollRange); + // No `scrollRange > 0` guard: the one caller passing `scrollRange: 0` (`measureAxis()`'s + // no-longer-overflowing branch) means "nothing to scroll," which — same as + // `atPhysicalStart` above — is trivially also "at the end." Every other caller only ever + // reaches this with a genuinely positive `scrollRange` already, so this doesn't change + // anything for them. + const atPhysicalEnd = Math.round(scrollOffset) >= Math.round(scrollRange); const [startState, endState] = this.edgeSignals(axis); // Vertical has no RTL concept — top is always physically first. Horizontal's physical From ab9755e20278261ec92962b7ba88fd4e7a5a61ea Mon Sep 17 00:00:00 2001 From: Artem Belik Date: Fri, 31 Jul 2026 17:25:50 +0300 Subject: [PATCH 10/13] refactor: api --- tools/public_api_guard/components/scrollbar-private.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/public_api_guard/components/scrollbar-private.api.md b/tools/public_api_guard/components/scrollbar-private.api.md index fde0c180e9..fd6243a438 100644 --- a/tools/public_api_guard/components/scrollbar-private.api.md +++ b/tools/public_api_guard/components/scrollbar-private.api.md @@ -80,7 +80,7 @@ export type KbqScrollbarConfig = Partial<{ // @public export const kbqScrollbarConfigProvider: (config: KbqScrollbarConfig) => Provider; -// @public (undocumented) +// @public export class KbqScrollbarModule { // (undocumented) static ɵfac: _angular_core.ɵɵFactoryDeclaration; From 67916c88bc42085951b29c03c2518f77646b69d8 Mon Sep 17 00:00:00 2001 From: Artem Belik Date: Fri, 31 Jul 2026 17:30:04 +0300 Subject: [PATCH 11/13] refactor: examples --- .../private-scrollbar-disable-interaction-example.ts | 1 - .../private-scrollbar-rtl/private-scrollbar-rtl-example.ts | 2 +- .../private-scrollbar-scroll-to-example.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts index 4deaff5a0f..b610c4c4e2 100644 --- a/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-disable-interaction/private-scrollbar-disable-interaction-example.ts @@ -18,7 +18,6 @@ import { KbqToggleModule } from '@koobiq/components/toggle';
diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-rtl/private-scrollbar-rtl-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-rtl/private-scrollbar-rtl-example.ts index f568a32f14..805713e474 100644 --- a/packages/docs-examples/components/scrollbar/private-scrollbar-rtl/private-scrollbar-rtl-example.ts +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-rtl/private-scrollbar-rtl-example.ts @@ -26,7 +26,7 @@ import { KbqScrollbar } from '@koobiq/components/scrollbar/private';
-
+

In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists of an attacker submitting many possible keys or passwords with the hope of eventually diff --git a/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts b/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts index c30b08917e..63640eb874 100644 --- a/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts +++ b/packages/docs-examples/components/scrollbar/private-scrollbar-scroll-to/private-scrollbar-scroll-to-example.ts @@ -30,7 +30,7 @@ import { KbqScrollbar } from '@koobiq/components/scrollbar/private';

-
+

In cryptography, a brute-force attack or exhaustive key search is a cryptanalytic attack that consists of an attacker submitting many possible keys or passwords with the hope of eventually guessing From 90faccc9687288aca4d1b48154619cfdc669ef0f Mon Sep 17 00:00:00 2001 From: artembelik <24925224+artembelik@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:51:01 +0000 Subject: [PATCH 12/13] test: updated e2e snapshots --- .../icon/__screenshots__/02-light.png | Bin 101966 -> 101960 bytes .../private/__screenshots__/01-light.png | Bin 8539 -> 7343 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/packages/components/icon/__screenshots__/02-light.png b/packages/components/icon/__screenshots__/02-light.png index 360868b8c68ba5e2f7e8bcf79d992615934adf7a..ee5afbb1ff30b2cee01fdd743be1d50a652c455f 100644 GIT binary patch delta 28707 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 G;lBVpccxGP diff --git a/packages/components/scrollbar/private/__screenshots__/01-light.png b/packages/components/scrollbar/private/__screenshots__/01-light.png index d539b74011e85c165c75e4c26fd4acfab1fd5ba7..1eea95b593d47818c0617225f41541ad0a92faeb 100644 GIT binary patch literal 7343 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 Date: Mon, 3 Aug 2026 15:14:08 +0300 Subject: [PATCH 13/13] refactor: review --- .../scrollbar/private/scrollbar.spec.ts | 69 +++++++++++++++++++ .../components/scrollbar/private/scrollbar.ts | 12 +++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/packages/components/scrollbar/private/scrollbar.spec.ts b/packages/components/scrollbar/private/scrollbar.spec.ts index 7bc54bc28c..1b63309ff9 100644 --- a/packages/components/scrollbar/private/scrollbar.spec.ts +++ b/packages/components/scrollbar/private/scrollbar.spec.ts @@ -219,6 +219,29 @@ class TestScrollbarVirtualHost { readonly items = Array.from({ length: 100 }, (_, i) => i); } +@Component({ + selector: 'test-scrollbar-virtual-horizontal-host', + imports: [KbqScrollbar, KbqScrollbarVirtualViewport, ScrollingModule], + template: ` +

+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +class TestScrollbarVirtualHorizontalHost { + readonly scrollbar = viewChild.required(KbqScrollbar); + readonly items = Array.from({ length: 100 }, (_, i) => i); +} + @Component({ selector: 'test-scrollbar-composed-host', template: ` @@ -1108,6 +1131,52 @@ describe(KbqScrollbar.name, () => { expect(scrollToOffsetSpy).toHaveBeenCalledWith(123, 'auto'); }); + + it('scrollStart/scrollEnd do NOT call scrollToOffset on a (default) vertical viewport — `left` is not this axis', () => { + const fixture = createComponent(TestScrollbarVirtualHost); + const scrollToOffsetSpy = jest + .spyOn(CdkVirtualScrollViewport.prototype, 'scrollToOffset') + .mockImplementation(() => {}); + + fixture.componentInstance.scrollbar().scrollStart(); + fixture.componentInstance.scrollbar().scrollEnd(); + + expect(scrollToOffsetSpy).not.toHaveBeenCalled(); + }); + + it('scrollToTop/scrollToBottom do NOT call scrollToOffset on an orientation="horizontal" viewport — `top` is not this axis', () => { + const fixture = createComponent(TestScrollbarVirtualHorizontalHost); + const scrollToOffsetSpy = jest + .spyOn(CdkVirtualScrollViewport.prototype, 'scrollToOffset') + .mockImplementation(() => {}); + + fixture.componentInstance.scrollbar().scrollToTop(); + fixture.componentInstance.scrollbar().scrollToBottom(); + + expect(scrollToOffsetSpy).not.toHaveBeenCalled(); + }); + + it('routes scrollTo({ left }) through scrollToOffset when the delegated viewport is orientation="horizontal"', () => { + const fixture = createComponent(TestScrollbarVirtualHorizontalHost); + const scrollToOffsetSpy = jest + .spyOn(CdkVirtualScrollViewport.prototype, 'scrollToOffset') + .mockImplementation(() => {}); + + fixture.componentInstance.scrollbar().scrollTo({ left: 77 }); + + expect(scrollToOffsetSpy).toHaveBeenCalledWith(77, 'auto'); + }); + + it('scrollTo({ top }) is ignored (not just defaulted to 0) against an orientation="horizontal" viewport', () => { + const fixture = createComponent(TestScrollbarVirtualHorizontalHost); + const scrollToOffsetSpy = jest + .spyOn(CdkVirtualScrollViewport.prototype, 'scrollToOffset') + .mockImplementation(() => {}); + + fixture.componentInstance.scrollbar().scrollTo({ top: 999 }); + + expect(scrollToOffsetSpy).not.toHaveBeenCalled(); + }); }); describe('RTL', () => { diff --git a/packages/components/scrollbar/private/scrollbar.ts b/packages/components/scrollbar/private/scrollbar.ts index d02df87b92..fa08124a10 100644 --- a/packages/components/scrollbar/private/scrollbar.ts +++ b/packages/components/scrollbar/private/scrollbar.ts @@ -443,7 +443,17 @@ export class KbqScrollbar implements KbqOverflowShadowSource, OnDestroy { const viewport = this.viewport()?.viewport; if (viewport) { - viewport.scrollToOffset(options.top ?? options.left ?? 0, options.behavior ?? 'auto'); + // `scrollToOffset()` scrolls along whichever single axis the viewport itself is + // configured for (`orientation`, 'vertical' unless set to 'horizontal') — it has no + // concept of top/left independently. Picking whichever of `options.top`/`options.left` + // matches that axis (not just whichever happens to be set, defaulting to `top`) means a + // horizontal-only caller (`scrollStart()`/`scrollEnd()`) on the far more common default + // vertical viewport genuinely does nothing, instead of misreading `left` as a vertical + // offset and actually scrolling it — and the same the other way for `scrollToTop()`/ + // `scrollToBottom()` against an `orientation="horizontal"` viewport. + const offset = viewport.orientation === 'horizontal' ? options.left : options.top; + + if (offset !== undefined) viewport.scrollToOffset(offset, options.behavior ?? 'auto'); return; }