diff --git a/gatsby-config.js b/gatsby-config.js index 0902077..873736a 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -54,8 +54,15 @@ module.exports = { { resolve: `gatsby-source-filesystem`, options: { - name: `resources`, - path: `${__dirname}/src/content/resources`, + name: `languages`, + path: `${__dirname}/src/content/resources/language`, + }, + }, + { + resolve: `gatsby-source-filesystem`, + options: { + name: `topics`, + path: `${__dirname}/src/content/resources/topic`, }, }, { diff --git a/gatsby-node.js b/gatsby-node.js index 8874cbd..69ffe5c 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -48,57 +48,81 @@ const validateResourceArticle = node => { } const createResources = async ({ createPage, graphql }) => { - const languageResources = path.resolve(`src/templates/languagePost.tsx`) - const languageHome = path.resolve(`src/templates/languageHome.tsx`) + const resourcePage = path.resolve(`src/templates/resourcePage.tsx`) + const resourceHome = path.resolve(`src/templates/resourceHome.tsx`) - const result = await graphql(` - query FetchResources { - allFile(filter: { sourceInstanceName: { eq: "resources" } }) { - edges { - node { - relativePath - sourceInstanceName + const languagesQuery = await graphql( + ` + query FetchLanguages { + allFile(filter: { sourceInstanceName: { eq: "languages" } }) { + edges { + node { + relativePath + sourceInstanceName + } } } } - } - `) + ` + ) - if (result.errors) { - return Promise.reject(result.errors) + const topicsQuery = await graphql( + ` + query FetchTopics { + allFile(filter: { sourceInstanceName: { eq: "topics" } }) { + edges { + node { + relativePath + sourceInstanceName + } + } + } + } + ` + ) + + const resources = [ + { key: "topics", query: topicsQuery }, + { key: "languages", query: languagesQuery }, + ] + + for ({ key, query } of resources) { + if (query.errors) { + return Promise.reject(query.errors) + } + + const resources = query.data.allFile.edges.reduce((acc, { node }) => { + const [resourceCategory] = node.relativePath.split("/") + if (!acc.includes(resourceCategory)) { + acc.push(resourceCategory) + } + return acc + }, []) + + // create home page for each resources + resources.forEach((resource) => { + createPage({ + path: path.join("resources", resource), + component: resourceHome, + context: { + resourceType: key, + entry: resource, + layout: LAYOUT_RESOURCES, + }, + }) + }) + + query.data.allFile.edges.forEach(({ node }) => { + createPage({ + path: path.join("resources", node.relativePath), + component: resourcePage, + context: { + file: node.relativePath, + layout: LAYOUT_RESOURCES, + }, + }) + }) } - - const languages = result.data.allFile.edges.reduce((acc, { node }) => { - const [language] = node.relativePath.split("/") - if (!acc.includes(language)) { - acc.push(language) - } - return acc - }, []) - - // create home page for each languages - languages.forEach(language => { - createPage({ - path: path.join("resources", language), - component: languageHome, - context: { - language, - layout: LAYOUT_RESOURCES, - }, - }) - }) - - // create resource pages - return result.data.allFile.edges.forEach(({ node }) => { - createPage({ - path: path.join("resources", node.relativePath), - component: languageResources, - context: { - file: node.relativePath, - layout: LAYOUT_RESOURCES, - }, - }) - }) } const createArchives = async ({ createPage, graphql }) => { 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/content/resources/cpp/intro.md b/src/content/resources/language/cpp/intro.md similarity index 100% rename from src/content/resources/cpp/intro.md rename to src/content/resources/language/cpp/intro.md diff --git a/src/content/resources/cpp/multidimensional-arrays.md b/src/content/resources/language/cpp/multidimensional-arrays.md similarity index 100% rename from src/content/resources/cpp/multidimensional-arrays.md rename to src/content/resources/language/cpp/multidimensional-arrays.md diff --git a/src/content/resources/cpp/pragma-once.md b/src/content/resources/language/cpp/pragma-once.md similarity index 100% rename from src/content/resources/cpp/pragma-once.md rename to src/content/resources/language/cpp/pragma-once.md diff --git a/src/content/resources/java/class.md b/src/content/resources/language/java/class.md similarity index 100% rename from src/content/resources/java/class.md rename to src/content/resources/language/java/class.md diff --git a/src/content/resources/java/generics.md b/src/content/resources/language/java/generics.md similarity index 100% rename from src/content/resources/java/generics.md rename to src/content/resources/language/java/generics.md diff --git a/src/content/resources/java/inheritance.md b/src/content/resources/language/java/inheritance.md similarity index 100% rename from src/content/resources/java/inheritance.md rename to src/content/resources/language/java/inheritance.md diff --git a/src/content/resources/java/intro.md b/src/content/resources/language/java/intro.md similarity index 100% rename from src/content/resources/java/intro.md rename to src/content/resources/language/java/intro.md diff --git a/src/content/resources/java/streams.md b/src/content/resources/language/java/streams.md similarity index 100% rename from src/content/resources/java/streams.md rename to src/content/resources/language/java/streams.md diff --git a/src/content/resources/javascript/es6/arrow-functions.md b/src/content/resources/language/javascript/es6/arrow-functions.md similarity index 100% rename from src/content/resources/javascript/es6/arrow-functions.md rename to src/content/resources/language/javascript/es6/arrow-functions.md diff --git a/src/content/resources/javascript/intro.md b/src/content/resources/language/javascript/intro.md similarity index 100% rename from src/content/resources/javascript/intro.md rename to src/content/resources/language/javascript/intro.md diff --git a/src/content/resources/javascript/iterative-vs-functional.md b/src/content/resources/language/javascript/iterative-vs-functional.md similarity index 100% rename from src/content/resources/javascript/iterative-vs-functional.md rename to src/content/resources/language/javascript/iterative-vs-functional.md diff --git a/src/content/resources/javascript/promises/async-await.md b/src/content/resources/language/javascript/promises/async-await.md similarity index 100% rename from src/content/resources/javascript/promises/async-await.md rename to src/content/resources/language/javascript/promises/async-await.md diff --git a/src/content/resources/javascript/promises/converting-a-callback.md b/src/content/resources/language/javascript/promises/converting-a-callback.md similarity index 100% rename from src/content/resources/javascript/promises/converting-a-callback.md rename to src/content/resources/language/javascript/promises/converting-a-callback.md diff --git a/src/content/resources/javascript/promises/intro.md b/src/content/resources/language/javascript/promises/intro.md similarity index 100% rename from src/content/resources/javascript/promises/intro.md rename to src/content/resources/language/javascript/promises/intro.md diff --git a/src/content/resources/javascript/promises/simplifying-promises.md b/src/content/resources/language/javascript/promises/simplifying-promises.md similarity index 100% rename from src/content/resources/javascript/promises/simplifying-promises.md rename to src/content/resources/language/javascript/promises/simplifying-promises.md diff --git a/src/content/resources/javascript/spread-operator.md b/src/content/resources/language/javascript/spread-operator.md similarity index 100% rename from src/content/resources/javascript/spread-operator.md rename to src/content/resources/language/javascript/spread-operator.md diff --git a/src/content/resources/javascript/variables.md b/src/content/resources/language/javascript/variables.md similarity index 100% rename from src/content/resources/javascript/variables.md rename to src/content/resources/language/javascript/variables.md diff --git a/src/content/resources/kotlin/intro.md b/src/content/resources/language/kotlin/intro.md similarity index 100% rename from src/content/resources/kotlin/intro.md rename to src/content/resources/language/kotlin/intro.md diff --git a/src/content/resources/kotlin/native/creating-c-bindings.md b/src/content/resources/language/kotlin/native/creating-c-bindings.md similarity index 100% rename from src/content/resources/kotlin/native/creating-c-bindings.md rename to src/content/resources/language/kotlin/native/creating-c-bindings.md diff --git a/src/content/resources/php/design-patterns/singleton.md b/src/content/resources/language/php/design-patterns/singleton.md similarity index 100% rename from src/content/resources/php/design-patterns/singleton.md rename to src/content/resources/language/php/design-patterns/singleton.md diff --git a/src/content/resources/php/intro.md b/src/content/resources/language/php/intro.md similarity index 100% rename from src/content/resources/php/intro.md rename to src/content/resources/language/php/intro.md diff --git a/src/content/resources/php/pdo/prepared-statements.md b/src/content/resources/language/php/pdo/prepared-statements.md similarity index 100% rename from src/content/resources/php/pdo/prepared-statements.md rename to src/content/resources/language/php/pdo/prepared-statements.md diff --git a/src/content/resources/python/intro.md b/src/content/resources/language/python/intro.md similarity index 100% rename from src/content/resources/python/intro.md rename to src/content/resources/language/python/intro.md diff --git a/src/content/resources/python/operators.md b/src/content/resources/language/python/operators.md similarity index 100% rename from src/content/resources/python/operators.md rename to src/content/resources/language/python/operators.md diff --git a/src/content/resources/bot-development/bot-deployment-with-heroku.md b/src/content/resources/topic/bot-development/bot-deployment-with-heroku.md similarity index 100% rename from src/content/resources/bot-development/bot-deployment-with-heroku.md rename to src/content/resources/topic/bot-development/bot-deployment-with-heroku.md diff --git a/src/content/resources/bot-development/intro.md b/src/content/resources/topic/bot-development/intro.md similarity index 100% rename from src/content/resources/bot-development/intro.md rename to src/content/resources/topic/bot-development/intro.md diff --git a/src/content/resources/bot-development/kutils-guide.md b/src/content/resources/topic/bot-development/kutils-guide.md similarity index 100% rename from src/content/resources/bot-development/kutils-guide.md rename to src/content/resources/topic/bot-development/kutils-guide.md diff --git a/src/content/resources/cryptography/history.md b/src/content/resources/topic/cryptography/history.md similarity index 100% rename from src/content/resources/cryptography/history.md rename to src/content/resources/topic/cryptography/history.md diff --git a/src/content/resources/cryptography/intro.md b/src/content/resources/topic/cryptography/intro.md similarity index 100% rename from src/content/resources/cryptography/intro.md rename to src/content/resources/topic/cryptography/intro.md diff --git a/src/content/resources/web-development/deploying-a-site-with-netlify.md b/src/content/resources/topic/web-development/deploying-a-site-with-netlify.md similarity index 100% rename from src/content/resources/web-development/deploying-a-site-with-netlify.md rename to src/content/resources/topic/web-development/deploying-a-site-with-netlify.md diff --git a/src/content/resources/web-development/intro.md b/src/content/resources/topic/web-development/intro.md similarity index 100% rename from src/content/resources/web-development/intro.md rename to src/content/resources/topic/web-development/intro.md 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) ) diff --git a/src/templates/languageHome.tsx b/src/templates/resourceHome.tsx similarity index 88% rename from src/templates/languageHome.tsx rename to src/templates/resourceHome.tsx index 3d9f7b4..87dd89b 100644 --- a/src/templates/languageHome.tsx +++ b/src/templates/resourceHome.tsx @@ -7,7 +7,7 @@ import { PageContent } from "../components/PageContent" import useSidebar from "../hooks/useSidebar" // @todo maybe find alternative type for data -const LanguageHome: FC = ({ data, pageContext }) => { +const ResourceHome: FC = ({ data, pageContext }) => { const { current: language } = useSidebar() const { relativePath } = data.file const { html, excerpt, fields, frontmatter, timeToRead } = data.file.post @@ -43,13 +43,13 @@ const LanguageHome: FC = ({ data, pageContext }) => { ) } -export default LanguageHome +export default ResourceHome export const query = graphql` - query LanguageHome($language: String!) { + query ResourceHome($resourceType: String!, $entry: String!) { file( - sourceInstanceName: { eq: "resources" } - relativeDirectory: { eq: $language } + sourceInstanceName: { eq: $resourceType } + relativeDirectory: { eq: $entry } base: { eq: "intro.md" } ) { relativePath diff --git a/src/templates/languagePost.tsx b/src/templates/resourcePage.tsx similarity index 94% rename from src/templates/languagePost.tsx rename to src/templates/resourcePage.tsx index 1213f03..6c90173 100644 --- a/src/templates/languagePost.tsx +++ b/src/templates/resourcePage.tsx @@ -10,7 +10,7 @@ import useSidebar from "../hooks/useSidebar" import { buildToc } from "../utils" // @todo maybe find alternative type for data -const LanguagePost: FC = ({ data }) => { +const ResourcePage: FC = ({ data }) => { const { current: language } = useSidebar() const { relativePath } = data.file const { @@ -61,10 +61,10 @@ const LanguagePost: FC = ({ data }) => { ) } -export default LanguagePost +export default ResourcePage export const query = graphql` - query LanguagePost($file: String!) { + query ResourcePage($file: String!) { file(relativePath: { eq: $file }) { relativePath post: childMarkdownRemark {