-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new floating panels component (#346)
* Add new floating panels component * Added dynamic max height calculations * fixed positioning * Fixed max-height scroll issues * removed unneeded code complexity * updated component tests * 3.2.48
- Loading branch information
Showing
9 changed files
with
348 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { Story, Canvas, Controls, Meta } from "@storybook/addon-docs"; | ||
import FloatingPanels from "."; | ||
import * as stories from "./FloatingPanels.stories"; | ||
|
||
<Meta of={stories} /> | ||
|
||
# Floating Panels | ||
|
||
The Floating Panels Component is a flexible React component that creates a set of collapsible panels floating on the screen. It's ideal for displaying information or controls that need to be easily accessible but not always visible. | ||
|
||
## Features | ||
|
||
- Customisable container height | ||
- Customisable position on the screen | ||
- Expandable/collapsible panels | ||
- Configurable icons and titles for each panel | ||
- can default specific panels to start expanded on load | ||
- Ability to include any content within panels (can pass content as a component) | ||
|
||
## Usage | ||
|
||
```js run | ||
const panels = [ | ||
{ | ||
id: "legend", | ||
iconName: "map", | ||
title: "Legend", | ||
defaultExpanded: true, | ||
content: <div>Legend content goes here</div> | ||
}, | ||
{ | ||
id: "view-options", | ||
iconName: "eye", | ||
title: "View Options", | ||
defaultExpanded: true, | ||
content: <div>View options content goes here</div> | ||
}, | ||
{ | ||
id: "person-details", | ||
iconName: "user", | ||
title: "Person Details", | ||
content: <div>Person details content goes here</div> | ||
}, | ||
{ | ||
id: "properties", | ||
iconName: "gear", | ||
title: "Properties", | ||
content: <div>Properties content goes here</div> | ||
} | ||
// Add more panels as needed... | ||
]; | ||
``` | ||
|
||
<Canvas of={stories.defaultFloatingPanels} /> | ||
|
||
## Properties | ||
|
||
<Controls component={FloatingPanels} /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import React from "react"; | ||
import FloatingPanels from "."; | ||
import Box from "../Box"; | ||
import { far } from "@fortawesome/free-regular-svg-icons"; | ||
import { library } from "@fortawesome/fontawesome-svg-core"; | ||
import TextInput from "../TextInput"; | ||
import Toggle from "../Toggle"; | ||
import Spacer from "../Spacer"; | ||
import Badge from "../Badge"; | ||
import { P } from "../Typography"; | ||
|
||
library.add(far); | ||
|
||
export default { | ||
title: "Components/FloatingPanels", | ||
decorators: [(storyFn) => <Box minHeight="600px">{storyFn()}</Box>], | ||
component: FloatingPanels | ||
}; | ||
const Properties = () => { | ||
return ( | ||
<> | ||
<Spacer mb="r"> | ||
<Badge variant="secondary">Blah</Badge> | ||
<P> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do | ||
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad | ||
minim veniam, quis nostrud exercitation ullamco laboris nisi ut | ||
aliquip ex ea commodo consequat. Duis aute irure dolor in | ||
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla | ||
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in | ||
culpa qui officia deserunt mollit anim id est laborum. | ||
</P> | ||
<Toggle id="toggle1" label="Group items" small /> | ||
<Toggle id="toggle2" label="Show teams" small /> | ||
</Spacer> | ||
<TextInput | ||
id="textInput1" | ||
key="textInput1" | ||
type="text" | ||
label="Full name" | ||
placeholder="E.g. John Smith" | ||
my="20px" | ||
/> | ||
</> | ||
); | ||
}; | ||
const panels = [ | ||
{ | ||
id: "view-options", | ||
iconName: "eye", | ||
title: "View Options", | ||
defaultExpanded: true, | ||
content: <Properties /> | ||
}, | ||
{ | ||
id: "properties", | ||
iconName: "sun", | ||
title: "Properties", | ||
content: <Properties /> | ||
}, | ||
{ | ||
id: "person-details", | ||
iconName: "user", | ||
title: "Person Details", | ||
content: <Properties /> | ||
} | ||
]; | ||
|
||
export const defaultFloatingPanels = () => { | ||
return ( | ||
<FloatingPanels | ||
panels={panels} | ||
containerHeight={500} | ||
position={{ right: 20, top: 20 }} | ||
/> | ||
); | ||
}; | ||
|
||
defaultFloatingPanels.storyName = "Default Floating Panels"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import styled from "styled-components"; | ||
import { themeGet } from "@styled-system/theme-get"; | ||
|
||
export const Container = styled.div` | ||
z-index: 2; | ||
position: absolute; | ||
display: flex; | ||
flex-direction: column; | ||
gap: 8px; | ||
width: 300px; | ||
max-height: ${({ containerHeight }) => | ||
containerHeight ? `${containerHeight}px` : "100%"}; | ||
${({ position }) => | ||
Object.entries(position) | ||
.filter(([, value]) => value !== undefined) | ||
.map( | ||
([key, value]) => | ||
`${key}: ${typeof value === "number" ? `${value}px` : value};` | ||
) | ||
.join("\n")} | ||
`; | ||
|
||
export const PanelWrapper = styled.div` | ||
background: white; | ||
border: ${({ isExpanded, theme }) => | ||
isExpanded ? `1px solid ${theme.colors.greyLighter}` : "1px solid white"}; | ||
border-radius: 8px; | ||
border-radius: 0 0 8px 8px; | ||
box-shadow: ${({ isExpanded }) => | ||
isExpanded ? "0 1px 3px rgba(0, 0, 0, 0.1)" : "none"}; | ||
overflow-y: ${({ isExpanded }) => (isExpanded ? "auto" : "hidden")}; | ||
// min-height: 48px; | ||
padding: ${({ isExpanded }) => (isExpanded ? "0 12px 12px 12px" : "0 12px")}; | ||
margin-top: 46px; | ||
max-height: ${({ isExpanded }) => (isExpanded ? "none" : "0")}; | ||
transition: max-height 0.3s ease-in-out; | ||
`; | ||
|
||
export const PanelHeader = styled.button` | ||
font-family: ${themeGet("fonts.main")}; | ||
box-shadow: 0 0 4px rgba(0, 0, 0, 0.1); | ||
color: ${themeGet("colors.greyDarkest")}; | ||
width: 100%; | ||
margin-left: -13px; | ||
margin-top: -46px; | ||
border-radius: ${({ isExpanded }) => (isExpanded ? "8px 8px 0 0" : "8px")}; | ||
appearance: none; | ||
background-color: white; | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
padding: 12px 12px 12px 12px; | ||
height: 46px; | ||
position: fixed; | ||
width: 300px; | ||
border-bottom: ${({ isExpanded, theme }) => | ||
isExpanded ? `1px solid ${theme.colors.greyLighter}` : "none"}; | ||
border: solid 1px ${themeGet("colors.greyLighter")}; | ||
z-index: 1; | ||
cursor: pointer; | ||
user-select: none; | ||
transition: padding 0.3s ease-in-out; | ||
&:focus { | ||
outline: none; | ||
} | ||
`; | ||
|
||
export const HeaderContent = styled.div` | ||
display: flex; | ||
align-items: center; | ||
gap: 12px; | ||
`; | ||
|
||
export const Title = styled.span` | ||
font-size: 14px; | ||
font-weight: 500; | ||
`; | ||
|
||
export const IconWrapper = styled.div` | ||
background-color: ${themeGet("colors.primary")}; | ||
width: 22px; | ||
height: 22px; | ||
border-radius: 50%; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
`; | ||
|
||
export const ChevronWrapper = styled(IconWrapper)` | ||
transition: background-color 0.3s ease-in-out; | ||
background-color: ${({ isHovered }) => | ||
isHovered ? themeGet("colors.greyLighter") : "white"}; | ||
`; | ||
|
||
export const PanelContent = styled.div` | ||
padding-top: 12px; | ||
display: ${({ isExpanded }) => (isExpanded ? "block" : "none")}; | ||
height: ${({ isExpanded }) => (isExpanded ? "100%" : "0")}; | ||
opacity: ${({ isExpanded }) => (isExpanded ? "1" : "0")}; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import React, { useState } from "react"; | ||
import PropTypes from "prop-types"; | ||
|
||
import Icon from "../Icon"; | ||
import { | ||
PanelWrapper, | ||
PanelHeader, | ||
HeaderContent, | ||
Title, | ||
IconWrapper, | ||
ChevronWrapper, | ||
PanelContent | ||
} from "./FloatingPanels.styles"; | ||
|
||
const Panel = ({ | ||
iconName, | ||
title, | ||
containerHeight, | ||
content, | ||
defaultExpanded = false | ||
}) => { | ||
const [isExpanded, setIsExpanded] = useState(defaultExpanded); | ||
const arrowIcon = isExpanded ? "chevron-up" : "chevron-down"; | ||
const [isHovered, setIsHovered] = useState(false); | ||
return ( | ||
<PanelWrapper containerHeight={containerHeight} isExpanded={isExpanded}> | ||
<PanelHeader | ||
onClick={() => setIsExpanded(!isExpanded)} | ||
isExpanded={isExpanded} | ||
onMouseEnter={() => setIsHovered(true)} | ||
onMouseLeave={() => setIsHovered(false)} | ||
onFocus={() => setIsHovered(true)} | ||
onBlur={() => setIsHovered(false)} | ||
> | ||
<HeaderContent> | ||
<IconWrapper> | ||
<Icon size="xs" color="white" icon={["far", iconName]} /> | ||
</IconWrapper> | ||
<Title>{title}</Title> | ||
</HeaderContent> | ||
<ChevronWrapper isHovered={isHovered}> | ||
<Icon size="sm" icon={["fas", arrowIcon]} color="greyDarker" /> | ||
</ChevronWrapper> | ||
</PanelHeader> | ||
<PanelContent isExpanded={isExpanded}>{content}</PanelContent> | ||
</PanelWrapper> | ||
); | ||
}; | ||
|
||
Panel.propTypes = { | ||
iconName: PropTypes.string.isRequired, | ||
title: PropTypes.string.isRequired, | ||
content: PropTypes.node.isRequired, | ||
defaultExpanded: PropTypes.bool, | ||
containerHeight: PropTypes.number, | ||
isExpanded: PropTypes.bool.isRequired | ||
}; | ||
|
||
Panel.defaultProps = { | ||
defaultExpanded: false | ||
}; | ||
|
||
export default Panel; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import React from "react"; | ||
import { Container } from "./FloatingPanels.styles"; | ||
import Panel from "./Panel"; | ||
import PropTypes from "prop-types"; | ||
|
||
const FloatingPanels = ({ | ||
panels, | ||
containerHeight, | ||
position = { right: 20, top: 20 } | ||
}) => { | ||
return ( | ||
<Container containerHeight={containerHeight} position={position}> | ||
{panels.map((panel) => ( | ||
<Panel key={panel?.id} {...panel} containerHeight={containerHeight} /> | ||
))} | ||
</Container> | ||
); | ||
}; | ||
|
||
FloatingPanels.propTypes = { | ||
panels: PropTypes.arrayOf( | ||
PropTypes.shape({ | ||
id: PropTypes.string.isRequired, | ||
iconName: PropTypes.string.isRequired, | ||
title: PropTypes.string.isRequired, | ||
content: PropTypes.node.isRequired, | ||
defaultExpanded: PropTypes.bool | ||
}) | ||
).isRequired, | ||
containerHeight: PropTypes.number, | ||
position: PropTypes.shape({ | ||
top: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
right: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
bottom: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
left: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) | ||
}) | ||
}; | ||
|
||
FloatingPanels.defaultProps = { | ||
position: { right: 20, top: 20 } | ||
}; | ||
|
||
export default FloatingPanels; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "orcs-design-system", | ||
"version": "3.2.47", | ||
"version": "3.2.48", | ||
"engines": { | ||
"node": "20.12.2" | ||
}, | ||
|