Skip to content

Commit d6867db

Browse files
authored
[@mantine/core] AppShell: Fix unexpected Header transition when scrollbar becomes hidden via scroll lock (#8420)
* fix: prevent AppShell Header and Footer from bouncing when scrollbar is hidden/unhidden * animte inset-inline-start, inset-inline-end instead * prevent jumping header/footer/aside for both default and alt layout * updated tests
1 parent 608df1d commit d6867db

File tree

6 files changed

+43
-9
lines changed

6 files changed

+43
-9
lines changed

packages/@mantine/core/src/components/AppShell/AppShell.module.css

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@
7575
&:where([data-with-border]) {
7676
border-inline-start: 1px solid var(--app-shell-border-color);
7777
}
78+
79+
:where([data-scroll-locked]) & {
80+
visibility: var(--app-shell-aside-scroll-locked-visibility);
81+
}
7882
}
7983

8084
.main {
@@ -90,12 +94,12 @@
9094
.footer {
9195
position: fixed;
9296
inset-inline: 0;
93-
transition-property: transform, left, right;
97+
transition-property: transform, margin-inline-start, margin-inline-end;
9498
background-color: var(--mantine-color-body);
9599

96100
:where([data-layout='alt']) & {
97-
inset-inline-start: var(--app-shell-navbar-offset, 0rem);
98-
inset-inline-end: var(--app-shell-aside-offset, 0rem);
101+
margin-inline-start: var(--app-shell-navbar-offset, 0rem);
102+
margin-inline-end: var(--app-shell-aside-offset, 0rem);
99103
}
100104
}
101105

packages/@mantine/core/src/components/AppShell/AppShell.story.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useDisclosure } from '@mantine/hooks';
22
import { Button } from '../Button';
33
import { Group } from '../Group';
4+
import { Modal } from '../Modal';
45
import { AppShell } from './AppShell';
56

67
export default { title: 'AppShell' };
@@ -17,6 +18,7 @@ export function Usage() {
1718
const [asideMobileOpened, { toggle: toggleAsideMobile }] = useDisclosure(false);
1819
const [headerOpened, { toggle: toggleHeader }] = useDisclosure(true);
1920
const [footerOpened, { toggle: toggleFooter }] = useDisclosure(true);
21+
const [modalOpened, { toggle: toggleModal }] = useDisclosure(false);
2022

2123
return (
2224
<AppShell
@@ -46,7 +48,9 @@ export function Usage() {
4648
<AppShell.Section grow>Grow section</AppShell.Section>
4749
<AppShell.Section>Last section</AppShell.Section>
4850
</AppShell.Navbar>
49-
<AppShell.Header>Header</AppShell.Header>
51+
<AppShell.Header>
52+
<Group justify="center">Header</Group>
53+
</AppShell.Header>
5054
<AppShell.Main>
5155
<Group>
5256
<Button onClick={toggleHeader}>Toggle header</Button>
@@ -56,8 +60,14 @@ export function Usage() {
5660

5761
<Button onClick={toggleAside}>Toggle aside</Button>
5862
<Button onClick={toggleAsideMobile}>Toggle aside mobile</Button>
63+
64+
<Button onClick={toggleModal}>Open modal</Button>
5965
</Group>
6066
<p>{longContent}</p>
67+
68+
<Modal opened={modalOpened} onClose={toggleModal} title="Modal inside AppShell">
69+
<p>Dialog Content</p>
70+
</Modal>
6171
</AppShell.Main>
6272

6373
<AppShell.Aside>Aside</AppShell.Aside>
@@ -71,6 +81,8 @@ export function AltLayout() {
7181
const [opened, { toggle }] = useDisclosure(true);
7282
const [mobileOpened, { toggle: toggleMobile }] = useDisclosure(false);
7383
const [headerOpened, { toggle: toggleHeader }] = useDisclosure(true);
84+
const [modalOpened, { toggle: toggleModal }] = useDisclosure(false);
85+
7486
return (
7587
<AppShell
7688
padding="md"
@@ -91,17 +103,24 @@ export function AltLayout() {
91103
>
92104
<AppShell.Aside>Aside</AppShell.Aside>
93105
<AppShell.Header>
94-
<Group justify="flex-end">
106+
<Group justify="space-between">
107+
<Button>Left</Button>
108+
<Button>Center</Button>
95109
<Button>Button hidden by the aside</Button>
96110
</Group>
97111
</AppShell.Header>
98-
<AppShell.Main>
112+
<AppShell.Main style={{ height: '200vh' }}>
99113
<Group>
100114
<Button onClick={toggleHeader}>Toggle header</Button>
101115
<Button onClick={toggle}>Toggle navbar</Button>
102116
<Button onClick={toggleMobile}>Toggle navbar mobile</Button>
117+
<Button onClick={toggleModal}>Open modal</Button>
103118
</Group>
104119
<p>Other content</p>
120+
121+
<Modal opened={modalOpened} onClose={toggleModal} title="Modal inside AppShell">
122+
<p>Dialog Content</p>
123+
</Modal>
105124
</AppShell.Main>
106125

107126
<AppShell.Footer>Footer</AppShell.Footer>

packages/@mantine/core/src/components/AppShell/AppShell.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export interface AppShellProps
7979
/** If set, `Navbar`, `Aside`, `Header` and `Footer` components are hidden */
8080
disabled?: boolean;
8181

82-
/** If set, `Header` and `Footer` components include styles to offset scrollbars. Based on `react-remove-scroll`. @default `true` for `layout="default"`, `false` for `layout="alt"` */
82+
/** If set, `Header` and `Footer` components include styles to offset scrollbars. Based on `react-remove-scroll`. @default `true` */
8383
offsetScrollbars?: boolean;
8484
}
8585

@@ -135,7 +135,7 @@ export const AppShell = factory<AppShellFactory>((_props, ref) => {
135135
disabled,
136136
aside,
137137
footer,
138-
offsetScrollbars = layout !== 'alt',
138+
offsetScrollbars = true,
139139
mod,
140140
attributes,
141141
...others

packages/@mantine/core/src/components/AppShell/AppShellAside/AppShellAside.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import cx from 'clsx';
2+
import { RemoveScroll } from 'react-remove-scroll';
13
import {
24
Box,
35
BoxProps,
@@ -50,7 +52,12 @@ export const AppShellAside = factory<AppShellAsideFactory>((_props, ref) => {
5052
component="aside"
5153
ref={ref}
5254
mod={[{ 'with-border': withBorder ?? ctx.withBorder }, mod]}
53-
{...ctx.getStyles('aside', { className, classNames, styles, style })}
55+
{...ctx.getStyles('aside', {
56+
className: cx({ [RemoveScroll.classNames.zeroRight]: ctx.offsetScrollbars }, className),
57+
classNames,
58+
styles,
59+
style,
60+
})}
5461
{...others}
5562
__vars={{ '--app-shell-aside-z-index': `calc(${zIndex ?? ctx.zIndex} + 1)` }}
5663
/>

packages/@mantine/core/src/components/AppShell/AppShellMediaStyles/assign-aside-variables/assign-aside-variables.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ describe('@mantine/core/AppShell/assign-aside-variables', () => {
5858
'--app-shell-aside-width': '100%',
5959
'--app-shell-aside-transform-rtl': 'translateX(calc(var(--app-shell-aside-width) * -1))',
6060
'--app-shell-aside-transform': 'translateX(var(--app-shell-aside-width))',
61+
'--app-shell-aside-scroll-locked-visibility': 'hidden',
6162
},
6263
});
6364
});
@@ -93,6 +94,7 @@ describe('@mantine/core/AppShell/assign-aside-variables', () => {
9394
'--app-shell-aside-offset': '0px !important',
9495
'--app-shell-aside-transform-rtl': 'translateX(calc(var(--app-shell-aside-width) * -1))',
9596
'--app-shell-aside-transform': 'translateX(var(--app-shell-aside-width))',
97+
'--app-shell-aside-scroll-locked-visibility': 'hidden',
9698
},
9799
});
98100
});

packages/@mantine/core/src/components/AppShell/AppShellMediaStyles/assign-aside-variables/assign-aside-variables.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export function assignAsideVariables({
5757
minMediaStyles[breakpointValue]['--app-shell-aside-transform'] = collapsedAsideTransform;
5858
minMediaStyles[breakpointValue]['--app-shell-aside-transform-rtl'] = collapsedAsideTransformRtl;
5959
minMediaStyles[breakpointValue]['--app-shell-aside-offset'] = '0px !important';
60+
minMediaStyles[breakpointValue]['--app-shell-aside-scroll-locked-visibility'] = 'hidden';
6061
}
6162

6263
if (aside?.collapsed?.mobile) {
@@ -66,5 +67,6 @@ export function assignAsideVariables({
6667
maxMediaStyles[breakpointValue]['--app-shell-aside-offset'] = '0px';
6768
maxMediaStyles[breakpointValue]['--app-shell-aside-transform'] = collapsedAsideTransform;
6869
maxMediaStyles[breakpointValue]['--app-shell-aside-transform-rtl'] = collapsedAsideTransformRtl;
70+
maxMediaStyles[breakpointValue]['--app-shell-aside-scroll-locked-visibility'] = 'hidden';
6971
}
7072
}

0 commit comments

Comments
 (0)