mirror of
https://github.com/Stone-Red-Code/website.git
synced 2026-09-04 09:15:58 +02:00
feat(resources): support topics + languages in sidebar
This commit is contained in:
@@ -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<HTMLAttributes<HTMLDivElement>> = (props) => {
|
||||
const archives = useStaticQuery<IAllArchivesQuery>(ALL_ARCHIVES)
|
||||
const { archives } = useStaticQuery(ALL_ARCHIVES)
|
||||
const tree = useBuildTree(archives, "/archives")
|
||||
const sortedTree = sort((a, b) => a.title.localeCompare(b.title), tree)
|
||||
|
||||
|
||||
@@ -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<React.SetStateAction<boolean>>
|
||||
}> = ({ items, setExpanded }) => {
|
||||
const { current, setCurrent } = useSidebar()
|
||||
|
||||
return (
|
||||
<SC.StyledLanguageList>
|
||||
<SC.StyledResourceList>
|
||||
{items.map((item) => (
|
||||
<SC.Language
|
||||
<SC.Resource
|
||||
key={item.title}
|
||||
className={current === item.title ? "active" : ""}
|
||||
onClick={() => {
|
||||
@@ -147,55 +159,52 @@ const LanguageList: FC<{
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</SC.Language>
|
||||
</SC.Resource>
|
||||
))}
|
||||
</SC.StyledLanguageList>
|
||||
</SC.StyledResourceList>
|
||||
)
|
||||
}
|
||||
|
||||
const AllLanguages: FC<{
|
||||
items: IFileOrFolder[]
|
||||
const ExpandResources: FC<{
|
||||
expanded: boolean
|
||||
setExpanded: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}> = ({ items, expanded, setExpanded }) => {
|
||||
}> = ({ children, expanded, setExpanded }) => {
|
||||
const { current } = useSidebar()
|
||||
|
||||
if (!current) {
|
||||
return <LanguageList items={items} setExpanded={setExpanded} />
|
||||
}
|
||||
const showList = expanded || !current
|
||||
|
||||
return (
|
||||
<SC.ExpandLanguages>
|
||||
<SC.ExpandLanguagesHeader
|
||||
onClick={() => setExpanded((prevState) => !prevState)}
|
||||
>
|
||||
Expand languages {expanded ? <SC.CollapseIcon /> : <SC.ExpandIcon />}
|
||||
</SC.ExpandLanguagesHeader>
|
||||
{expanded && <LanguageList items={items} setExpanded={setExpanded} />}
|
||||
</SC.ExpandLanguages>
|
||||
<SC.ExpandResources>
|
||||
{current && (
|
||||
<SC.ExpandResourcesHeader
|
||||
onClick={() => setExpanded((prevState) => !prevState)}
|
||||
>
|
||||
Expand resources {expanded ? <SC.CollapseIcon /> : <SC.ExpandIcon />}
|
||||
</SC.ExpandResourcesHeader>
|
||||
)}
|
||||
{showList && children}
|
||||
</SC.ExpandResources>
|
||||
)
|
||||
}
|
||||
|
||||
export const ResourcesSidebar: FC<HTMLAttributes<HTMLDivElement>> = (props) => {
|
||||
const [expandedLanguages, setExpandedLanguages] = useState(false)
|
||||
const resources = useStaticQuery<IAllResourcesQuery>(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 (
|
||||
<Sidebar {...props}>
|
||||
<AllLanguages
|
||||
items={sortedLanguages}
|
||||
expanded={expandedLanguages}
|
||||
setExpanded={setExpandedLanguages}
|
||||
/>
|
||||
<ExpandResources expanded={expanded} setExpanded={setExpanded}>
|
||||
<ResourceList items={languagesTree} setExpanded={setExpanded} />
|
||||
<ResourceList items={topicsTree} setExpanded={setExpanded} />
|
||||
</ExpandResources>
|
||||
{currentLanguage && <Tree item={currentLanguage} firstLevel={true} />}
|
||||
{currentTopic && <Tree item={currentTopic} firstLevel={true} />}
|
||||
</Sidebar>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
`
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user