feat(resources): make languages collapsible

This commit is contained in:
Jean-Philippe Sirois
2020-01-18 02:55:10 -05:00
parent 423c9949b6
commit 0a9484ffae
6 changed files with 164 additions and 65 deletions
+67 -33
View File
@@ -46,7 +46,6 @@ const childrenSort = sortWith<IFileOrFolder>([
function Tree({
item,
index,
firstLevel,
}: {
item: IFileOrFolder
@@ -74,8 +73,8 @@ function Tree({
)
}
if (firstLevel && index !== undefined) {
return <FirstLevelFolder key={item.title} item={item} index={index} />
if (firstLevel) {
return <FirstLevelFolder key={item.title} item={item} />
}
return <Folder key={item.title} item={item} />
@@ -108,46 +107,81 @@ function Folder({ item }: { item: IFolder }) {
)
}
const FirstLevelFolder = memo(
({ item, index }: { item: IFolder; index: number }) => {
const { current, setCurrent } = useSidebar()
const FirstLevelFolder = memo(({ item }: { item: IFolder }) => {
const { setCurrent } = useSidebar()
useMatchingPath(item.path, () => {
setCurrent(index)
})
useMatchingPath(item.path, () => {
setCurrent(item.title)
})
const collapsed = current !== index
const sortedChildren = childrenSort(item.children)
const sortedChildren = childrenSort(item.children)
return (
<SC.TreeWrapper className="firstLevel" collapsed={collapsed}>
<SC.FirstLabel
className={!collapsed ? "active" : undefined}
onClick={() => setCurrent(index)}
>
{humanize(item.title)}
<SC.CollapseToggler />
</SC.FirstLabel>
<SC.Children>
{sortedChildren.map(node => (
<Tree item={node} />
return (
<SC.TreeWrapper className="firstLevel">
<SC.FirstLabel>{humanize(item.title)}</SC.FirstLabel>
<SC.Children>
{sortedChildren.map(node => (
<Tree item={node} />
))}
</SC.Children>
</SC.TreeWrapper>
)
})
const AllLanguages: FC<{
items: IFileOrFolder[]
expanded: boolean
setExpanded: React.Dispatch<React.SetStateAction<boolean>>
}> = ({ items, expanded, setExpanded }) => {
const { current, setCurrent } = useSidebar()
return (
<SC.ExpandLanguages>
<SC.ExpandLanguagesHeader
onClick={() => setExpanded(prevState => !prevState)}
>
Expand languages {expanded ? <SC.CollapseIcon /> : <SC.ExpandIcon />}
</SC.ExpandLanguagesHeader>
{expanded && (
<SC.ExpandLanguagesList>
{items.map(item => (
<SC.Language
className={current === item.title ? "active" : ""}
onClick={() => {
setCurrent(item.title)
setExpanded(false)
}}
>
{item.title}
</SC.Language>
))}
</SC.Children>
</SC.TreeWrapper>
)
}
)
</SC.ExpandLanguagesList>
)}
</SC.ExpandLanguages>
)
}
export const ResourcesSidebar: FC<HTMLAttributes<HTMLDivElement>> = props => {
const [expandedLanguages, setExpandedLanguages] = useState(false)
const resources = useStaticQuery<IAllResourcesQuery>(ALL_RESOURCES)
const tree = useBuildTree(resources, "/resources")
const sortedTree = sort((a, b) => a.title.localeCompare(b.title), tree)
const languages = useBuildTree(resources, "/resources")
const { current } = useSidebar()
const sortedLanguages = sort(
(a, b) => a.title.localeCompare(b.title),
languages
)
const currentLanguage =
sortedLanguages.find(lang => lang.title === current) || sortedLanguages[0]
return (
<Sidebar {...props}>
{sortedTree.map((node, index) => (
<Tree item={node} index={index} firstLevel={true} />
))}
<AllLanguages
items={sortedLanguages}
expanded={expandedLanguages}
setExpanded={setExpandedLanguages}
/>
<Tree item={currentLanguage} firstLevel={true} />
</Sidebar>
)
}
+63 -25
View File
@@ -1,7 +1,9 @@
import { Link } from "gatsby"
import { transparentize } from "polished"
import styled from "styled-components"
import styled, { css } from "styled-components"
import ChevronUp from "../../icons/chevron-up.svg"
import Collapse from "../../icons/collapse.svg"
import Expand from "../../icons/expand.svg"
export const Children = styled.div`
padding-left: 16px;
@@ -10,6 +12,10 @@ export const Children = styled.div`
flex-direction: column;
align-items: flex-start;
overflow: hidden;
&:last-child {
padding-bottom: 0;
}
`
export const Label = styled.div`
@@ -44,7 +50,7 @@ export const FirstLabel = styled.div`
box-sizing: border-box;
padding: 12px 15px 12px 0;
font-weight: 700;
color: ${props => transparentize(0.5, props.theme.sidebar.foreground)};
color: ${props => props.theme.sidebar.foreground};
cursor: pointer;
&:hover {
@@ -55,30 +61,17 @@ export const FirstLabel = styled.div`
}
}
&.active {
color: ${props => props.theme.sidebar.foreground};
}
svg {
margin-left: auto;
transform: rotate(90deg);
transition: transform 0.3s;
path {
fill: ${props => transparentize(0.5, props.theme.sidebar.foreground)};
}
}
&.active svg {
transform: rotate(0);
path {
fill: ${props => props.theme.sidebar.foreground};
}
}
`
export const TreeWrapper = styled.div<{ collapsed: boolean }>`
export const TreeWrapper = styled.div<{ collapsed?: boolean }>`
display: flex;
flex-direction: column;
width: 100%;
@@ -88,11 +81,6 @@ export const TreeWrapper = styled.div<{ collapsed: boolean }>`
overflow: hidden;
}
& + & {
border-top: 1px solid
${props => transparentize(0.8, props.theme.sidebar.foreground)};
}
${Children} {
display: ${props => (props.collapsed ? "none" : "flex")};
}
@@ -105,22 +93,64 @@ export const TreeWrapper = styled.div<{ collapsed: boolean }>`
export const CollapseToggler = styled(ChevronUp)``
export const PageLink = styled(Link)`
export const CollapseIcon = styled(Collapse)`
margin-left: auto;
path {
fill: ${props => transparentize(0.5, props.theme.sidebar.foreground)};
}
`
export const ExpandIcon = styled(Expand)`
margin-left: auto;
path {
fill: ${props => transparentize(0.5, props.theme.sidebar.foreground)};
}
`
export const ExpandLanguages = styled.div`
padding: 12px 15px 12px 0;
border-bottom: 1px solid
${props => transparentize(0.8, props.theme.sidebar.foreground)};
margin-bottom: 8px;
`
export const ExpandLanguagesHeader = styled.div`
user-select: none;
display: flex;
align-items: center;
cursor: pointer;
color: ${props => transparentize(0.5, props.theme.sidebar.foreground)};
font-weight: 700;
`
export const ExpandLanguagesList = styled.div`
padding: 8px 0;
display: flex;
flex-direction: column;
align-items: flex-start;
overflow: hidden;
`
const item = css`
display: block;
padding: 4px 0 0;
margin-bottom: 4px;
color: ${props => props.theme.sidebar.foreground};
align-self: flex-start;
text-decoration: none;
border-bottom: 2px solid transparent;
cursor: pointer;
&:hover {
border-color: ${props =>
transparentize(0.7, props.theme.sidebar.foreground)};
}
& + & {
margin-top: 4px;
& + &,
${TreeWrapper} + & {
padding: 8px 0 0;
}
&.active {
@@ -132,3 +162,11 @@ export const PageLink = styled(Link)`
border-color: ${props => transparentize(0.7, props.theme.sidebar.active)};
}
`
export const Language = styled.div`
${item};
`
export const PageLink = styled(Link)`
${item};
`