summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2021-08-25 09:13:04 +0200
committerOskar Nyberg <oskar@mullvad.net>2021-08-25 09:13:04 +0200
commitef901c6bf5ce1bddc688caa02667fb52cf5a43ca (patch)
treee69ea05f2040d0066b2173dcc218a5d817ce6d2f
parentb1b565681460d6f339372911e5bb786ac6b71c29 (diff)
parent3e06e3ac6589ad5a20a6c2f2c6c36724de1255e7 (diff)
downloadmullvadvpn-ef901c6bf5ce1bddc688caa02667fb52cf5a43ca.tar.xz
mullvadvpn-ef901c6bf5ce1bddc688caa02667fb52cf5a43ca.zip
Merge branch 'fix-select-location-scroll'
-rw-r--r--gui/locales/messages.pot7
-rw-r--r--gui/src/renderer/components/SelectLocation.tsx115
-rw-r--r--gui/src/renderer/components/SelectLocationStyles.tsx12
-rw-r--r--gui/src/renderer/components/SettingsHeader.tsx6
4 files changed, 84 insertions, 56 deletions
diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot
index 06f07ecf2a..7ad0485586 100644
--- a/gui/locales/messages.pot
+++ b/gui/locales/messages.pot
@@ -920,7 +920,7 @@ msgctxt "select-location-nav"
msgid "Exit"
msgstr ""
-#. Heading in select location view
+#. Title label in navigation bar
msgctxt "select-location-nav"
msgid "Select location"
msgstr ""
@@ -941,6 +941,11 @@ msgctxt "select-location-view"
msgid "Providers: %(numberOfProviders)d"
msgstr ""
+#. Heading in select location view
+msgctxt "select-location-view"
+msgid "Select location"
+msgstr ""
+
#. Navigation button to the 'Account' view
msgctxt "settings-view"
msgid "Account"
diff --git a/gui/src/renderer/components/SelectLocation.tsx b/gui/src/renderer/components/SelectLocation.tsx
index f7a694917f..a125cecfe1 100644
--- a/gui/src/renderer/components/SelectLocation.tsx
+++ b/gui/src/renderer/components/SelectLocation.tsx
@@ -55,6 +55,7 @@ interface IProps {
interface IState {
showFilterMenu: boolean;
+ headingHeight: number;
}
interface ISelectLocationSnapshot {
@@ -63,7 +64,8 @@ interface ISelectLocationSnapshot {
}
export default class SelectLocation extends React.Component<IProps, IState> {
- public state = { showFilterMenu: false };
+ // The default headingHeight value is based on a one-line heading.
+ public state = { showFilterMenu: false, headingHeight: 50 };
private scrollView = React.createRef<CustomScrollbars>();
private spacePreAllocationViewRef = React.createRef<SpacePreAllocationView>();
@@ -76,9 +78,14 @@ export default class SelectLocation extends React.Component<IProps, IState> {
private snapshotByScope: { [index: number]: ISelectLocationSnapshot } = {};
private filterButtonRef = React.createRef<HTMLDivElement>();
+ private headingRef = React.createRef<HTMLHeadingElement>();
public componentDidMount() {
this.scrollToSelectedCell();
+ this.setState((state) => ({
+ // 10 px is the margin ontop of the heading.
+ headingHeight: (this.headingRef.current?.offsetHeight ?? state.headingHeight) + 10,
+ }));
}
public componentDidUpdate(
@@ -117,10 +124,15 @@ export default class SelectLocation extends React.Component<IProps, IState> {
<Layout onClick={this.onClickAnywhere}>
<StyledContainer>
<NavigationContainer>
- <NavigationBar alwaysDisplayBarTitle={true}>
+ <NavigationBar>
<NavigationItems>
<CloseBarItem action={this.props.onClose} />
- <TitleBarItem />
+ <TitleBarItem>
+ {
+ // TRANSLATORS: Title label in navigation bar
+ messages.pgettext('select-location-nav', 'Select location')
+ }
+ </TitleBarItem>
<StyledFilterContainer ref={this.filterButtonRef}>
<StyledFilterIconButton
@@ -143,55 +155,60 @@ export default class SelectLocation extends React.Component<IProps, IState> {
)}
</StyledFilterContainer>
</NavigationItems>
- <StyledNavigationBarAttachment>
- <StyledSettingsHeader>
- <HeaderTitle>
- {
- // TRANSLATORS: Heading in select location view
- messages.pgettext('select-location-nav', 'Select location')
- }
- </HeaderTitle>
- </StyledSettingsHeader>
-
- {this.props.providers.length > 0 && (
- <StyledProviderCountRow>
- {messages.pgettext('select-location-view', 'Filtered:')}
- <StyledProvidersCount>
- {sprintf(
- messages.pgettext(
- 'select-location-view',
- 'Providers: %(numberOfProviders)d',
- ),
- {
- numberOfProviders: this.props.providers.length,
- },
- )}
- <StyledClearProvidersButton
- aria-label={messages.gettext('Clear')}
- onClick={this.props.onClearProviders}>
- <ImageView
- height={16}
- width={16}
- source="icon-close"
- tintColor={colors.white60}
- tintHoverColor={colors.white80}
- />
- </StyledClearProvidersButton>
- </StyledProvidersCount>
- </StyledProviderCountRow>
- )}
- {this.props.allowBridgeSelection && (
- <StyledScopeBar
- defaultSelectedIndex={this.props.locationScope}
- onChange={this.props.onChangeLocationScope}>
- <ScopeBarItem>{messages.pgettext('select-location-nav', 'Entry')}</ScopeBarItem>
- <ScopeBarItem>{messages.pgettext('select-location-nav', 'Exit')}</ScopeBarItem>
- </StyledScopeBar>
- )}
- </StyledNavigationBarAttachment>
</NavigationBar>
<NavigationScrollbars ref={this.scrollView}>
<SpacePreAllocationView ref={this.spacePreAllocationViewRef}>
+ <StyledNavigationBarAttachment top={-this.state.headingHeight}>
+ <StyledSettingsHeader>
+ <HeaderTitle ref={this.headingRef}>
+ {
+ // TRANSLATORS: Heading in select location view
+ messages.pgettext('select-location-view', 'Select location')
+ }
+ </HeaderTitle>
+ </StyledSettingsHeader>
+
+ {this.props.providers.length > 0 && (
+ <StyledProviderCountRow>
+ {messages.pgettext('select-location-view', 'Filtered:')}
+ <StyledProvidersCount>
+ {sprintf(
+ messages.pgettext(
+ 'select-location-view',
+ 'Providers: %(numberOfProviders)d',
+ ),
+ {
+ numberOfProviders: this.props.providers.length,
+ },
+ )}
+ <StyledClearProvidersButton
+ aria-label={messages.gettext('Clear')}
+ onClick={this.props.onClearProviders}>
+ <ImageView
+ height={16}
+ width={16}
+ source="icon-close"
+ tintColor={colors.white60}
+ tintHoverColor={colors.white80}
+ />
+ </StyledClearProvidersButton>
+ </StyledProvidersCount>
+ </StyledProviderCountRow>
+ )}
+ {this.props.allowBridgeSelection && (
+ <StyledScopeBar
+ defaultSelectedIndex={this.props.locationScope}
+ onChange={this.props.onChangeLocationScope}>
+ <ScopeBarItem>
+ {messages.pgettext('select-location-nav', 'Entry')}
+ </ScopeBarItem>
+ <ScopeBarItem>
+ {messages.pgettext('select-location-nav', 'Exit')}
+ </ScopeBarItem>
+ </StyledScopeBar>
+ )}
+ </StyledNavigationBarAttachment>
+
<StyledContent>
{this.props.locationScope === LocationScope.relay ? (
<ExitLocations
diff --git a/gui/src/renderer/components/SelectLocationStyles.tsx b/gui/src/renderer/components/SelectLocationStyles.tsx
index 0da4c40184..308421a46f 100644
--- a/gui/src/renderer/components/SelectLocationStyles.tsx
+++ b/gui/src/renderer/components/SelectLocationStyles.tsx
@@ -20,10 +20,13 @@ export const StyledContent = styled.div({
overflow: 'visible',
});
-export const StyledNavigationBarAttachment = styled.div({
- marginTop: '8px',
- paddingHorizontal: '4px',
-});
+export const StyledNavigationBarAttachment = styled.div({}, (props: { top: number }) => ({
+ position: 'sticky',
+ top: `${props.top}px`,
+ padding: '8px 18px 8px 16px',
+ backgroundColor: colors.darkBlue,
+ zIndex: 1,
+}));
export const StyledFilterContainer = styled.div({
position: 'relative',
@@ -45,6 +48,7 @@ export const StyledFilterMenu = styled.div({
borderRadius: '4px',
backgroundColor: colors.darkBlue,
overflow: 'hidden',
+ zIndex: 2,
});
export const StyledFilterByProviderButton = styled.button({
diff --git a/gui/src/renderer/components/SettingsHeader.tsx b/gui/src/renderer/components/SettingsHeader.tsx
index ea1d2ab634..64bcfec595 100644
--- a/gui/src/renderer/components/SettingsHeader.tsx
+++ b/gui/src/renderer/components/SettingsHeader.tsx
@@ -21,12 +21,14 @@ interface ISettingsHeaderProps {
className?: string;
}
-export default function SettingsHeader(props: ISettingsHeaderProps) {
+function SettingsHeader(props: ISettingsHeaderProps, forwardRef: React.Ref<HTMLDivElement>) {
return (
- <Container className={props.className}>
+ <Container ref={forwardRef} className={props.className}>
{React.Children.map(props.children, (child) => {
return React.isValidElement(child) ? <ContentWrapper>{child}</ContentWrapper> : undefined;
})}
</Container>
);
}
+
+export default React.forwardRef(SettingsHeader);