diff --git a/src/components/controls/panelChevron.tsx b/src/components/controls/panelChevron.tsx new file mode 100644 index 000000000..520a99fce --- /dev/null +++ b/src/components/controls/panelChevron.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import styled from 'styled-components'; +import { FaChevronRight, FaChevronDown } from "react-icons/fa"; + +const Container = styled.span` + padding-right: 6px; + color: ${(props) => props.theme.color}; +` + +type Props = { + show: boolean +} + +/** + * An interactive chevron to show/hide a panel's options. + */ +export const PanelChevron = ({ show }: Props) => { + const icon = show ? : + + return ( + + {icon} + + ) +} diff --git a/src/components/controls/panelHeader.tsx b/src/components/controls/panelHeader.tsx index d156269b8..9d16c4ed5 100644 --- a/src/components/controls/panelHeader.tsx +++ b/src/components/controls/panelHeader.tsx @@ -1,9 +1,10 @@ import React from "react"; import { useAppDispatch } from "../../hooks"; import { togglePanelDisplay } from "../../actions/panelDisplay"; -import { HeaderContainer } from "./styles"; +import { PanelSectionHeaderContainer } from "./styles"; import Toggle from "./toggle"; import { AnnotatedTitle, Title, Tooltip } from "./annotatedTitle"; +import { PanelChevron } from "./panelChevron"; /** Panel identifier used internally. */ export type PanelId = string; @@ -15,28 +16,46 @@ type Props = { /** Indicates panel visibility. */ panelIsVisible: boolean + + /** Indicates whether there are options for the panel. */ + hasOptions: boolean + + /** Indicates options visibility. */ + optionsAreVisible: boolean + + /** Update options visibility. */ + setOptionsAreVisible: React.Dispatch> } /** * A header used by all panel controls, containing an interactive title. */ -export const PanelHeader = ({ panel, title, tooltip, panelIsVisible }: Props) => { +export const PanelHeader = ({ panel, title, tooltip, panelIsVisible, hasOptions, optionsAreVisible, setOptionsAreVisible }: Props) => { const dispatch = useAppDispatch(); function togglePanelVisibility() { dispatch(togglePanelDisplay(panel)) } + function toggleOptionsVisibility() { + setOptionsAreVisible(!optionsAreVisible); + } + return ( - - + + + {hasOptions && + } + + - + ); }; diff --git a/src/components/controls/panelSection.tsx b/src/components/controls/panelSection.tsx index 7dc6594b8..de22a67a9 100644 --- a/src/components/controls/panelSection.tsx +++ b/src/components/controls/panelSection.tsx @@ -23,6 +23,14 @@ export const PanelSection = ({ panel, title, tooltip, options=undefined }: Props const panelIsVisible = panelsToDisplay.includes(panel) + // Initially, panel visibility determines options visibility. + const [optionsAreVisible, setOptionsAreVisible] = React.useState(panelIsVisible); + + // Subsequent panel visibility updates also determines options visibility. + React.useEffect(() => { + setOptionsAreVisible(panelIsVisible) + }, [panelIsVisible]) + return ( - {panelIsVisible && options} + {optionsAreVisible && options} ); }; diff --git a/src/components/controls/styles.js b/src/components/controls/styles.js index 668adba3e..26137e4da 100644 --- a/src/components/controls/styles.js +++ b/src/components/controls/styles.js @@ -30,6 +30,10 @@ export const HeaderContainer = styled.div` margin-bottom: 5px; `; +export const PanelSectionHeaderContainer = styled(HeaderContainer)` + cursor: pointer; +` + export const PanelSectionContainer = styled.div` // Less padding is necessary on the top because there is already some space // from HeaderContainer's top margin. @@ -48,7 +52,7 @@ export const PanelSectionContainer = styled.div` `; export const TitleAndIconContainer = styled.span` - display: flex; + display: inline-flex; align-items: center; `;