From fe78bac55faff97a716f7286b98c55a827b88ebf Mon Sep 17 00:00:00 2001 From: Johan Lahti Date: Tue, 14 Mar 2023 13:43:55 +0100 Subject: [PATCH] fix: adapt container padding to grid mode (#1140) * fix: add padding to container and adapt width calc * test: misspelling in unit test * refactor: and padding and adjust widths * refactor: clean up duplicate code * test: fix * fix: adjust padding instead of size calc for column case * fix: rm border too * fix: adjust into perfection * test: update test image for grid * fix: rm border * test: upload new img --- .../src/components/listbox/ListBoxInline.jsx | 53 ++++++++++++------ .../assets/__tests__/get-list-sizes.test.jsx | 10 ++-- .../listbox/assets/get-list-sizes.js | 10 +++- .../styled-components.spec.jsx.snap | 4 +- .../grid-list-components/styled-components.js | 36 ------------ .../grid-one-row-js-linux.png | Bin 7357 -> 7359 bytes 6 files changed, 50 insertions(+), 63 deletions(-) diff --git a/apis/nucleus/src/components/listbox/ListBoxInline.jsx b/apis/nucleus/src/components/listbox/ListBoxInline.jsx index 24a9ed162..7355d8672 100644 --- a/apis/nucleus/src/components/listbox/ListBoxInline.jsx +++ b/apis/nucleus/src/components/listbox/ListBoxInline.jsx @@ -24,22 +24,28 @@ const drillDownIconWidth = 24; const classes = { listBoxHeader: `${PREFIX}-listBoxHeader`, screenReaderOnly: `${PREFIX}-screenReaderOnly`, + listboxWrapper: `${PREFIX}-listboxWrapper`, }; -const StyledGrid = styled(Grid)(({ theme }) => ({ - backgroundColor: theme.listBox?.backgroundColor ?? theme.palette.background.default, - [`& .${classes.listBoxHeader}`]: { - alignSelf: 'center', - display: 'inline-flex', - width: `calc(100% - ${searchIconWidth}px)`, - }, - [`& .${classes.screenReaderOnly}`]: { - position: 'absolute', - height: 0, - width: 0, - overflow: 'hidden', - }, -})); +const StyledGrid = styled(Grid, { shouldForwardProp: (p) => !['containerPadding'].includes(p) })( + ({ theme, containerPadding }) => ({ + backgroundColor: theme.listBox?.backgroundColor ?? theme.palette.background.default, + [`& .${classes.listBoxHeader}`]: { + alignSelf: 'center', + display: 'inline-flex', + width: `calc(100% - ${searchIconWidth}px)`, + }, + [`& .${classes.screenReaderOnly}`]: { + position: 'absolute', + height: 0, + width: 0, + overflow: 'hidden', + }, + [`& .${classes.listboxWrapper}`]: { + padding: containerPadding, + }, + }) +); const Title = styled(Typography)(({ theme }) => ({ color: theme.listBox?.title?.main?.color, @@ -185,7 +191,8 @@ function ListBoxInline({ options, layout }) { const showTitle = true; const showSearchToggle = search === 'toggle' && showSearch; const searchVisible = (search === true || showSearchToggle) && !selectDisabled(); - const dense = layout.layoutOptions?.dense ?? false; + const { layoutOptions = {} } = layout; + const dense = layoutOptions.dense ?? false; const searchHeight = dense ? 27 : 40; const extraheight = dense ? 39 : 49; const minHeight = 49 + (searchVisible ? searchHeight : 0) + extraheight; @@ -205,6 +212,13 @@ function ListBoxInline({ options, layout }) { const drillDownPaddingLeft = showSearchOrLockIcon ? 0 : ICON_PADDING; const headerPaddingLeft = CELL_PADDING_LEFT - (showIcons ? ICON_PADDING : 0); + // Add a container padding for grid mode to harmonize with the grid item margins (should sum to 8px). + const isGridMode = layoutOptions?.dataLayout === 'grid'; + let containerPadding; + if (isGridMode) { + containerPadding = layoutOptions.layoutOrder === 'row' ? '2px 4px' : '2px 6px 2px 4px'; + } + return ( - +
{translator.get('Listbox.ScreenReaderInstructions')}
{({ height, width }) => ( diff --git a/apis/nucleus/src/components/listbox/assets/__tests__/get-list-sizes.test.jsx b/apis/nucleus/src/components/listbox/assets/__tests__/get-list-sizes.test.jsx index 9a212b570..ae893297f 100644 --- a/apis/nucleus/src/components/listbox/assets/__tests__/get-list-sizes.test.jsx +++ b/apis/nucleus/src/components/listbox/assets/__tests__/get-list-sizes.test.jsx @@ -42,7 +42,7 @@ describe('get-list-sizes', () => { const sizes = getListSizes(args); expect(sizes).toEqual({ columnCount: 1, - columnWidth: 47.5, + columnWidth: 49.5, count: 200, itemPadding: 4, itemSize: 29, @@ -50,7 +50,7 @@ describe('get-list-sizes', () => { listHeight: 300, frequencyWidth: 40, maxCount: { - column: 706315, + column: 677777, row: 577000, }, overflowStyling: { @@ -134,7 +134,7 @@ describe('get-list-sizes', () => { }); }); - it('A minimum item width should kick in if text is short and reverve extra space for frequency', () => { + it('A minimum item width should kick in if text is short and reserve extra space for frequency', () => { args.layout.layoutOptions.dataLayout = 'grid'; args.layout.layoutOptions.layoutOrder = 'column'; args.textWidth = 10; @@ -176,7 +176,7 @@ describe('get-list-sizes', () => { const sizes = getListSizes(args); expect(sizes).toEqual({ columnCount: 1, - columnWidth: 47.5, + columnWidth: 49.5, count: 200, itemPadding: 4, itemSize: 29, @@ -184,7 +184,7 @@ describe('get-list-sizes', () => { listHeight: 300, frequencyWidth: 40, maxCount: { - column: 706315, + column: 677777, row: 577000, }, overflowStyling: { diff --git a/apis/nucleus/src/components/listbox/assets/get-list-sizes.js b/apis/nucleus/src/components/listbox/assets/get-list-sizes.js index facd7edd8..51fbff79a 100644 --- a/apis/nucleus/src/components/listbox/assets/get-list-sizes.js +++ b/apis/nucleus/src/components/listbox/assets/get-list-sizes.js @@ -35,17 +35,21 @@ export default function getListSizes({ layout, width, height, listCount, count, const listHeight = height ?? 8 * itemSize; if (layoutOrder) { + // Modify container width to achieve the exact design with 8px margins on each side (left and right). + let containerWidth = width; + if (layoutOrder === 'row') { + containerWidth += itemPadding * 2; overflowStyling = { overflowX: 'hidden' }; const maxColumns = maxVisibleColumns?.maxColumns || 3; if (maxVisibleColumns?.auto !== false) { - columnCount = Math.min(listCount, Math.ceil((width - scrollBarWidth) / columnAutoWidth)); // TODO: smarter sizing... based on glyph count + font size etc...?? + columnCount = Math.min(listCount, Math.ceil((containerWidth - scrollBarWidth) / columnAutoWidth)); // TODO: smarter sizing... based on glyph count + font size etc...?? } else { columnCount = Math.min(listCount, maxColumns); } rowCount = Math.ceil(listCount / columnCount); - columnWidth = (width - scrollBarWidth) / columnCount; + columnWidth = (containerWidth - scrollBarWidth) / columnCount; } else { overflowStyling = { overflowY: 'hidden' }; const maxRows = maxVisibleRows?.maxRows || 3; @@ -57,7 +61,7 @@ export default function getListSizes({ layout, width, height, listCount, count, } columnCount = Math.ceil(listCount / rowCount); - columnWidth = Math.max(columnAutoWidth, width / columnCount); + columnWidth = Math.max(columnAutoWidth, containerWidth / columnCount); } } diff --git a/apis/nucleus/src/components/listbox/components/grid-list-components/__tests__/__snapshots__/styled-components.spec.jsx.snap b/apis/nucleus/src/components/listbox/components/grid-list-components/__tests__/__snapshots__/styled-components.spec.jsx.snap index ae6158fb1..c64a548a5 100644 --- a/apis/nucleus/src/components/listbox/components/grid-list-components/__tests__/__snapshots__/styled-components.spec.jsx.snap +++ b/apis/nucleus/src/components/listbox/components/grid-list-components/__tests__/__snapshots__/styled-components.spec.jsx.snap @@ -18,7 +18,7 @@ exports[`styled-components StyledFixedSizeGrid should return a renderable base c exports[`styled-components StyledFixedSizeGrid should return a renderable base component for Grid 2`] = ` { - "css-qk0o61": "&.ListBox-styledScrollbars{scrollbar-color:#BBB #f1f1f1;&::-webkit-scrollbar{width:10px;height:10px;}&::-webkit-scrollbar-track{background-color:#f1f1f1;}&::-webkit-scrollbar-thumb{background-color:#BBB;border-radius:1rem;}&::-webkit-scrollbar-thumb:hover{background-color:#555;}}&::-webkit-scrollbar{width:10px;height:10px;}&::-webkit-scrollbar-track{background-color:#f1f1f1;}&::-webkit-scrollbar-thumb{background-color:#BBB;border-radius:1rem;}&::-webkit-scrollbar-thumb:hover{background-color:#555;}", + "css-1nwu5vb": "&.ListBox-styledScrollbars{scrollbar-color:#BBB #f1f1f1;&::-webkit-scrollbar{width:10px;height:10px;}&::-webkit-scrollbar-track{background-color:#f1f1f1;}&::-webkit-scrollbar-thumb{background-color:#BBB;border-radius:1rem;}&::-webkit-scrollbar-thumb:hover{background-color:#555;}}", } `; @@ -39,6 +39,6 @@ exports[`styled-components StyledFixedSizeList should return a renderable base c exports[`styled-components StyledFixedSizeList should return a renderable base component for List 2`] = ` { - "css-qk0o61": "&.ListBox-styledScrollbars{scrollbar-color:#BBB #f1f1f1;&::-webkit-scrollbar{width:10px;height:10px;}&::-webkit-scrollbar-track{background-color:#f1f1f1;}&::-webkit-scrollbar-thumb{background-color:#BBB;border-radius:1rem;}&::-webkit-scrollbar-thumb:hover{background-color:#555;}}&::-webkit-scrollbar{width:10px;height:10px;}&::-webkit-scrollbar-track{background-color:#f1f1f1;}&::-webkit-scrollbar-thumb{background-color:#BBB;border-radius:1rem;}&::-webkit-scrollbar-thumb:hover{background-color:#555;}", + "css-1nwu5vb": "&.ListBox-styledScrollbars{scrollbar-color:#BBB #f1f1f1;&::-webkit-scrollbar{width:10px;height:10px;}&::-webkit-scrollbar-track{background-color:#f1f1f1;}&::-webkit-scrollbar-thumb{background-color:#BBB;border-radius:1rem;}&::-webkit-scrollbar-thumb:hover{background-color:#555;}}", } `; diff --git a/apis/nucleus/src/components/listbox/components/grid-list-components/styled-components.js b/apis/nucleus/src/components/listbox/components/grid-list-components/styled-components.js index 9b4456357..6b383a680 100644 --- a/apis/nucleus/src/components/listbox/components/grid-list-components/styled-components.js +++ b/apis/nucleus/src/components/listbox/components/grid-list-components/styled-components.js @@ -37,48 +37,12 @@ export default function getStyledComponents() { shouldForwardProp: (prop) => prop !== 'scrollDisabled', })(({ scrollDisabled }) => ({ [`&.${classes.styledScrollbars}`]: getScrollbarStyling(scrollDisabled), - // TODO: Verify these props and make generic together with grid component. - '&::-webkit-scrollbar': { - width: 10, - height: 10, - }, - - '&::-webkit-scrollbar-track': { - backgroundColor: scrollBarBackground, - }, - - '&::-webkit-scrollbar-thumb': { - backgroundColor: scrollBarThumb, - borderRadius: '1rem', - }, - - '&::-webkit-scrollbar-thumb:hover': { - backgroundColor: scrollBarThumbHover, - }, })); const StyledFixedSizeGrid = styled(FixedSizeGrid, { shouldForwardProp: (prop) => prop !== 'scrollDisabled', })(({ scrollDisabled }) => ({ [`&.${classes.styledScrollbars}`]: getScrollbarStyling(scrollDisabled), - - '&::-webkit-scrollbar': { - width: 10, - height: 10, - }, - - '&::-webkit-scrollbar-track': { - backgroundColor: scrollBarBackground, - }, - - '&::-webkit-scrollbar-thumb': { - backgroundColor: scrollBarThumb, - borderRadius: '1rem', - }, - - '&::-webkit-scrollbar-thumb:hover': { - backgroundColor: scrollBarThumbHover, - }, })); return { diff --git a/test/rendering/listbox/listbox.spec.js-snapshots/grid-one-row-js-linux.png b/test/rendering/listbox/listbox.spec.js-snapshots/grid-one-row-js-linux.png index ac11a96f009ed12dfbd877779797accc23ec79fb..1b841622509551e52b4601afc4912770ba2d8ef3 100644 GIT binary patch literal 7359 zcmeI1`8$;T+sCa*&-eRveuP~%)ZMX7a2p#N+YZ#l z3npxA?8o6+{+?!x!N%WhN2@EN zZ=OHIr8%~{MC>PqQ^56+ZP)d!FI}9v(%$x$rf(`UOK0$?+%G7@KIR`dqcSwWmEn7k zqp?HKfcqZyukAyJcsI*wa-P||TkFe~FK){#_a{`j1o}Q^w8Yh+706=M{^XTpvDJyB zVP(0(bBB0&+4k}7nqy}>_nLDro5KI)y0kT~uFjr5(fX|hM_;TSX1Twcv8whhc#~36 zQow%>FrS+R7}fw=S)$3;n>*N$*67b#?yuyq)wdZUZM?%qkjMX zov7lI>t46w_^GimDE)3}S(!`AixXt(-E-&W>o@B5%Gmyipm6_a?kF;S&o6CpOhQ6e zTwGjAQnF)Vye0PO(^N%QO2FvdTi+{;g@lA2hliUH2z4ZdtXpjDqLZTeot-9wz5{Lx zD*=JRZB0$LQ)H#21b>~Q6xc|h63^UD+qP|+nXz$dVxk~zxE_7ub2t}9sD7o@W3hol z8{&-lW71Mm%`1O8bVbZ@xXRF- zLQ-)p8TUDOrkf;)U2qe>A%~gi>BT4LHLbILb7ubj)rpCTi=Pj8OlHK{Pt&Vnr7fp( zvQkWZe9CIZTxA}=o%W5LFMM;7{k)VBsDb9MfK}d_CYKk2PY@}7<+%3qL2n5 zAQ-)cO@I&kT4pbaLZPtpq*0Gy7RH(cRj}D&E_2^2V@T<`Q5Y&YnPpEE^820|60#X( z;^>&^NKTSOVQ$^(y1R*kjH_*&7~(zubc;<}y@+ zBabRLkWG0%6-?JL>L@HJh0aWQmF%%HG2w_Z@$xF+D+$_I9mcPW$1ytf zWf@!;U0vP!#%+hrXc|=s?Ade1a+803Lb=Cq;lhF!)K*{GBEP*eLw{8!b+f=n)g*TyvX6D&DrH75=oud`0?Wzk>#YA(@IJ+KOc%% z7#J9|kFxQquvEm4OG%k%Yd@H)UA9+SY(Fn0BV*y>k`tg#bg!nJ9v>TX{`8n%)1!uJ zpZCPY&8-_+;l8~`o4f!s;Mb1^mbGP67Lm{&)}5yPWRZM!-uXl54rY@PLH-qkeUeBR zK4WcWh9%RuVcnNuL!p0?+U_fN_7~rH=#5_*oF`b)scrN00MMW?Zfw^Ii%?)wPOj9D z-iIhtM@NU)<_}M>xi)1;eRWm=Wpd#{hp=&xwxD}fE7xsL)XdkdVwVT~T4or{qrZ=dik^>BTe?D&e#$EI z{HZkPr)fw=II$38L;CW9zm-E z>^zyNsha43-w(j~5XUJWC>R^N>EP&y`4WA2lf6fF>D#v$T7OxhqU#G2Q`7tQ?lY8v z>DgI`i}m1gI}3}<@bK;6NO_r=I-u09<+|ls_JWz0GCU#rm%XpJQu3dYxIgDoD_^|W z8ygqL<9vw0VB}_JM>mcdySo=6Zu$MYZCQDFURs(~YisK!>-q6RYHD`-_wPS!gVxh~ zTv$1g_zFGz{d<1UI-PiUK%G}ca|^tA#Pvou80|%mn%|#;w=Rswp_^wI80O-X;OSfO zF)=aNZgWdXSy`j|TX%F8-)P)gI@F?x!Q_VWhzOT3aJBXsSy>z`W@ct)wA%sBtXD?| zrKP2lmGg>5=__V-c51KgV*UJD=9h`|ipJ3n1Es+(YVp#NFYSA!>5d&cFdiP3*RQ7? zHB8EW{(KKrk%LFLO8^~^3YFKsaKRZ$mniS>cw}Tm3KS&rOVZrjeCo?n(HQbaM{sP&L7D<3N}YMp$Hymz)RC_13a@%q+ENG0hq6MTH24kH zax9Tp1feT**7uT3oW7_|c>8lnQ<;j$A+mbLS=<6CH#fJdz5NxLZ20NZCrRjqsi~>{ zTL1E;**;Mep5Cgo8vdrZ*f=&e7US-2ap_X{=O~dP<8&S60cGFWcwbt7#%Ng}pLND3CNPi$dU|@D6Rj_kzkP-~%#229SXf&pA|FyyqY6*# z0)I-zLw;6ORhgNZBBFpDf(>v5tigcaeoatd6nF&cyScf64S5rP&kohvoDbdJG28bB z9w!5;63FPb)TBAJy}Sc?C#d9}gir&LRXK;9Pay^G5717OT0Z{8^SFWmxentEoWe!DL8#EBCi-OIObpykqZH8e~vi7K+*!Rkh#zz3kf=2GdJ0hj=^9$yK+op6BDJ1dh#SqGo9OCX(Bc) z2E6AAx!z1BTWD$B1EG)6SFj@=xsufbtveI0`uO=t!?wG)xIBLH#Pt06`-VwpQo*|= z6&1~!nwk)5+`W5u(l|*$7wIv*OP9=m1^}13dwY9}SVD5wLx7W}^F82xo78<=-Q9f+ z(?>@}kXp5-sM&;tg#nU1e)K51W!wadEj=PCDxS<0wuI;)UG5;I6vG2gXom&%=A zuK*_}-OFttI832gcJuKm-uS#7lC7IU(Tkz`6y)cd>FPcN$v%hu`}pxAJB`CrPBVG; z-aUJqyK~K(o0?YodtzU_uwPU}ODHIq-e`<84-Bl06jsY+GVyN1b#}y{^~!~Xg)s&m zmY@CT5vRC>#MzcgTN&iL$DOFg^k))An4UsAjH~O6cU50sUrv1dKBvAE=Zj8}UymF) z0)Ls@xRpmZKS9AsPsKa-*Q|q>q>765;NT#uzh`J5nglH8x9GK;gLWNo6UrBu^jK8R@txe8B z8yH0B>FalQcBaL|>;@s{K;*lB$D&{5H7NJ!$EfVyqvr|{k!K5WjD>YfT4x}g~690QN5tQ-#T!q&*^bwB&Fi^ zs7!^epPwJ`9oEN3xkKaZB{j0TmYUD>a|lAkTfHY~gVmN`xGEl#!IwGlj;GYs?OWnx zds>x-^PN&~IsgI)87Z`>rbCD!OD-%lR3{oi!{gx|_v<8P7N@>R$;$Q&)%v&Lpx)VK zW!*SYhHdJ$&41}#yy&_9Bs?5jIEFNxlvHjT9zWq&H+RetfN)`z6*!Te`0`~L09DJ9 z`fIRcA=NwW%Ht7XVYl5&X=)R6n%}&QHuzl^)C}Qrr#dq5XrR_`M@K3s0hXa01^y%j z{M}>cmyGl+y6-QU2LuRzP0a>eR1y>g+T7g4WaNTtUx@=*9xZY7s17^=(ir^%1JFE~ zAYiKV&)(u2$XrBH;Ol}GX8swCQgmqEOiiD8yME|{AOOvjxec5)NKv(drUore&&^o`1k@O&sP+xZ_5^7=IAkEL38jP~ zLGkoyuqzah{tN8Hm*U!5JBUreQ_j&~Jn8;Rv&J`XlIV(1Mr67ZbD8ZeMrMY$ZUdS5 z`NzQ{06FyC%LghMKigB!BV!1tWh*o`0^)$yD%i>B!aC;kuK+nu?HRwiLn8J7GaPgjKKLa5#hxND3aMkUH=PNa{>aPn*Di0%o7= z{|QF%E(#^n;N|Hiny>5EcscM{N{(q(DP%Wy${Z{J2@izY)z^NVcajhK9KJt}=75Ku zgHJ@1i39`!n_!uQwL215EYX2w;67z8-;M%R0ii->xQ=`b%LVt-*MD;T`t?(ROe~dC zT1LijWn~52u#?G{;or3@HTC?%Y359kCAh4Lic0B?#+?xdl*m>`<>WdlF@QKD2l+Rp z?q1L!S;Hib%$52tj(5P)_i%15#ZGlzcK_?$Mz;P!95F91uME(7oJw_ViV;t>R}0=S zUdzF=IohW1DX@78Z{`l(|DUcoP)3OVb~fHUggD;d{Ykt3I(h#p``_1SS_2cE@Z3qG z==dx(Hca$5net3IdM{iVgee&b4>}G3EQ3T691OUO zeR+EApw*<%8P9XbAT2=*NJAP#LIU{Gs*0N8590iwRuetRVuiy|22cx_Tq)rHNbnyy^`hzQ-*bg=g%PA1(qM`9yC@aB)IeF=Lr0dyBvE21pFjV^I7-}2bOQ+=H}VPHRK$_+@HhJ~yj3VY_dK7Y`A$9cV(eEF& zho#IrLALD_QX!QO6KtLc$Wj1#3TtX=Jb7eq&Q;CGoVfD(M^YmnAK!}FLUj|K(+OZjC$5q=uo7+(+0M_$l}}wRmR81?c|D) zZd{rh%sV?@Lr)3>|M|veqO18uJG& zuvqLqoo_*hcUV|iB>)B!{(KNoi9+(fEk(_`=1#Gyu+cFk0ShiJE)d8wF_^0)0aJ(s zX3kLHvK^x2Yr=y{Jqm5cc6PL{v_(pHw|Mz#jP0AsTL;wxu1FXrwPzcrzgK%6U9A3) zzl2x;S?S7RGT`ii0?Cuy2sw-e>PHw82$8#cLIzUt93b#dVV#R&Ab*HV>oQEN}8m)r~^tc{Ae0{r9_6qv)|2u#$9-V5>xght0D|v4;8Bxmq`q>&NcDFJ+z4q)pi$-dBwH{t;MMmh)7H7HUIVQx(qIIw z$qMA&d%T^sJX};&r9wT`T-BXt>B^Yyjv@8-UI7WXt}KjOcGZ7|M*W7!7mg-b>&x{$ zP0u6%k8^SKi75_^KMnleIq2zC)}@OV+W~^o!4nLDZ?6ekIoC3$^YAblwBb~hlq`+Z z7-su)x-=W{uB9&88L-mn?vIi0fN$)BL z5RhI%3#jxODIq2y@aBHy@4e%_pKlyS_Q*(Ed#$;?HWx2$X{mAjar_Sy3dM!Faa|XM z+I9}EHI7~I&a?gdW#MU?yROsX8x!+hyiBdswXWKCZg#kow}Ky`jm{ z(MNaA?mSj~^(4og$NSyN1%tyl!-khA`-G<>-QH*#$M`L?qVEe2lHJ$kFzapZ@d}c8 zw=Wz!j5@ZzYitMV;jIJbP`3O34_DM4LucpQskWGO{OZ)~zQEtg3A7^5MN>Pa)YMcf z-aj9@KCY@`?<89j6w+RwkX0v|i$_`nvOI)@gs7_-s6B^k_8MX^FG^j<#4#@k1X6FN zp2upNteB$W=Eq&#-Q7v&%}OsNum@eO+1g~9mb?A1S>*l{k!+M6EM}N<%hR*;Wk`sD zxp~fHYowTh0@krVr()re(@43Fv$ONdSFiLfEaFp&(w$J9LK8g5ja;f0W$3I;{xUl| zJ9!%S(WC45lk@$#Zv{1DFX_`8f8pXCPb5-*BHLu2Dy;|bT^B$bef zW$)M~4^Pjz8ro=&nQKjIboB8Gvde2;Io-062O%yMlSfsqN0}epd6R?QSK`lHU%k^O zBrk6^IXUUie;tdBbB~s@Yv*l?l2k4%EF}8Kw!VD%@_}N&T9=xEpo=YQ{{r!g0Qc^RoWk)+TBx-uc?~57b_3CLWq-JG_ z;%h8aR8(eHJCsNXTN`#YD=lI*3OH2F{?ge`lQ~woW@TERZ{HG+)Nr5rrlYSPZrc{! zH(qcW_o*4Cc+{JK1nWgynX#DthavhdYBqZKS9%AbM5*o~bn@2uSxtoHSDOMAPafk7V|B|9tY!Ou=JCXGU>^=ArVR;SwBT@vKn z(|-Q^DM)o*W{vcabdpu&1O((g7pj}Q*cPu1%gV^86X7p5j76*4>T8v#%Zaq`$yB>F zJT&B^V2DcGjqg%WQX0d{7BEBb%=JZ4F|nQ;lcM4~4S$@+Ru&uW_}vk>IqtWy)}uW? zo1UOZkreRz^@!j5`y>94vB>U{UHz%dJ%wUd$U?%x>%T7x3pdis=fjH&3w8D>Z4^Lx zmHF8p{+jI*v9hvCgerSbYG`wb&-it9bl#||t549F9>dOMB7ibr5eL1wDzBt6d}_+p znOrG7!SZx=FYI97$3K$o?}2B`rpFndH!Z$Fsa_40aT@rLz*?~L9};*tI!k?${N%}# zX>4r)%sV0?LIjpXza0L)Ka@{Cwx@^^xW$ZC3Jf4H&CJZGUr!Ux?&r8iuJmZ9nj{WX zxTkmb^!WOX3dzaIO|Wn@fDndw>eMM5bFoaGlWys^tdg+K{=w_ZiujN2R1F}8XRc|9 zIv$Vb#?GUSXeF7CA8*%;x{%lusF$wQ7cQbFh%qa5($Ub+_-f5)Qrb+Mn{!~>z76Ij zSI8sd6s1bwCx;|611<86h=)0LuJsvNVu&)%Ln&=Bvhsw??CklT+HK~h1LBUxmAK64 z=x7`i?Wt6igR84ofUDcz&krcrDwEsd70kK^8y51Kp56Hpyt|S%>Kl=ekbfrL`%8s? zSCUG*5NC}v&~cRc~GCu)IMIEpvP`%H5EITT0o>4`q zH4_mLnOj)U_w%a`SdAG&$fmSK?}+0urm^{=evJR|)W;xgo39S+`<9m#&UCoyP>HmFnPzWUyDZ`6o3$h$HnD-`SOKx)KXVB33i;dNvor*-kzJA zTWjF9%t%bUnwy*Zpo$Er*Hu-0`g9LZ_cZINhP!)Fe;zj5q{vnT#-3O#bs=Z1uX}># zQt1@{p@{hSyquhK21Z7X)9rDL&-(&8)BUka>sJDI+jBX#MN1QLUB(_{Qg3g{$B!Qs zxl&3?4dKTt-j`dNo4aD=+{53#O$OzSh>Xkz)z)V6ae$eH$H()RvV_i@nU8kN+f847 z{rWWn1cI-0nw_C><1={A){wD>$hmX9CtRz$(jWQJCnHyUYN8eWE1zsXII zgO;|oz7JP}a2vmu6&Jlu-oRkGz>4ZimGVj1#l^*80%M(>`%9;HG4yVPEFC#=q&>Qi zw9I5>Wo6CvTtUmrAK}>9UPT^Xo=J;wnY(Z1=$MVzL}eu!w7F$^)&A$-FVwZPJo(*! zeZT4teg>7-xqdwuN|z*}mv&ZMT#H!jI;OZdSfqw9uhe;1%AxnHX0#M0T|2?h#wInC zUqN)%$w^o{K4)NHfZv)mlT5%(d_92ap6N=K#;@G1Fwf)UH>2r&X>3$Qd>s@@4@@^y zKuJ8ZHTA%0)q5@BBHUy(uj%;iIJAd4a2PaA&)2sKL>G#fS6nQ$I9g-oyT0&7u!cSr zqtIw#X_^1>lmN z`=f`5nEn3kjrCnDX_twSan^-OOtr^lKj#{>Ps` z$heN?Fc?0Q6BGAFtMTeLZ}vj?iR)>EJIss|lnfmlvXDVWM$A2MxO?!^_5g@HXf#?M zi_MLV<%L)z0y^0G?!1GGOFtMEHICw^cK-bNp=!K4VR-?TrTlbH*TP^?#QXP|fM|be zpV6OucBBw*K;m&(mslh*XeKCPfkK>kfoTSqga>2I9_vN+sGLoUk@8$HHqh6ffm$Qq z2I)9*;zTwZ=h3yO_lpCC%3!q73cjTW4;`X5pGml6S)Z0B3@{awkuh|iZtnx^PIy)s zSXkusq-%?vI&}^78SVGG;1DgpmZ4r!QW7oWe7x|B?5UK5ETGR5DR%@|tEQT!rqRM$ zu^HA)q4wjAuYIQDyo&Lww*CO&xtSR^elKur$RYRUdNb8Am^Y+(M6#vsb)E!{X%K&o zk4t+kncLXdi2V8I)fX>btT0(<{7OoDtla5YrvgAN!bsSUhK7dBygaQl+VQ=`_C4W5 zVp2+qpk^G}VE69b?VjC(CPqF!m41o|fBp5>VOTbU!LZMPM;Qv;|er1eD`#pKpxNzxxs-0 z2hiTXK0unBaVazFNz;7WF%6HNS3DQNPW1KkPGj0hB}qxwAbDox)r5IR}Kg>C(?Z z?2c34-V@tvHN2N(h==+3a@*V6E%+R1(_twY8D~q%%4iM(SY16mB(B0M$AJQ?<7nKy z8XMqQ8e0=h@0vB(;pIU%eQa6(j@cKgr;QR^nCq3rRwji9`>%T zlx2DYu1&Yye{+xA~RwV82w~`}YeCbBrM;`P_hD)1GMwq6B-@3o8Hf z=N&$|2iGAj6MM7o#mTv=x|A`iZwrE!XJzL-=*NIdBk84{OE$KQkk>H5V$#Iq;gN=&S&EAR~dsFcykBkUhFT-iXCj!A|i%ng!M* z1-e94#>y)z_%n)OVNy;57Z%A?M#-wLD{s#{bmPF=NGU2>v_?vh+GNLa?BemNY;0_z zXU}$C?6{n~wuVEdtQN}qW{hG4Ym4_^9}LTHM!QeFYH4XH_M(igE^+P(0v+ais z3po*|g}1(QCzjK>ia@{CL}ux}ftA9d5Gr>M=DRB^D-$})N=hKhn*g=joxzlVa+OP! z^N`%XZ?qi!r8z^T9Gse-wsf0E;6#R@SsHA%-9W5y-33|ZTSncQ=8p;9{fNhW~ zdIE@WY<$H-Y!|=9^i%ZTSZ<2`%jRX#cdU+1;)fvfZYwpyXFh ze!y0FHZOY0M=J87aW|j|>En_eISGchu;Ao8P;m9xUcPP!rDP?`J?33Ld^sVrFS{$c zDp%X(mC@3%Quaj1f=FLl|7>4&ZmuRjK`n@VpP`FO9%KXI*#*w?va&|Nw4q9mY_OZd zAa9UhrcDc>6d)D%K(RpNj$gEW1&IX*hT%M1x|o#i3*8+F>lNp@Fpvn%L#N5N1ZaF8 z`p1lpFQ0`G6a3fTgFp8T4rZ2@Ujpin?(TdKAZ*lQ?TH+7CruT~#Zo#j;~yz`xSDG^V2 z{yQW8t5J~8Mt%%nR8*co9F+>(^kXqdb$gq{*;9O%D%22qDAMSD(Xf9<&zq*aIjy!n z{`}+E3 zCnGKUlL?Ah;3Av+rUOWPLVCqUfPZPHsDxWD%P_gIwq#%{Zv+)P-1zGeR7$B*4!IbVUp8odV(J_eZ_Nej9J7O6W1kYj94G8y~odM>_cYn)PVlDFYw; zW0LA4edq7zoBXO@#hFJ+;TQ^0d3$?%XhUn&H8koa#SbBqb0MQAr=~3K7ovUEGS-*K z+49ibR$GUJg!CNJzI?x-VeSrSlX%>v``19KSPJ2;HLdYhHPDkG0|`RNa_J6{sLg=G z8vBKT!bpYkM>DR0%oxLbi`?56G9VR;T)e0UvP_{^Jt6<7eX$W;Zac; zAP{S`TVIyoxTNCygT@%wTDgHJ zqbYDn60Me|fYF5zgS0S6oB4#>;+C$iF2KVNa)MxHQBhHX+FuvUU!-pk*Xji}e?4|I z?&|7dU5M7G%0b`)HJ^bNxzODVGBpICWEiD&eA#>*#U33TyfZZ|O`QnQ0IWSxTZyGl zsb%H?G~)yKvA=P-j`{`$a*k*+m|7D6%`U7*vqm-p6d5?J*d#TRoTP z`SUfWf?(97p&?U@T{3HvNy$!2QzcwpxuHZjw*e$Y+Hr?wq7Y+NJ21HFM@L}hX9+>U^E)O(!E9}d=he!e(!n1^R@te&$fEiH{RuT4cwO-xd#a%l=Qhvih=(9I11C#FT*nV@Uq?^p#1YiMm<{$USiA zmQN%eWGxc{;m|W3iL%HMs+tm`>j>>=UGFM01_GC3UWICh0u71rO#I!kS4Nip1`x#O z@MY5z6Em6V@pI1(_}8p%ej|a+N%^eJY##5};%)M^8TZaVXEZv@kIFON$@!4y-(Axz zhrOsh`risrQCI)D{;9z~82ASR|6t(%B?guQ|1d6X4i@P`{vV0LsAyf!yLS8We*mQH BE9(FN