diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2022-11-03 11:46:07 +0100 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2022-11-04 17:30:51 +0100 |
| commit | 590638128ad97135f629032422f4ff6688aeaaab (patch) | |
| tree | 2f124394d2372d3224219c5ef0338c7bc0e945e2 | |
| parent | 2968800c8da2877d7270bef512e543c4dee84223 (diff) | |
| download | mullvadvpn-590638128ad97135f629032422f4ff6688aeaaab.tar.xz mullvadvpn-590638128ad97135f629032422f4ff6688aeaaab.zip | |
Create expandable section component
| -rw-r--r-- | gui/src/renderer/components/ChevronButton.tsx | 6 | ||||
| -rw-r--r-- | gui/src/renderer/components/SplitTunnelingSettings.tsx | 20 | ||||
| -rw-r--r-- | gui/src/renderer/components/cell/Section.tsx | 49 | ||||
| -rw-r--r-- | gui/src/renderer/components/cell/Selector.tsx | 53 |
4 files changed, 87 insertions, 41 deletions
diff --git a/gui/src/renderer/components/ChevronButton.tsx b/gui/src/renderer/components/ChevronButton.tsx index 0485c22caf..6e16fdf6db 100644 --- a/gui/src/renderer/components/ChevronButton.tsx +++ b/gui/src/renderer/components/ChevronButton.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import styled from 'styled-components'; import { colors } from '../../config.json'; -import * as Cell from './cell'; +import { Icon } from './cell/Label'; interface IProps extends React.HTMLAttributes<HTMLButtonElement> { up: boolean; @@ -13,7 +13,7 @@ const Button = styled.button({ background: 'none', }); -const Icon = styled(Cell.Icon)({ +const StyledIcon = styled(Icon)({ flex: 0, alignSelf: 'stretch', justifyContent: 'center', @@ -24,7 +24,7 @@ export default function ChevronButton(props: IProps) { return ( <Button {...otherProps}> - <Icon + <StyledIcon tintColor={colors.white80} tintHoverColor={colors.white} source={up ? 'icon-chevron-up' : 'icon-chevron-down'} diff --git a/gui/src/renderer/components/SplitTunnelingSettings.tsx b/gui/src/renderer/components/SplitTunnelingSettings.tsx index 4cad24f445..f68be19589 100644 --- a/gui/src/renderer/components/SplitTunnelingSettings.tsx +++ b/gui/src/renderer/components/SplitTunnelingSettings.tsx @@ -410,6 +410,16 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett splitTunnelingEnabled && (!filteredNonSplitApplications || filteredNonSplitApplications.length > 0); + const excludedTitle = ( + <Cell.SectionTitle> + {messages.pgettext('split-tunneling-view', 'Excluded apps')} + </Cell.SectionTitle> + ); + + const allTitle = ( + <Cell.SectionTitle>{messages.pgettext('split-tunneling-view', 'All apps')}</Cell.SectionTitle> + ); + return ( <> <SettingsHeader> @@ -428,10 +438,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett {splitTunnelingEnabled && <SearchBar searchTerm={searchTerm} onSearch={setSearchTerm} />} <Accordion expanded={showSplitSection}> - <Cell.Section> - <Cell.SectionTitle> - {messages.pgettext('split-tunneling-view', 'Excluded apps')} - </Cell.SectionTitle> + <Cell.Section sectionTitle={excludedTitle}> <ApplicationList applications={filteredSplitApplications} rowRenderer={excludedRowRenderer} @@ -440,10 +447,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett </Accordion> <Accordion expanded={showNonSplitSection}> - <Cell.Section> - <Cell.SectionTitle> - {messages.pgettext('split-tunneling-view', 'All apps')} - </Cell.SectionTitle> + <Cell.Section sectionTitle={allTitle}> <ApplicationList applications={filteredNonSplitApplications} rowRenderer={includedRowRenderer} diff --git a/gui/src/renderer/components/cell/Section.tsx b/gui/src/renderer/components/cell/Section.tsx index 38df17ebdb..1971e8dcb7 100644 --- a/gui/src/renderer/components/cell/Section.tsx +++ b/gui/src/renderer/components/cell/Section.tsx @@ -2,7 +2,11 @@ import React from 'react'; import styled from 'styled-components'; import { colors } from '../../../config.json'; +import { useBoolean } from '../../lib/utilityHooks'; +import Accordion from '../Accordion'; +import ChevronButton from '../ChevronButton'; import { buttonText, openSans, sourceSansPro } from '../common-styles'; +import { Container } from './Container'; import { Row } from './Row'; const StyledSection = styled.div({ @@ -25,11 +29,50 @@ export const SectionTitle = styled(Row)(buttonText, (props: SectionTitleProps) = export const CellSectionContext = React.createContext<boolean>(false); -export function Section(props: React.HTMLAttributes<HTMLDivElement>) { - const { children, ...otherProps } = props; +interface SectionProps extends React.HTMLAttributes<HTMLDivElement> { + sectionTitle?: React.ReactElement; +} + +export function Section(props: SectionProps) { + const { children, sectionTitle, ...otherProps } = props; return ( <StyledSection {...otherProps}> - <CellSectionContext.Provider value={true}>{children}</CellSectionContext.Provider> + <CellSectionContext.Provider value={true}> + {sectionTitle && <StyledTitleContainer>{sectionTitle}</StyledTitleContainer>} + {children} + </CellSectionContext.Provider> </StyledSection> ); } + +const StyledChevronButton = styled(ChevronButton)({ + padding: 0, + marginRight: '16px', +}); + +const StyledTitleContainer = styled(Container)({ + display: 'flex', + padding: 0, +}); + +interface ExpandableSectionProps extends SectionProps { + expandedInitially?: boolean; +} + +export function ExpandableSection(props: ExpandableSectionProps) { + const { expandedInitially, sectionTitle, ...otherProps } = props; + const [expanded, , , toggleExpanded] = useBoolean(!!expandedInitially); + + const title = ( + <> + {sectionTitle} + <StyledChevronButton up={expanded} onClick={toggleExpanded} /> + </> + ); + + return ( + <Section className={props.className} sectionTitle={title} {...otherProps}> + <Accordion expanded={expanded}>{props.children}</Accordion> + </Section> + ); +} diff --git a/gui/src/renderer/components/cell/Selector.tsx b/gui/src/renderer/components/cell/Selector.tsx index cb0d6344b6..cfd2357d6c 100644 --- a/gui/src/renderer/components/cell/Selector.tsx +++ b/gui/src/renderer/components/cell/Selector.tsx @@ -3,28 +3,15 @@ import styled from 'styled-components'; import { colors } from '../../../config.json'; import { messages } from '../../../shared/gettext'; -import { useBoolean } from '../../lib/utilityHooks'; -import Accordion from '../Accordion'; import { AriaDetails, AriaInput, AriaLabel } from '../AriaGroup'; -import ChevronButton from '../ChevronButton'; import { normalText } from '../common-styles'; import InfoButton from '../InfoButton'; import * as Cell from '.'; -const StyledTitle = styled(Cell.Container)({ - display: 'flex', - padding: 0, -}); - const StyledTitleLabel = styled(Cell.SectionTitle)({ flex: 1, }); -const StyledChevronButton = styled(ChevronButton)({ - padding: 0, - marginRight: '16px', -}); - export interface SelectorItem<T> { label: string; value: T; @@ -52,8 +39,6 @@ interface SelectorProps<T, U> extends CommonSelectorProps<T, U> { } export default function Selector<T, U>(props: SelectorProps<T, U>) { - const [expanded, , , toggleExpanded] = useBoolean(!props.expandable); - const items = props.items.map((item) => { const selected = props.value === item.value; const ref = selected ? (props.selectedCellRef as React.Ref<HTMLButtonElement>) : undefined; @@ -88,8 +73,8 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) { ); } - const title = props.title && ( - <StyledTitle> + const title = props.title ? ( + <> <AriaLabel> <StyledTitleLabel as="label" disabled={props.disabled} thin={props.thinTitle}> {props.title} @@ -100,8 +85,9 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) { <InfoButton>{props.details}</InfoButton> </AriaDetails> )} - {props.expandable && <StyledChevronButton up={expanded} onClick={toggleExpanded} />} - </StyledTitle> + </> + ) : ( + <></> ); // Add potential additional items to the list. Used for custom entry. @@ -112,14 +98,27 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) { </Cell.Group> ); - return ( - <AriaInput> - <Cell.Section role="listbox" className={props.className}> - {title} - {props.expandable ? <Accordion expanded={expanded}>{children}</Accordion> : children} - </Cell.Section> - </AriaInput> - ); + if (props.expandable) { + return ( + <AriaInput> + <Cell.ExpandableSection + role="listbox" + expandedInitially={false} + className={props.className} + sectionTitle={title}> + {children} + </Cell.ExpandableSection> + </AriaInput> + ); + } else { + return ( + <AriaInput> + <Cell.Section role="listbox" className={props.className} sectionTitle={title}> + {children} + </Cell.Section> + </AriaInput> + ); + } } const StyledCellIcon = styled(Cell.Icon)((props: { visible: boolean }) => ({ |
