From c82ff199f14374c7d8ac6130dab0ed56b0edee8a Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Tue, 30 Jun 2026 15:09:00 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(frontend)=20replace=20header?= =?UTF-8?q?=20by=20floating=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We replace the header by a floating header, to have more space for the content and in fine to have more harmony between the doc and the doc-grid, in mobile view or not. The layout have to be adapted, as well the legacy home page. --- CHANGELOG.md | 1 + .../impress/configuration/theme/default.json | 2 +- .../e2e/__tests__/app-impress/404.spec.ts | 2 +- .../public/assets/icon-docs-dsfr-v2.png | Bin 7389 -> 6800 bytes .../apps/impress/public/assets/icon-docs.svg | 14 +- .../header => }/components/Title.tsx | 9 +- .../separators/SeparatedSection.tsx | 14 +- .../impress/src/core/config/api/useConfig.tsx | 2 +- .../doc-table-content/components/Heading.tsx | 28 +++- .../docs/docs-grid/components/DocsGrid.tsx | 10 +- .../impress/src/features/footer/Footer.tsx | 3 +- .../src/features/header/components/Header.tsx | 96 ----------- .../src/features/header/components/index.ts | 3 +- .../apps/impress/src/features/header/conf.ts | 1 - .../apps/impress/src/features/header/index.ts | 2 - .../features/home/components/HomeBottom.tsx | 2 +- .../features/home/components/HomeContent.tsx | 6 - .../features/home/components/HomeHeader.tsx | 24 ++- .../language/components/LanguagePicker.tsx | 6 +- .../left-panel/components/LeftPanelFooter.tsx | 2 +- .../left-panel/components/LeftPanelHeader.tsx | 153 +++++++++++++----- .../features/{header => left-panel}/types.ts | 0 .../right-panel/components/RightPanel.tsx | 3 +- .../src/hooks/useRouteChangeCompleteFocus.tsx | 11 -- .../apps/impress/src/layouts/MainLayout.tsx | 99 +++--------- .../apps/impress/src/layouts/PageLayout.tsx | 40 +---- 26 files changed, 214 insertions(+), 319 deletions(-) rename src/frontend/apps/impress/src/{features/header => }/components/Title.tsx (70%) delete mode 100644 src/frontend/apps/impress/src/features/header/components/Header.tsx delete mode 100644 src/frontend/apps/impress/src/features/header/conf.ts rename src/frontend/apps/impress/src/features/{header => left-panel}/types.ts (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e0046f64..95523d42d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to - ✨(y-provider) preserve callouts, PDFs, page breaks, interlinking links and commented text on HTML/markdown export #2296 - ✨(frontend) add a user menu #2463 +- ✨(frontend) new header and responsive harmonization #2471 - ✨(backend) add management command to reset a Document #1882 ### Changed diff --git a/src/backend/impress/configuration/theme/default.json b/src/backend/impress/configuration/theme/default.json index 453382079..3f87d89c7 100644 --- a/src/backend/impress/configuration/theme/default.json +++ b/src/backend/impress/configuration/theme/default.json @@ -142,7 +142,7 @@ "icon": { "src": "/assets/icon-docs.svg", "style": { - "width": "32px", + "width": "40px", "height": "auto" }, "alt": "", diff --git a/src/frontend/apps/e2e/__tests__/app-impress/404.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/404.spec.ts index 79b4be2ad..42fa5df12 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/404.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/404.spec.ts @@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test'; test.beforeEach(async ({ page }) => { await page.goto('/'); await expect( - page.locator('header').first().locator('h1').getByText('Docs'), + page.locator('nav').first().locator('h1').getByText('Docs'), ).toBeVisible(); await page.goto('unknown-page404'); }); diff --git a/src/frontend/apps/impress/public/assets/icon-docs-dsfr-v2.png b/src/frontend/apps/impress/public/assets/icon-docs-dsfr-v2.png index 34a5d7709d89903be33005b30b9e11bcfb5d582a..a11c7a63ce04ea32d75b3bbef4aa7011cdc8f997 100644 GIT binary patch literal 6800 zcmXY$bzD>L`^G0dTDn8Jhe(NlAV_UUNrMAqlrUk`C`oCCpfVb1kQ9;8C?znYr34hD zOIrGO{QSOuY&++5Ugw%b0?lNwA71ggbQT-Xr<`{3u=7AO!%T<_oKMv?N{G7yMOKu1H( z#NT#%4(7!^Sx@@sx-pMugucP7W2M7Gf!^qd#8Xtnr;|WkiQZ6*ww}OPjHjN!P>kmz zfo^Kc-<#eAr$(=wA_bY&46x=G+a9#(Kiih7)>b-W+$41O?;X^ML+PGKg zJYobU8+OFujJjwd9LV&B`CdC|9=JFSU&-7{+HHBE#@jWhx%YAt-gi_P#C1%NI$-dg z=NL4#*DlXsynNVl^xMYZSr0dz`j2}kZftf=!7WR>(#Is#bat{=C%4Hg)))6M-H@wL z+C@ZqX2;ec8)FT5w$+VVPGX1s90#xZo;Irld2(Klx9Jqn-Xr&KcCsbTI&~j}I1++g zzCQ}7Wb#|wR*%VaSPWaowN)O}Fw~S|RWKd}l@?^(YAntn3S$Geb40oZ-zY-TtQVRH z&F|90Th@q>>PZJzSxjrxuspj_6AK^o;>qBNI%-gDs{ZeCpM@culMNFR0mHIo_L_QL z0TKg^ZCJ6y@)L!5@kksi2}!Z>u{_zvO_0>pRALty`(r=F{|*F@6r&bSw9Dqu?NMCO zV=|ZBd7k*()?L5!qj*(f)&6`x-YQl`r10x-m{EUP&6Y$i_gUK%(Q{OEv>4+x*l{Cu1Ava;63uvhu~9{iuhs-8V;gc zTiU)mvO#M5qjjG-k`}dj8}e9RXJ`~eW5L=nY}O+m>wNiS=ZSptY$WZ04sQlkn)JXP zuc?)nn`GC8vGaA_ix^d&htV+=G~TO*EjDD*PhQup%(A%qaE}nW)N>)})$zk z!hiO;H_{%!YV+Ut7qDPxaD2={yert0z8vtauiZK^ipPEW+|nw#lKSd+Suh@K54-(R zq>Cf9gF&6_0UcJI%ubuYn2eWlPz!!Ldf58Lfga>H&Wg9o#NeIDEb++0i?CP+>Kl>p zNR846kx1Lp_M__y$t$yLqe^v+v1!-R+7X54Pr5RdLXO*JiBHg~>0%2UR}O+0obc<2 zRIsG9o7XwYBV2@ktqBow@QdI`tvfvm`>Brzp+uQ!$Rnplrp6F1QfMl<#TXY+fonSo zw9SStL*H$(V>ytSB#80PCWMyNNi=ar^|tueuZRR?+D+~!#%93D8S6uq;xEpx*XkB} zCLCD05A-z&P$}vpw)fZj_b{5QaW{?A(3Hk%3(8diGz+G!tBJuUngsz;e;wP+Uu<5V z&f&h$fml8i?~&Air*2j1lNWW`I1T2GCx_80J5oSb`lZgh16t5OvSb$o%WAyqXp!2_ z<6H(9u#`{%@e!jy`m8+KxhwLb!rM?Vs*C?A6%?{0;B}_6O40oL>^o=2DB65H9HwX79C~^-wajqGfC{dQFEt3B{)~# zqBj^O4Ngb)AvqcaNU<#sk8Ej?`fh;KC@EH?=4VJ3Zq4ecL1f~x;Ck} zQ*Qfe=fJ;@dJ8iMaed z>FOyoui}xkIs3JfuUO#zH07rl`_wEbZ{CB-Q>s9^|9PQqOr|K2;;N%L$`W$3VoffsJ(x&A_JM7z--{jbPTg4nOut*Fwk3f;r~l06`4{&^kIctmtuU`p$m>ni4QZBY8f35NWE5LGo`eCbj zB`;}lyN4fALWliIn^EE8`MO@Vm*utRJH%zsXeYS?ML+H)(JJuv;?eaKaOErWhrDi= z3RkVH3xzZ&mdi3VRFv#4?SWt@6$I^F3(w72mtuR2Y&1ODk)v+u?IoR0?P)t-OuNkA z!>Su8e!HYa(jWXvyR^5d3t>ZY3!d#^A_1jlI@pK_t(1-y08Pb>*8wXiy{qCHxM`GE z4Ly}bqP+O4*5o)q4CB#0cV5;Ifihq3XL~(&yto%glX{cRKRPDDlbPz<7ti3#eOLNz zTeX1E&MZrsx}{C8ZPp06rR|81dAo>ga}hxAX>+<~g&ea|%|{I0sD@k3n2>!u@V%Z- z^$%1v9NKrL8%kr9Qq9`z-3_;wu>!8X-zeEu889SdcxP%H7}1lrmlM@`_1u7T%mL$k zcl?TJ`il`h@`6=0G3lOkvG*sLJo06@Q-&QF1U!mA#);v|;Mh#(kzml7-JfD(@+9c{ zM-nR&$AvjkH^1-xFuDRRI!!=ttwcSFQBCMib9^x&a?bwkveQXlM8PuQ_=C)d{>K-= zw~!Us1bMYu@XAwViHVX)WXGvn41^~x$dbCNHj|Hyu*Le=Z+g@=4;dzG+*`DOMdka9|I0-@umfkM(&Ys&xCYD=# z-h^)Yu<~{=y3UnT&KUdx_{3v`ki@+hJoK?$6(szc+OObOd11!(e0n*h6}^oxXH)3B zHdYEYH;Os8AxjXt_(vR0hgNQ+CHsLPBqR4=PHWP}@Q3Q4S&21y-A??A-Xy$8Z4 z7phCW5%YS#Ii4iKnZ1i$!#p?L%GI-#zoG=~WarO$qhzZDEBrgH#3?`#W=_yiJ+Vlg zySeyg)=I{9w07EA3rYubes-sPeuiS@7F6vzd8Qzfm^hY?}bn z;U*7Wbuge{3%XN$@n)5=V`*BYEOvuRiREejCnG$)kCBA;hooCn5(3k8q)6fpJD&tV z+X{U+7#^rhb~`J1e|0+B5&nW>zezMgJPEud>uqDtP)_*dg*;-O!_nNAgS7k4Ayg_iXR~?F%9b$q@NALAob%IL znMwdo4EKH>XNc|3ycjjxXOa=usi90(Fqly;s`aO5$i&YCg(v@{q5$V-?FASis<_Qs zSNDz$(aa2)tsdy;4cnXv(i`8MPP0y&Eo;1lYB2t4W z=Q+9;uW)DzydZXSPPw-#PU}DVLDKv%i>R`;+?%S(us?Gq z3UIuOy{eSo>&$SAXK(jL1QG(OZG@MS3)8@dG(nml>5!Z8E{N!={nd*vPdV!1VY;VL zUdc@f)(z{#h;zB75VW?1SvKAu(x&|U3Du1BtFKX~EN`&vIs|nkG$flcs=947iu7ak z#YD{u3dLNs#hxY1O}pZIAB#j=Mptf>GW37edFPGG{M5E4OeiyB)_=5<|2Z4NpXfsz zEKjbnA<6NhG~f~Y`A$4QDUc8Ftq7AiPHKBZu*ESw5$AAqbeV$AnrIz}{T06(?BH~s zGyD;?-DdP44N>OX+0~1Cof^5EjFKuMuL-Z;`!4f2q!XsH`asZz)is>qkE{JH)Rf6P z8%THF>ks1pwo7~HRUs7}Uv!EPTf|evu+|Oh9;4}uhpb#oR~jlxRwb4|>`}mdP?kp_ zoX?I`q!rw0VNj{2elS#~o!@6X{#~%JmY;lTx8{ll0;N;y=?5=(D%y3RRU;Zb@3f!^Kjs z_Pyhy9Zc^1RTf#++xwXrrWWf_Mq>SAuuZ7oG_2U}Sc&~p>N?x{jO?9qUb<$Px_M-{ zura)Nt}gxM(%C1#P>~nSyfr32mJCd?`6aCUHB*4C{SFP;SRcK#Jg}^$ru^c6=;CBt zA?){y%#bq1@QkE*>_%4in4(8MO3y}^6pHpyH)!UW5&RO6&mD(*o00?Sj}W_&emO;n zbnoqWKKc@G+bfH@Fs>bK(_Gy)Bo?}11%UD!oyBKiWzKwk;zdhmzCyI^k`o~@-+oTV zCuR7zW6gG(F`3C$t|J(L19W_=K%iG_eO`GXdVW3KPjBZ(oXs7jN5|}4PgcbCe^p*a zDK~Km?LG8+1dNXkzMPlzuVL$xG3nMjdLB|%rf3BIPECUy-`wxDs{E?bG4Z77Q2pA^&a0ckXr9K#6fZ&?`||81F`BNQ-v*ePn&|Ur}^Cw zB^R%hu<5e2(^^x6iBPH}(^gf1APEz-$_@uE=eM53%1dC<$d@~WiZ1w-E5ulSMN=GD z?RmJ6kJHrv{t^FqvEuYN+o~V)T`JRoh156BG9_KTG7~r{zosQ9H=pl8z|JgJ7rmvL>0Ei*hFFb*cqgybO~>Agz-G{(t> zkoKY^qb+ut8_(pA&tIsX{fE+@QatwID`~xtckNn&X;<5JHJJ;aVnwsulwp=0k{hx} z_8$^}qxGC>-@T|e4SO@vF?tj+mmW+JDQdV=+ej(2^@o|==pq- z&jQkkLzgucyK+c_t%gH>aJ|{h-(1OyiA1@crB-Ks)vAoD=_9+8W|WPkY*8LshsX>@ zrpc+V3|N;ckAmbi&7=&Xm1p*m|G46zzk9N5$Fqe6-i2

}wkU4dyp zIn6b~lv6Ae=&=G(I9aomVaq(AU2*eGHv*?w-lQ0&{w=TnnNjH)y|ikpM6 z?g+cVtEBgoJl^U2=llgNxkrs#;~Gax12960>#61~DuAOn23o~aS33~|4a(gbupIy> zMJRp_KL!IuZP7_LLR9|c87%L1K}f*2+#mHT+N!YAd&;!#^y5|iWvv6Z)O5bkar?zP zl0#ifyaoR?v?}C_qssPq8@@VY!`5aMgm+YW<@3+F13^jst^fNBD+LtsntNqy*{HklPgY0ul za6oZd>P1b@ufKWb&B0Z>;b=PLK|yT8qoJcY&uD>s&2!{Qt5DxSPEnV&&Otg?1C2_c z!uQqb9Fy}xluUU?i=y)%USlHJCl4s=-h)>x17Ec?|6`KCM0Z#xz`MXZEZKH%vS#hX zeBylV6d+pNjB4qeB3R!?9dS9K-EBy7sphnhTT7@-#WJ~atNl!euaap%cp3aWmsO!r zfIC5vsDMPX9!NDs2QV(9S;$K&qeGtZo}88Z>ByBdp2w_*ml-eB*N2G@NkShR^vk@M zi9)%StX>3h*Vgg5_PLD{A{}rJGgd~n%@*2BC`&z_{af7vf-9?i;p2&Dh`b>NQOq~qoq0= z$D@(7^a5e&UCDQ|v~~#NOMAD|XcsM$JD{P_CPCMsG_A<)DB8(zA_RfPMrw+&S+ZrU zJeaqN1`IV$cs{(P021c6{l*iC<)g#)ysYMjuk3kD(5XY;5EX-0w(VL6CU<6r?=!07 z<-vG6N{V_qk+KnyF4>Y&8%OP+@-nNlcRJ>6zLPC&&4H1$lP^c{$i+nYACA8wY)Pe$ z)GK_K+*_0F3%1SFW2Osttn*afi1yS)Dn13Wv3<7l9cCTTt`^_+g`GyTdeWV{!n$PA zFUYnda*iK*{^;UxF%vR0DTt4nso74tycoNj-5~fYJU`F8euQ*Y{Wg9IQ71!fci??V z4z@N$+20EAG`IRrKC8K_`+;$vfZ6YwZfgKrON)xTd%X;V8Pkz^V;H^vWn*$lqej=> z*Pk*Rj#DEASboVzU$=4~nbh%^fexZjb&<6v8PDWoKLhZBP)9KeL+ z*41qWOP^E`t;B6wwXaU>Owlmw#wilf-Y%#jvm4FNE%-BIQx`k#F|+)Dvh||mXYWYb zIkP$x|E6QhIc?U=s<^J5Zi=^#1r+pn%%&k{~?z>QlMkIqUG-RIP;oHoxXkOA4=grM`@j zU4KmO$vGGd_YFvj8;>KIC34M8yXUc8qz?o6n3@dHw_ZqH@1o>=#Uddm*Yjt|yI)#F SF~Gk_ARSG8jVg6``2PXscs&*X literal 7389 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 b`5uVrQ|(h - - + + + diff --git a/src/frontend/apps/impress/src/features/header/components/Title.tsx b/src/frontend/apps/impress/src/components/Title.tsx similarity index 70% rename from src/frontend/apps/impress/src/features/header/components/Title.tsx rename to src/frontend/apps/impress/src/components/Title.tsx index 79789224b..f6ec1cf69 100644 --- a/src/frontend/apps/impress/src/features/header/components/Title.tsx +++ b/src/frontend/apps/impress/src/components/Title.tsx @@ -1,21 +1,21 @@ import { useTranslation } from 'react-i18next'; -import { Text } from '@/components/'; +import { Text, TextType } from '@/components/'; -type TitleSemanticsProps = { +type TitleSemanticsProps = TextType & { headingLevel?: 'h1' | 'h2' | 'h3'; className?: string; }; export const Title = ({ headingLevel = 'h2', - className, + ...props }: TitleSemanticsProps) => { const { t } = useTranslation(); return ( {t('Docs')} diff --git a/src/frontend/apps/impress/src/components/separators/SeparatedSection.tsx b/src/frontend/apps/impress/src/components/separators/SeparatedSection.tsx index 547b41772..026a76be7 100644 --- a/src/frontend/apps/impress/src/components/separators/SeparatedSection.tsx +++ b/src/frontend/apps/impress/src/components/separators/SeparatedSection.tsx @@ -1,25 +1,23 @@ import { PropsWithChildren } from 'react'; import { css } from 'styled-components'; -import { useCunninghamTheme } from '@/cunningham'; +import { Box, BoxType } from '../Box'; -import { Box } from '../Box'; - -type SeparatedSectionProps = { +type SeparatedSectionProps = BoxType & { showSeparator?: 'top' | 'bottom' | boolean; }; export const SeparatedSection = ({ showSeparator = true, children, + ...boxProps }: PropsWithChildren) => { - const { spacingsTokens } = useCunninghamTheme(); - return ( ; diff --git a/src/frontend/apps/impress/src/features/docs/doc-table-content/components/Heading.tsx b/src/frontend/apps/impress/src/features/docs/doc-table-content/components/Heading.tsx index dc4700d43..e55febc42 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-table-content/components/Heading.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-table-content/components/Heading.tsx @@ -4,8 +4,11 @@ import { css } from 'styled-components'; import { Box, Text } from '@/components'; import { useCunninghamTheme } from '@/cunningham'; import { DocsBlockNoteEditor } from '@/docs/doc-editor/types'; +import { getMainContentElement } from '@/layouts/utils'; import { useResponsiveStore } from '@/stores'; +const SCROLL_MARGIN_TOP = 50; + const leftPaddingMap: { [key: number]: string } = { 3: '1.5rem', 2: '0.9rem', @@ -57,11 +60,26 @@ export const Heading = ({ editor.setTextCursorPosition(headingId, 'end'); const blockEl = document.getElementById(headingId); - blockEl?.scrollIntoView({ - behavior: 'smooth', - inline: 'start', - block: 'start', - }); + + // Try to scroll the main content container instead of the block itself + // to avoid the block being hidden behind the header + const container = getMainContentElement(); + + if (blockEl && container) { + const top = + blockEl.getBoundingClientRect().top - + container.getBoundingClientRect().top + + container.scrollTop - + SCROLL_MARGIN_TOP; + + container.scrollTo({ top, behavior: 'smooth' }); + } else { + blockEl?.scrollIntoView({ + behavior: 'smooth', + inline: 'start', + block: 'start', + }); + } }} $radius="var(--c--globals--spacings--st)" $background={ diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx index b9618e9e7..d32e97407 100644 --- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx +++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx @@ -79,12 +79,13 @@ export const DocsGrid = ({ return ( @@ -193,7 +193,7 @@ const DocGridTitleBar = ({ target }: { target: DocDefaultFilter }) => { { - const { t } = useTranslation(); - const { data: config } = useConfig(); - const { spacingsTokens } = useCunninghamTheme(); - const { isLargeScreen } = useResponsiveStore(); - - const icon = config?.theme_customization?.header?.icon; - - return ( - <> - - - {!isLargeScreen && } - - - {icon && ( - rest)(icon)} - /> - )} - - </Box> - </StyledLink> - {isLargeScreen && ( - <Box - className="--docs--header-block-right" - $align="center" - $gap={spacingsTokens['sm']} - $direction="row" - > - <ButtonLogin /> - </Box> - )} - </Box> - </> - ); -}; diff --git a/src/frontend/apps/impress/src/features/header/components/index.ts b/src/frontend/apps/impress/src/features/header/components/index.ts index 127632913..f2a5d7552 100644 --- a/src/frontend/apps/impress/src/features/header/components/index.ts +++ b/src/frontend/apps/impress/src/features/header/components/index.ts @@ -1,2 +1 @@ -export * from './Header'; -export * from './Title'; +export * from './HeaderFloatingBar'; diff --git a/src/frontend/apps/impress/src/features/header/conf.ts b/src/frontend/apps/impress/src/features/header/conf.ts deleted file mode 100644 index b50057ad7..000000000 --- a/src/frontend/apps/impress/src/features/header/conf.ts +++ /dev/null @@ -1 +0,0 @@ -export const HEADER_HEIGHT = 52; diff --git a/src/frontend/apps/impress/src/features/header/index.ts b/src/frontend/apps/impress/src/features/header/index.ts index e32f9ebdb..ef6330a0f 100644 --- a/src/frontend/apps/impress/src/features/header/index.ts +++ b/src/frontend/apps/impress/src/features/header/index.ts @@ -1,3 +1 @@ export * from './components/'; -export * from './conf'; -export * from './types'; diff --git a/src/frontend/apps/impress/src/features/home/components/HomeBottom.tsx b/src/frontend/apps/impress/src/features/home/components/HomeBottom.tsx index 38cfafd8b..5374e6330 100644 --- a/src/frontend/apps/impress/src/features/home/components/HomeBottom.tsx +++ b/src/frontend/apps/impress/src/features/home/components/HomeBottom.tsx @@ -2,10 +2,10 @@ import { useTranslation } from 'react-i18next'; import IconDocs from '@/assets/icons/icon-docs.svg'; import { Box, Text } from '@/components'; +import { Title } from '@/components/Title'; import { useConfig } from '@/core/config'; import { useCunninghamTheme } from '@/cunningham'; import { ProConnectButton } from '@/features/auth'; -import { Title } from '@/features/header'; import { useResponsiveStore } from '@/stores'; export function HomeBottom() { diff --git a/src/frontend/apps/impress/src/features/home/components/HomeContent.tsx b/src/frontend/apps/impress/src/features/home/components/HomeContent.tsx index 5a516ab29..eec4e3fc5 100644 --- a/src/frontend/apps/impress/src/features/home/components/HomeContent.tsx +++ b/src/frontend/apps/impress/src/features/home/components/HomeContent.tsx @@ -4,7 +4,6 @@ import { css } from 'styled-components'; import { Box, Icon, Text } from '@/components'; import { Footer } from '@/features/footer'; -import { LeftPanel } from '@/features/left-panel'; import { MAIN_LAYOUT_ID } from '@/layouts/conf'; import { useResponsiveStore } from '@/stores'; @@ -50,11 +49,6 @@ export function HomeContent() { `} > <HomeHeader /> - {isSmallMobile && ( - <Box $css="& .--docs--left-panel-header{display: none;}"> - <LeftPanel /> - </Box> - )} <Box $css={css` height: calc(100vh - ${getHeaderHeight(isSmallMobile)}px); diff --git a/src/frontend/apps/impress/src/features/home/components/HomeHeader.tsx b/src/frontend/apps/impress/src/features/home/components/HomeHeader.tsx index e33746354..8dfe4c08c 100644 --- a/src/frontend/apps/impress/src/features/home/components/HomeHeader.tsx +++ b/src/frontend/apps/impress/src/features/home/components/HomeHeader.tsx @@ -1,12 +1,11 @@ import Image from 'next/image'; import { Box } from '@/components'; +import { Title } from '@/components/Title'; import { Waffle } from '@/components/Waffle'; import { useConfig } from '@/core'; import { useCunninghamTheme } from '@/cunningham'; -import { Title } from '@/features/header'; import { LanguagePickerLegacy } from '@/features/language'; -import { LeftPanelToggleMobile } from '@/features/left-panel'; import { useResponsiveStore } from '@/stores'; export const HEADER_HEIGHT = 91; @@ -38,14 +37,9 @@ export const HomeHeader = () => { $align="center" $gap="2rem" $direction="row" - $width={isSmallMobile ? '100%' : 'auto'} + $width="auto" $justify="center" > - {isSmallMobile && ( - <Box $position="absolute" $css="left: 1rem;"> - <LeftPanelToggleMobile /> - </Box> - )} {!isSmallMobile && logo?.src && ( <Image priority @@ -78,12 +72,14 @@ export const HomeHeader = () => { {icon?.withTitle && <Title />} </Box> </Box> - {!isSmallMobile && ( - <Box $direction="row" $gap="1rem" $align="center"> - <LanguagePickerLegacy /> - <Waffle /> - </Box> - )} + <Box + $direction="row" + $gap={!isSmallMobile ? '1rem' : undefined} + $align="center" + > + <LanguagePickerLegacy /> + <Waffle /> + </Box> </Box> ); }; diff --git a/src/frontend/apps/impress/src/features/language/components/LanguagePicker.tsx b/src/frontend/apps/impress/src/features/language/components/LanguagePicker.tsx index 75b219fff..1eba4ff33 100644 --- a/src/frontend/apps/impress/src/features/language/components/LanguagePicker.tsx +++ b/src/frontend/apps/impress/src/features/language/components/LanguagePicker.tsx @@ -14,6 +14,7 @@ import { getMatchingLocales, useSynchronizedLanguage, } from '@/features/language'; +import { useResponsiveStore } from '@/stores/useResponsiveStore'; /** * LanguagePickerLegacy component for selecting language. @@ -22,6 +23,7 @@ import { * @returns JSX.Element */ export const LanguagePickerLegacy = () => { + const { isSmallMobile } = useResponsiveStore(); const { t, i18n } = useTranslation(); const { data: conf } = useConfig(); const { data: user } = useAuthQuery(); @@ -87,7 +89,9 @@ export const LanguagePickerLegacy = () => { $gap="0.5rem" $align="center" > - <Icon iconName="translate" $color="inherit" $size="xl" /> + {!isSmallMobile && ( + <Icon iconName="translate" $color="inherit" $size="xl" /> + )} <span lang={toLangTag(currentLanguageCode)}> {currentLanguageLabel} </span> diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelFooter.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelFooter.tsx index 54082afac..c26266183 100644 --- a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelFooter.tsx +++ b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelFooter.tsx @@ -21,7 +21,7 @@ export const LeftPanelFooter = () => { }; return ( - <SeparatedSection showSeparator="top"> + <SeparatedSection showSeparator="top" $margin={{ top: 'auto' }}> <Box $padding={{ horizontal: 'sm' }} $justify="space-between" diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx index 72f6c9f23..b234e1ae0 100644 --- a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx +++ b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx @@ -1,57 +1,130 @@ import { Button } from '@gouvfr-lasuite/cunningham-react'; -import { t } from 'i18next'; +import Image from 'next/image'; import { useRouter } from 'next/router'; -import { PropsWithChildren } from 'react'; +import { useTranslation } from 'react-i18next'; +import { css } from 'styled-components'; -import HomeSVG from '@/assets/icons/ui-kit/house-rounded.svg'; -import { Box, SeparatedSection } from '@/components'; +import { + Box, + ButtonCloseModal, + SeparatedSection, + StyledLink, +} from '@/components'; +import { Title } from '@/components/Title'; +import { useConfig } from '@/core'; +import { NewDocButton } from '@/docs/doc-management/components/NewDocButton'; +import { DocSearchButtonModal } from '@/docs/doc-search/components/DocSearchButtonModal'; import { useAuth } from '@/features/auth'; -import { NewDocButton } from '@/features/docs/doc-management/components/NewDocButton'; -import { DocSearchButtonModal } from '@/features/docs/doc-search/components/DocSearchButtonModal'; +import HomeSVG from '@/icons/house-rounded.svg'; +import { useResponsiveStore } from '@/stores'; import { useLeftPanelStore } from '../stores'; -export const LeftPanelHeader = ({ children }: PropsWithChildren) => { - const router = useRouter(); - const { authenticated } = useAuth(); - - const { togglePanel, closePanel } = useLeftPanelStore(); - - const goToHome = () => { - void router.push('/'); - togglePanel({ type: 'mobile' }); - }; +export const LeftPanelHeader = () => { + const { data: config } = useConfig(); + const { isMobile } = useResponsiveStore(); + const { closePanel } = useLeftPanelStore(); + const icon = config?.theme_customization?.header?.icon; return ( <Box $width="100%" className="--docs--left-panel-header"> - <SeparatedSection> - <Box - $padding={{ horizontal: 'sm' }} - $width="100%" - $direction="row" - $justify="space-between" - $align="center" + <Box + $padding={{ horizontal: 'sm' }} + $direction="row" + $align="center" + $gap="2xs" + $minHeight="68px" + > + <StyledLink + href="/" + data-testid="header-logo-link" + $css={css` + outline: none; + &:focus-visible { + box-shadow: 0 0 0 2px var(--c--globals--colors--brand-400) !important; + border-radius: var(--c--globals--spacings--st); + } + `} > - {authenticated && ( - <NewDocButton onClose={() => closePanel({ type: 'mobile' })} /> - )} - <Box $direction="row" $gap="2px" $margin={{ left: 'auto' }}> - {router.pathname !== '/' && ( - <Button - data-testid="home-button" - onClick={goToHome} - aria-label={t('Back to homepage')} - size="medium" - color="brand" - variant="tertiary" - icon={<HomeSVG aria-hidden="true" width={24} height={24} />} + <Box + $align="center" + $gap="var(--c--globals--spacings--4xs)" + $direction="row" + $position="relative" + $height="fit-content" + $margin={{ top: 'auto' }} + > + {icon && ( + <Image + data-testid="header-icon-docs" + width={0} + height={0} + priority + {...(({ withTitle: _, ...rest }) => rest)(icon)} /> )} - <DocSearchButtonModal /> + <Title + headingLevel="h1" + className={icon?.withTitle ? undefined : 'sr-only'} + $size="1.8rem" + /> </Box> - </Box> - </SeparatedSection> - {children} + </StyledLink> + {isMobile && ( + <Box $margin={{ left: 'auto' }}> + <ButtonCloseModal + onClick={closePanel} + aria-label="Close left panel" + /> + </Box> + )} + </Box> + <LeftPanelHeaderActions /> </Box> ); }; +export const LeftPanelHeaderActions = () => { + const router = useRouter(); + const { authenticated } = useAuth(); + const { togglePanel, closePanel } = useLeftPanelStore(); + const { t } = useTranslation(); + const { isMobile } = useResponsiveStore(); + + const goToHome = () => { + void router.push('/'); + + if (isMobile) { + togglePanel(); + } + }; + + return ( + <SeparatedSection> + <Box + $padding={{ horizontal: 'sm' }} + $width="100%" + $direction="row" + $justify="space-between" + $align="center" + > + {authenticated && ( + <NewDocButton onClose={() => isMobile && closePanel()} /> + )} + <Box $direction="row" $gap="2px" $margin={{ left: 'auto' }}> + {router.pathname !== '/' && ( + <Button + data-testid="home-button" + onClick={goToHome} + aria-label={t('Back to homepage')} + size="medium" + color="brand" + variant="tertiary" + icon={<HomeSVG aria-hidden="true" width={24} height={24} />} + /> + )} + <DocSearchButtonModal /> + </Box> + </Box> + </SeparatedSection> + ); +}; diff --git a/src/frontend/apps/impress/src/features/header/types.ts b/src/frontend/apps/impress/src/features/left-panel/types.ts similarity index 100% rename from src/frontend/apps/impress/src/features/header/types.ts rename to src/frontend/apps/impress/src/features/left-panel/types.ts diff --git a/src/frontend/apps/impress/src/features/right-panel/components/RightPanel.tsx b/src/frontend/apps/impress/src/features/right-panel/components/RightPanel.tsx index 0afe2c7e0..7b090e9b3 100644 --- a/src/frontend/apps/impress/src/features/right-panel/components/RightPanel.tsx +++ b/src/frontend/apps/impress/src/features/right-panel/components/RightPanel.tsx @@ -6,7 +6,6 @@ import { Box } from '@/components'; import { CommentSideBar } from '@/features/docs/doc-comments/components/CommentSideBar'; import { useDocStore, useProviderStore } from '@/features/docs/doc-management'; import { TableContentSideBar } from '@/features/docs/doc-table-content/components/TableContentSideBar'; -import { HEADER_HEIGHT } from '@/features/header'; import { useFocusStore, useResponsiveStore } from '@/stores'; import { @@ -77,7 +76,7 @@ export const RightPanel = () => { aria-label={panelLabel} inert={!isPanelOpen} $width="300px" - $height={`calc(100dvh - ${HEADER_HEIGHT}px)`} + $height="100dvh" $position={isMobile ? 'absolute' : 'sticky'} $zIndex={25} $hasTransition diff --git a/src/frontend/apps/impress/src/hooks/useRouteChangeCompleteFocus.tsx b/src/frontend/apps/impress/src/hooks/useRouteChangeCompleteFocus.tsx index 8848b7273..446438c0a 100644 --- a/src/frontend/apps/impress/src/hooks/useRouteChangeCompleteFocus.tsx +++ b/src/frontend/apps/impress/src/hooks/useRouteChangeCompleteFocus.tsx @@ -34,21 +34,10 @@ export const useRouteChangeCompleteFocus = () => { return; } - const prefersReducedMotion = window.matchMedia( - '(prefers-reduced-motion: reduce)', - ).matches; - if (isKeyboardNavigationRef.current) { focusMainContentStart({ preventScroll: true }); isKeyboardNavigationRef.current = false; } - if (router.pathname === '/docs/[id]') { - return; - } - focusTarget.scrollIntoView({ - behavior: prefersReducedMotion ? 'auto' : 'smooth', - block: 'start', - }); }); }; diff --git a/src/frontend/apps/impress/src/layouts/MainLayout.tsx b/src/frontend/apps/impress/src/layouts/MainLayout.tsx index 0ae721d2f..9a19293c0 100644 --- a/src/frontend/apps/impress/src/layouts/MainLayout.tsx +++ b/src/frontend/apps/impress/src/layouts/MainLayout.tsx @@ -3,109 +3,63 @@ import { useTranslation } from 'react-i18next'; import { css } from 'styled-components'; import { Box, BoxProps } from '@/components'; -import { Header } from '@/features/header'; -import { HEADER_HEIGHT } from '@/features/header/conf'; import { LeftPanel, ResizableLeftPanel } from '@/features/left-panel'; import { RightPanel } from '@/features/right-panel/components/RightPanel'; import { DocEditorSkeleton, Skeleton } from '@/features/skeletons'; -import { useResponsiveStore } from '@/stores'; import { MAIN_LAYOUT_ID } from './conf'; import { usePanelCoordination } from './usePanelCoordination'; type MainLayoutProps = { - backgroundColor?: 'white' | 'grey'; enableResizablePanel?: boolean; }; export function MainLayout({ children, - backgroundColor = 'white', enableResizablePanel = false, }: PropsWithChildren<MainLayoutProps>) { return ( - <Box className="--docs--main-layout"> - <Header /> - <Box - $direction="row" - $margin={{ top: `${HEADER_HEIGHT}px` }} - $width="100%" - $height={`calc(100dvh - ${HEADER_HEIGHT}px)`} - > - <MainLayoutContent - backgroundColor={backgroundColor} - enableResizablePanel={enableResizablePanel} - > - {children} - </MainLayoutContent> - </Box> + <Box + className="--docs--main-layout" + $direction="row" + $width="100%" + $height="100dvh" + > + <MainLayoutContent enableResizablePanel={enableResizablePanel}> + {children} + </MainLayoutContent> </Box> ); } export interface MainLayoutContentProps { - backgroundColor: 'white' | 'grey'; enableResizablePanel: boolean; } export function MainLayoutContent({ children, - backgroundColor, enableResizablePanel, }: PropsWithChildren<MainLayoutContentProps>) { - const { isLargeScreen } = useResponsiveStore(); - if (enableResizablePanel) { - return ( - <MainResizableLayout backgroundColor={backgroundColor}> - {children} - </MainResizableLayout> - ); - } - - if (!isLargeScreen) { - return ( - <> - <LeftPanel /> - <MainContent backgroundColor={backgroundColor}>{children}</MainContent> - </> - ); + return <MainResizableLayout>{children}</MainResizableLayout>; } return ( <> - <Box - $css={css` - width: 300px; - border-right: 1px solid - var(--c--contextuals--border--surface--primary); - `} - > - <LeftPanel /> - </Box> - <MainContent backgroundColor={backgroundColor}>{children}</MainContent> + <LeftPanel /> + <MainContent>{children}</MainContent> + <RightPanel /> </> ); } -interface MainResizableLayoutProps { - backgroundColor: 'white' | 'grey'; -} - -const MainResizableLayout = ({ - children, - backgroundColor, -}: PropsWithChildren<MainResizableLayoutProps>) => { +const MainResizableLayout = ({ children }: PropsWithChildren) => { usePanelCoordination(); return ( - <ResizableLeftPanel leftPanel={<LeftPanel />}> + <ResizableLeftPanel> <Box $direction="row" $width="100%" $position="relative"> - <MainContent - backgroundColor={backgroundColor} - $flex="auto" - $padding="0" - > + <MainContent $flex="auto" $padding="0"> {children} </MainContent> <RightPanel /> @@ -114,19 +68,11 @@ const MainResizableLayout = ({ ); }; -type MainContentProps = BoxProps & { - backgroundColor: 'white' | 'grey'; -}; - -const MainContent = ({ +export const MainContent = ({ children, - backgroundColor, ...props -}: PropsWithChildren<MainContentProps>) => { - const { isDesktop } = useResponsiveStore(); - +}: PropsWithChildren<BoxProps>) => { const { t } = useTranslation(); - const currentBackgroundColor = !isDesktop ? 'white' : backgroundColor; return ( <Box @@ -137,14 +83,9 @@ const MainContent = ({ $align="center" $flex={1} $width="100%" - $height={`calc(100dvh - ${HEADER_HEIGHT}px)`} + $height="100dvh" $position="relative" - $padding={isDesktop ? 'base' : '0'} - $background={ - currentBackgroundColor === 'white' - ? 'var(--c--contextuals--background--surface--primary)' - : 'var(--c--contextuals--background--surface--tertiary)' - } + $background="var(--c--contextuals--background--surface--primary)" $css={css` overflow-y: auto; overflow-x: clip; diff --git a/src/frontend/apps/impress/src/layouts/PageLayout.tsx b/src/frontend/apps/impress/src/layouts/PageLayout.tsx index 44746e4c5..ceae98326 100644 --- a/src/frontend/apps/impress/src/layouts/PageLayout.tsx +++ b/src/frontend/apps/impress/src/layouts/PageLayout.tsx @@ -1,14 +1,11 @@ import { PropsWithChildren } from 'react'; -import { useTranslation } from 'react-i18next'; -import { css } from 'styled-components'; import { Box } from '@/components'; import { Footer } from '@/features/footer'; -import { HEADER_HEIGHT, Header } from '@/features/header'; +import { HeaderFloatingBar } from '@/features/header/components/HeaderFloatingBar'; import { LeftPanel } from '@/features/left-panel'; -import { useResponsiveStore } from '@/stores'; -import { MAIN_LAYOUT_ID } from './conf'; +import { MainContent } from './MainLayout'; interface PageLayoutProps { withFooter?: boolean; @@ -20,36 +17,13 @@ export function PageLayout({ withFooter = true, withLeftPanel = true, }: PropsWithChildren<PageLayoutProps>) { - const { isLargeScreen } = useResponsiveStore(); - const { t } = useTranslation(); return ( - <Box - $minHeight={`calc(100vh - ${HEADER_HEIGHT}px)`} - $margin={{ top: `${HEADER_HEIGHT}px` }} - className="--docs--page-layout" - > - <Header /> - <Box - as="main" - role="main" - id={MAIN_LAYOUT_ID} - tabIndex={-1} - $width="100%" - $css={css` - flex-grow: 1; - &:focus { - outline: 3px solid var(--c--globals--colors--primary-600); - outline-offset: -3px; - } - &:focus:not(:focus-visible) { - outline: none; - } - `} - aria-label={t('Main content')} - > - {withLeftPanel && !isLargeScreen && <LeftPanel />} + <Box className="--docs--page-layout" $direction="row" $minHeight="100vh"> + {withLeftPanel && <LeftPanel />} + <MainContent> + <HeaderFloatingBar /> {children} - </Box> + </MainContent> {withFooter && <Footer />} </Box> );