From 81bb9e2b61e831de8e7ad851a856386336ad9740 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Wed, 27 May 2020 04:53:38 -0400 Subject: [PATCH] feat(resources): support topics + languages in sidebar --- src/components/ArchivesSidebar/index.tsx | 6 +- src/components/ResourcesSidebar/index.tsx | 83 ++++++++++++--------- src/components/ResourcesSidebar/styles.tsx | 25 ++++--- src/components/ResourcesSidebar/useTree.tsx | 13 ++++ src/hooks/useBuildTree.tsx | 4 +- 5 files changed, 80 insertions(+), 51 deletions(-) create mode 100644 src/components/ResourcesSidebar/useTree.tsx diff --git a/src/components/ArchivesSidebar/index.tsx b/src/components/ArchivesSidebar/index.tsx index 4a30c3e..fe93fd8 100644 --- a/src/components/ArchivesSidebar/index.tsx +++ b/src/components/ArchivesSidebar/index.tsx @@ -11,7 +11,9 @@ import * as SC from "./styles" const ALL_ARCHIVES = graphql` query { - allFile(filter: { sourceInstanceName: { eq: "what-is-archive" } }) { + archives: allFile( + filter: { sourceInstanceName: { eq: "what-is-archive" } } + ) { edges { node { relativePath @@ -41,7 +43,7 @@ function Tree({ item }: { item: IFileOrFolder }) { } export const ArchivesSidebar: FC> = (props) => { - const archives = useStaticQuery(ALL_ARCHIVES) + const { archives } = useStaticQuery(ALL_ARCHIVES) const tree = useBuildTree(archives, "/archives") const sortedTree = sort((a, b) => a.title.localeCompare(b.title), tree) diff --git a/src/components/ResourcesSidebar/index.tsx b/src/components/ResourcesSidebar/index.tsx index 1cf6e8d..a86b085 100644 --- a/src/components/ResourcesSidebar/index.tsx +++ b/src/components/ResourcesSidebar/index.tsx @@ -1,21 +1,33 @@ import { graphql, useStaticQuery } from "gatsby" import descend from "ramda/es/descend" -import sort from "ramda/es/sort" import sortWith from "ramda/es/sortWith" import React, { FC, HTMLAttributes, memo, useState } from "react" -import useBuildTree from "../../hooks/useBuildTree" import { useLockBodyScroll } from "../../hooks/useLockBodyScroll" import useSidebar from "../../hooks/useSidebar" import TriangleDown from "../../icons/triangle-down.svg" -import { IAllResourcesQuery, IFileOrFolder, IFolder } from "../../types" +import { IFileOrFolder, IFolder } from "../../types" import { getPath, humanize } from "../../utils" import { Sidebar } from "../Sidebar" import * as SC from "./styles" import useMatchingPath from "./useMatchingPath" +import useTree from "./useTree" const ALL_RESOURCES = graphql` - query { - allFile(filter: { sourceInstanceName: { eq: "resources" } }) { + query AllTopicsAndAllLanguages { + languages: allFile(filter: { sourceInstanceName: { eq: "languages" } }) { + edges { + node { + relativePath + childMarkdownRemark { + frontmatter { + authors + title + } + } + } + } + } + topics: allFile(filter: { sourceInstanceName: { eq: "topics" } }) { edges { node { relativePath @@ -129,16 +141,16 @@ const FirstLevelFolder = memo(({ item }: { item: IFolder }) => { ) }) -const LanguageList: FC<{ +const ResourceList: FC<{ items: IFileOrFolder[] setExpanded: React.Dispatch> }> = ({ items, setExpanded }) => { const { current, setCurrent } = useSidebar() return ( - + {items.map((item) => ( - { @@ -147,55 +159,52 @@ const LanguageList: FC<{ }} > {item.title} - + ))} - + ) } -const AllLanguages: FC<{ - items: IFileOrFolder[] +const ExpandResources: FC<{ expanded: boolean setExpanded: React.Dispatch> -}> = ({ items, expanded, setExpanded }) => { +}> = ({ children, expanded, setExpanded }) => { const { current } = useSidebar() - if (!current) { - return - } + const showList = expanded || !current return ( - - setExpanded((prevState) => !prevState)} - > - Expand languages {expanded ? : } - - {expanded && } - + + {current && ( + setExpanded((prevState) => !prevState)} + > + Expand resources {expanded ? : } + + )} + {showList && children} + ) } export const ResourcesSidebar: FC> = (props) => { - const [expandedLanguages, setExpandedLanguages] = useState(false) - const resources = useStaticQuery(ALL_RESOURCES) - const languages = useBuildTree(resources, "/resources") + const [expanded, setExpanded] = useState(false) + const resources = useStaticQuery(ALL_RESOURCES) + const languagesTree = useTree(resources.languages) + const topicsTree = useTree(resources.topics) const { current } = useSidebar() - const sortedLanguages = sort( - (a, b) => a.title.localeCompare(b.title), - languages - ) - const currentLanguage = sortedLanguages.find((lang) => lang.title === current) + const currentLanguage = languagesTree.find((lang) => lang.title === current) + const currentTopic = topicsTree.find((topic) => topic.title === current) return ( - + + + + {currentLanguage && } + {currentTopic && } ) } diff --git a/src/components/ResourcesSidebar/styles.tsx b/src/components/ResourcesSidebar/styles.tsx index 8e2ceb3..fbf752d 100644 --- a/src/components/ResourcesSidebar/styles.tsx +++ b/src/components/ResourcesSidebar/styles.tsx @@ -79,6 +79,8 @@ export const TreeWrapper = styled.div<{ collapsed?: boolean }>` &.firstLevel { overflow: hidden; + border-top: 1px solid + ${(props) => transparentize(0.8, props.theme.sidebar.foreground)}; } ${Children} { @@ -109,15 +111,12 @@ export const ExpandIcon = styled(Expand)` } ` -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 ExpandResources = styled.div` + padding-right: 15px; + margin: 20px 0; ` -export const ExpandLanguagesHeader = styled.div` +export const ExpandResourcesHeader = styled.div` user-select: none; display: flex; align-items: center; @@ -130,12 +129,18 @@ export const ExpandLanguagesHeader = styled.div` } ` -export const StyledLanguageList = styled.div` - padding: 8px 0; +export const StyledResourceList = styled.div` + padding-top: 16px; display: flex; flex-direction: column; align-items: flex-start; overflow: hidden; + + & + & { + margin-top: 16px; + border-top: 1px solid + ${(props) => transparentize(0.8, props.theme.sidebar.foreground)}; + } ` const item = css` @@ -167,7 +172,7 @@ const item = css` } ` -export const Language = styled.div` +export const Resource = styled.div` ${item}; ` diff --git a/src/components/ResourcesSidebar/useTree.tsx b/src/components/ResourcesSidebar/useTree.tsx new file mode 100644 index 0000000..3ee7820 --- /dev/null +++ b/src/components/ResourcesSidebar/useTree.tsx @@ -0,0 +1,13 @@ +import sort from "ramda/es/sort" +import useBuildTree from "../../hooks/useBuildTree" +import { IAllResourcesQuery } from "../../types" + +export default function useTree(resources: IAllResourcesQuery["allFile"]) { + const resourcesTree = useBuildTree(resources, "/resources") + const sortedResources = sort( + (a, b) => a.title.localeCompare(b.title), + resourcesTree + ) + + return sortedResources +} diff --git a/src/hooks/useBuildTree.tsx b/src/hooks/useBuildTree.tsx index df337af..d42a0a0 100644 --- a/src/hooks/useBuildTree.tsx +++ b/src/hooks/useBuildTree.tsx @@ -2,10 +2,10 @@ import { IAllFilesQuery, IFileQuery } from "../types" import { join, traversePaths } from "../utils" export default function useBuildTree( - resources: IAllFilesQuery, + resources: IAllFilesQuery["allFile"], basePath: string ) { - const objects = resources.allFile.edges.map(({ node: file }: IFileQuery) => + const objects = resources.edges.map(({ node: file }: IFileQuery) => traversePaths(file.relativePath.split("/"), basePath) )