feat(archives): bootstrap what-is-archives from Github

This commit is contained in:
Jean-Philippe Sirois
2019-11-11 15:40:52 -08:00
parent 51eeb489e5
commit 7cc1e4c899
33 changed files with 578 additions and 242 deletions
+8
View File
@@ -63,6 +63,14 @@ module.exports = {
path: `${__dirname}/src/content/resources`, path: `${__dirname}/src/content/resources`,
}, },
}, },
{
resolve: `gatsby-source-git`,
options: {
name: `what-is-archive`,
remote: `https://github.com/the-programmers-hangout/technology-spotlight`,
patterns: [`*.md`, `!README.md`],
},
},
`gatsby-transformer-sharp`, `gatsby-transformer-sharp`,
`gatsby-plugin-react-svg`, `gatsby-plugin-react-svg`,
`gatsby-plugin-sharp`, `gatsby-plugin-sharp`,
+55 -5
View File
@@ -7,8 +7,21 @@ const fs = require("fs")
const requiredArticleFrontmatter = ["authors", "date"] const requiredArticleFrontmatter = ["authors", "date"]
const LAYOUT_RESOURCES = "resources" const LAYOUT_RESOURCES = "resources"
const LAYOUT_ARCHIVES = "archives"
const LAYOUT_REGULAR = "regular" const LAYOUT_REGULAR = "regular"
function resolveLayout(path) {
if (path.match(/resources/)) {
return LAYOUT_RESOURCES
}
if (path.match(/archives/)) {
return LAYOUT_ARCHIVES
}
return LAYOUT_REGULAR
}
let users = [] let users = []
try { try {
users = JSON.parse(fs.readFileSync("./users.json", "utf-8")) users = JSON.parse(fs.readFileSync("./users.json", "utf-8"))
@@ -38,6 +51,7 @@ const createResources = async ({ createPage, graphql }) => {
edges { edges {
node { node {
relativePath relativePath
sourceInstanceName
} }
} }
} }
@@ -60,9 +74,43 @@ const createResources = async ({ createPage, graphql }) => {
}) })
} }
const createArchives = async ({ createPage, graphql }) => {
const archive = path.resolve(`src/templates/archive.tsx`)
const result = await graphql(`
query FetchArchives {
allFile(filter: { sourceInstanceName: { eq: "what-is-archive" } }) {
edges {
node {
relativePath
sourceInstanceName
}
}
}
}
`)
if (result.errors) {
return Promise.reject(result.errors)
}
return result.data.allFile.edges.forEach(({ node }) => {
createPage({
path: path.join("archives", node.relativePath),
component: archive,
context: {
file: node.relativePath,
layout: LAYOUT_ARCHIVES,
},
})
})
}
exports.onCreateNode = async ({ node, getNode, actions }) => { exports.onCreateNode = async ({ node, getNode, actions }) => {
const { createPage, createNodeField } = actions const { createPage, createNodeField } = actions
if (node.internal.type === "MarkdownRemark") { // TODO: find a better way to avoid handling non-resources
// or better, attach authors to what-is archives
if (node.internal.type === "MarkdownRemark" && node.frontmatter.authors) {
const { frontmatter } = node const { frontmatter } = node
const isDoc = Boolean(!frontmatter.path) const isDoc = Boolean(!frontmatter.path)
@@ -91,13 +139,15 @@ exports.onCreateNode = async ({ node, getNode, actions }) => {
exports.onCreatePage = ({ page, actions }) => { exports.onCreatePage = ({ page, actions }) => {
const { createPage } = actions const { createPage } = actions
page.context.layout = page.path.match(/resources/) page.context.layout = resolveLayout(page.path)
? LAYOUT_RESOURCES
: LAYOUT_REGULAR
createPage(page) createPage(page)
} }
exports.createPages = ({ actions, graphql }) => { exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions const { createPage } = actions
return createResources({ createPage, graphql })
return Promise.all([
createArchives({ createPage, graphql }),
createResources({ createPage, graphql }),
])
} }
+95 -13
View File
@@ -7770,7 +7770,8 @@
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"resolved": false, "resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@@ -7791,12 +7792,14 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": false, "resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": false, "resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@@ -7811,17 +7814,20 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@@ -7938,7 +7944,8 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"resolved": false, "resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@@ -7950,6 +7957,7 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": false, "resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@@ -7964,6 +7972,7 @@
"version": "3.0.4", "version": "3.0.4",
"resolved": false, "resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@@ -7971,12 +7980,14 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"resolved": false, "resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"resolved": false, "resolved": false,
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@@ -7995,6 +8006,7 @@
"version": "0.5.1", "version": "0.5.1",
"resolved": false, "resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@@ -8075,7 +8087,8 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@@ -8087,6 +8100,7 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": false, "resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@@ -8172,7 +8186,8 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": false, "resolved": false,
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@@ -8208,6 +8223,7 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": false, "resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@@ -8227,6 +8243,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@@ -8270,12 +8287,14 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": false, "resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"resolved": false, "resolved": false,
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"optional": true
} }
} }
}, },
@@ -9522,6 +9541,39 @@
} }
} }
}, },
"gatsby-source-git": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/gatsby-source-git/-/gatsby-source-git-1.0.2.tgz",
"integrity": "sha512-TNCC8PnRK3knrO5Q9qpOWVsYuRuLZ07LGB2kQw4/b4bmbciVFZSNUfuJpokpl3/6DPvcePQtNjARVpdKrRYhog==",
"requires": {
"fast-glob": "^2.2.3",
"fs-extra": "^5.0.0",
"gatsby-source-filesystem": "^2.1.19",
"git-url-parse": "^11.1.1",
"rimraf": "^2.6.2",
"simple-git": "^1.105.0"
},
"dependencies": {
"fs-extra": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"requires": {
"glob": "^7.1.3"
}
}
}
},
"gatsby-telemetry": { "gatsby-telemetry": {
"version": "1.1.33", "version": "1.1.33",
"resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.1.33.tgz", "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.1.33.tgz",
@@ -9821,6 +9873,14 @@
"parse-url": "^5.0.0" "parse-url": "^5.0.0"
} }
}, },
"git-url-parse": {
"version": "11.1.2",
"resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.2.tgz",
"integrity": "sha512-gZeLVGY8QVKMIkckncX+iCq2/L8PlwncvDFKiWkBn9EtCfYDbliRTTp6qzyQ1VMdITUfq7293zDzfpjdiGASSQ==",
"requires": {
"git-up": "^4.0.0"
}
},
"github-from-package": { "github-from-package": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
@@ -11061,12 +11121,14 @@
"ansi-regex": { "ansi-regex": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"optional": true
}, },
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"optional": true
}, },
"slice-ansi": { "slice-ansi": {
"version": "1.0.0", "version": "1.0.0",
@@ -11092,6 +11154,7 @@
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"optional": true,
"requires": { "requires": {
"ansi-regex": "^4.1.0" "ansi-regex": "^4.1.0"
} }
@@ -16383,6 +16446,7 @@
"version": "0.15.0", "version": "0.15.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz",
"integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==", "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==",
"optional": true,
"requires": { "requires": {
"loose-envify": "^1.1.0", "loose-envify": "^1.1.0",
"object-assign": "^4.1.1" "object-assign": "^4.1.1"
@@ -16757,6 +16821,24 @@
} }
} }
}, },
"simple-git": {
"version": "1.126.0",
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.126.0.tgz",
"integrity": "sha512-47mqHxgZnN8XRa9HbpWprzUv3Ooqz9RY/LSZgvA7jCkW8jcwLahMz7LKugY91KZehfG0sCVPtgXiU72hd6b1Bw==",
"requires": {
"debug": "^4.0.1"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
}
}
},
"simple-swizzle": { "simple-swizzle": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+1
View File
@@ -44,6 +44,7 @@
"gatsby-remark-auto-headers": "^1.0.3", "gatsby-remark-auto-headers": "^1.0.3",
"gatsby-remark-katex": "^3.1.15", "gatsby-remark-katex": "^3.1.15",
"gatsby-source-filesystem": "^2.1.33", "gatsby-source-filesystem": "^2.1.33",
"gatsby-source-git": "^1.0.2",
"gatsby-transformer-remark": "^2.6.30", "gatsby-transformer-remark": "^2.6.30",
"gatsby-transformer-sharp": "^2.3.0", "gatsby-transformer-sharp": "^2.3.0",
"katex": "^0.11.1", "katex": "^0.11.1",
+35
View File
@@ -0,0 +1,35 @@
import { graphql, useStaticQuery } from "gatsby"
import React, { FC, HTMLAttributes } from "react"
import { IAllArchivesQuery, IFileOrFolder } from "../../types"
import { humanize } from "../../utils"
import { ColumnSidebar } from "../ColumnSidebar"
import useBuildTree from "./../../hooks/useBuildTree"
import * as SC from "./styles"
const ALL_ARCHIVES = graphql`
query {
allFile(filter: { sourceInstanceName: { eq: "what-is-archive" } }) {
edges {
node {
relativePath
}
}
}
}
`
function plantTree(item: IFileOrFolder) {
return (
<SC.PageLink key={item.title} to={item.path} activeClassName="active">
{humanize(item.title)}
</SC.PageLink>
)
}
export const ArchivesSidebar: FC<HTMLAttributes<HTMLDivElement>> = props => {
const archives = useStaticQuery<IAllArchivesQuery>(ALL_ARCHIVES)
const tree = useBuildTree(archives, "/archives")
const sortedTree = tree.sort((a, b) => a.title.localeCompare(b.title))
return <ColumnSidebar {...props}>{sortedTree.map(plantTree)}</ColumnSidebar>
}
+31
View File
@@ -0,0 +1,31 @@
import { Link } from "gatsby"
import { transparentize } from "polished"
import styled from "styled-components"
export const PageLink = styled(Link)`
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;
&:hover {
border-color: ${props =>
transparentize(0.7, props.theme.sidebar.foreground)};
}
& + & {
margin-top: 4px;
}
&.active {
font-weight: 700;
color: ${props => props.theme.sidebar.active};
}
&.active:hover {
border-color: ${props => transparentize(0.7, props.theme.sidebar.active)};
}
`
@@ -9,8 +9,9 @@ interface ILinkProps {
item: IFileOrFolder item: IFileOrFolder
} }
interface IResourceBreadcrumbProps { interface IBreadcrumbProps {
relativePath: any relativePath: any
basePath: string
} }
const Link: FC<ILinkProps> = ({ item }) => { const Link: FC<ILinkProps> = ({ item }) => {
@@ -28,12 +29,12 @@ function flatten([
return flatten([...currNode.children, ...previousNodes, currNode]) return flatten([...currNode.children, ...previousNodes, currNode])
} }
export function ResourceBreadcrumb({ relativePath }: IResourceBreadcrumbProps) { export function Breadcrumb({ relativePath, basePath }: IBreadcrumbProps) {
const paths = relativePath.split("/") const paths = relativePath.split("/")
const breadcrumbItems = flatten([traversePaths(paths)]) const breadcrumbItems = flatten([traversePaths(paths, basePath)])
return ( return (
<SC.ResourceBreadcrumbWrapper> <SC.BreadcrumbWrapper>
<SC.LinkWrapper> <SC.LinkWrapper>
<Link <Link
item={{ path: "/", title: "home", type: "folder", children: [] }} item={{ path: "/", title: "home", type: "folder", children: [] }}
@@ -43,8 +44,8 @@ export function ResourceBreadcrumb({ relativePath }: IResourceBreadcrumbProps) {
<SC.LinkWrapper> <SC.LinkWrapper>
<Link <Link
item={{ item={{
path: "/resources", path: basePath,
title: "resources", title: basePath.replace(/\//, ""),
type: "folder", type: "folder",
children: [], children: [],
}} }}
@@ -67,6 +68,6 @@ export function ResourceBreadcrumb({ relativePath }: IResourceBreadcrumbProps) {
</SC.CurrentPage> </SC.CurrentPage>
) )
})} })}
</SC.ResourceBreadcrumbWrapper> </SC.BreadcrumbWrapper>
) )
} }
@@ -2,7 +2,7 @@ import { Link } from "gatsby"
import { darken, lighten } from "polished" import { darken, lighten } from "polished"
import styled from "styled-components" import styled from "styled-components"
export const ResourceBreadcrumbWrapper = styled.div` export const BreadcrumbWrapper = styled.div`
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
@@ -1,22 +1,23 @@
import React, { FC, Fragment } from "react" import React, { FC, Fragment } from "react"
import ChevronUp from "../../icons/chevron-up.svg" import ChevronUp from "../../icons/chevron-up.svg"
import { ResourceBreadcrumb } from "../ResourceBreadcrumb" import { Breadcrumb } from "../Breadcrumb"
import { StackedAvatars } from "../StackedAvatars" import { StackedAvatars } from "../StackedAvatars"
import * as SC from "./styles" import * as SC from "./styles"
interface IResourceHeaderProps { interface IColumnHeaderProps {
relativePath: string relativePath: string
basePath: string
title: string title: string
authors: Array<{ authors?: Array<{
avatar: string avatar: string
hash: string hash: string
name: string name: string
}> }>
createdAt: string createdAt: string
timeToRead: number timeToRead: number
recommendedReading: string[] recommendedReading?: string[]
externalResources: string[] externalResources?: string[]
} }
function ExtraLink({ function ExtraLink({
@@ -47,7 +48,8 @@ function ExtraLink({
) )
} }
export const ResourceHeader: FC<IResourceHeaderProps> = ({ export const ColumnHeader: FC<IColumnHeaderProps> = ({
basePath,
relativePath, relativePath,
title, title,
authors, authors,
@@ -63,24 +65,26 @@ export const ResourceHeader: FC<IResourceHeaderProps> = ({
const dateToHuman = `${month} ${day}, ${year}` const dateToHuman = `${month} ${day}, ${year}`
return ( return (
<SC.ResourceHeaderWrapper> <SC.ColumnHeaderWrapper>
<ResourceBreadcrumb relativePath={relativePath} /> <Breadcrumb relativePath={relativePath} basePath={basePath} />
<SC.Title>{title}</SC.Title> <SC.Title>{title}</SC.Title>
<SC.Top> <SC.Top>
<SC.Meta> {authors && (
<StackedAvatars authors={authors} /> <SC.Meta>
<SC.PopoverToggler> <StackedAvatars authors={authors} />
{authors.length} contributor{authors.length > 1 && "s"} <SC.PopoverToggler>
<SC.Popover> {authors.length} contributor{authors.length > 1 && "s"}
{authors <SC.Popover>
.map(author => `${author.name}#${author.hash}`) {authors
.join(", ")} .map(author => `${author.name}#${author.hash}`)
</SC.Popover> .join(", ")}
</SC.PopoverToggler> </SC.Popover>
</SC.Meta> </SC.PopoverToggler>
<SC.Meta>{dateToHuman}</SC.Meta> </SC.Meta>
)}
{dateToHuman && <SC.Meta>{dateToHuman}</SC.Meta>}
<SC.Meta> <SC.Meta>
{timeToRead} minute{timeToRead !== 1 && "s"} read time {timeToRead} minute{timeToRead !== 1 && "s"} read time
</SC.Meta> </SC.Meta>
@@ -103,6 +107,6 @@ export const ResourceHeader: FC<IResourceHeaderProps> = ({
})} })}
</SC.ExternalResources> </SC.ExternalResources>
)} )}
</SC.ResourceHeaderWrapper> </SC.ColumnHeaderWrapper>
) )
} }
@@ -3,7 +3,7 @@ import { darken, lighten } from "polished"
import styled, { css } from "styled-components" import styled, { css } from "styled-components"
import { fontFamily, modularScale } from "../../design/typography" import { fontFamily, modularScale } from "../../design/typography"
export const ResourceHeaderWrapper = styled.div` export const ColumnHeaderWrapper = styled.div`
border-bottom: 1px solid #dbdbdb; border-bottom: 1px solid #dbdbdb;
padding-bottom: 32px; padding-bottom: 32px;
margin-bottom: 32px; margin-bottom: 32px;
+18
View File
@@ -0,0 +1,18 @@
import React, { FC } from "react"
import * as SC from "./styles"
interface IColumnLinkProps {
to: string
}
export const ColumnLink: FC<IColumnLinkProps> = ({ children, to }) => {
if (!to.match(/^(https?:\/\/)/)) {
return <SC.ColumnLinkInternal to={to}>{children}</SC.ColumnLinkInternal>
}
return (
<SC.ColumnLinkExternal href={to} target="_blank">
{children}
</SC.ColumnLinkExternal>
)
}
@@ -15,10 +15,10 @@ const linkStyle = css`
} }
` `
export const ResourcesLinkInternal = styled(Link)` export const ColumnLinkInternal = styled(Link)`
${linkStyle} ${linkStyle}
` `
export const ResourcesLinkExternal = styled.a` export const ColumnLinkExternal = styled.a`
${linkStyle} ${linkStyle}
` `
+49
View File
@@ -0,0 +1,49 @@
import { Link } from "gatsby"
import React, { FC, HTMLAttributes, PropsWithChildren } from "react"
import Scrollbar from "react-perfect-scrollbar"
import "react-perfect-scrollbar/dist/css/styles.css"
import Banner from "../../images/tph-banner.svg"
import { ThemeToggler } from "../ThemeToggler"
import * as SC from "./styles"
interface IMenuItemProps {
to: string
}
const MenuItem: FC<IMenuItemProps> = ({ children, to }) => {
return (
<SC.MenuItem to={to} activeClassName="active">
{children}
</SC.MenuItem>
)
}
export const ColumnSidebar: FC<
PropsWithChildren<HTMLAttributes<HTMLDivElement>>
> = props => {
const { children, ...restProps } = props
return (
<SC.ColumnSidebarWrapper {...restProps}>
<Scrollbar>
<Link to="/">
<Banner />
</Link>
<SC.Inner>
{children}
<SC.Menu>
<MenuItem to="/about">about</MenuItem>
<MenuItem to="/rules">rules</MenuItem>
<MenuItem to="/faq">faq</MenuItem>
<MenuItem to="/">hotbot</MenuItem>
<MenuItem to="/resources">resources</MenuItem>
<MenuItem to="/archives">tech spotlights</MenuItem>
</SC.Menu>
<ThemeToggler />
</SC.Inner>
</Scrollbar>
</SC.ColumnSidebarWrapper>
)
}
+56
View File
@@ -0,0 +1,56 @@
import { Link } from "gatsby"
import styled from "styled-components"
export const ColumnSidebarWrapper = styled.div`
box-sizing: border-box;
flex: 0 0 320px;
background: ${props => props.theme.sidebar.background};
color: ${props => props.theme.sidebar.foreground};
position: fixed;
top: 0;
bottom: 0;
width: 320px;
@media screen and (max-width: 767px) {
z-index: 100;
width: auto;
/* TODO: maybe find a less "awkward" way to animate this */
left: calc(-100% - 100px);
right: calc(100% - 0px);
transition: left 0.3s, right 0.3s;
}
&.is-open {
left: 0;
right: 100px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
`
export const Inner = styled.div`
padding: 30px 0 30px 20px;
`
export const Menu = styled.nav`
display: flex;
flex-direction: column;
border-top: 1px dashed #a5a5a5;
margin: 20px 0 40px;
padding-top: 20px;
`
export const MenuItem = styled(Link)`
padding: 4px 0;
font-size: 20px;
color: ${props => props.theme.sidebar.foreground};
text-decoration: none;
&:hover,
&:active {
text-decoration: underline;
}
&.active {
font-weight: 700;
}
`
+1 -1
View File
@@ -86,7 +86,7 @@ export const Header: FC<IHeaderProps> = ({ isHome }) => {
<MenuItem to="/">faq</MenuItem> <MenuItem to="/">faq</MenuItem>
<MenuItem to="/">hotbot</MenuItem> <MenuItem to="/">hotbot</MenuItem>
<MenuItem to="/resources">resources</MenuItem> <MenuItem to="/resources">resources</MenuItem>
<MenuItem to="/">tech spotlight</MenuItem> <MenuItem to="/archives">tech spotlights</MenuItem>
</SC.Menu> </SC.Menu>
<DiscordButton>join us</DiscordButton> <DiscordButton>join us</DiscordButton>
{isHome && <Partner />} {isHome && <Partner />}
-20
View File
@@ -1,20 +0,0 @@
import React, { FC } from "react"
import * as SC from "./styles"
interface IResourcesLinkProps {
to: string
}
export const ResourcesLink: FC<IResourcesLinkProps> = ({ children, to }) => {
if (!to.match(/^(https?:\/\/)/)) {
return (
<SC.ResourcesLinkInternal to={to}>{children}</SC.ResourcesLinkInternal>
)
}
return (
<SC.ResourcesLinkExternal href={to} target="_blank">
{children}
</SC.ResourcesLinkExternal>
)
}
@@ -3,7 +3,7 @@ import { join, traversePathsToFiles } from "../../utils"
export default function useBuildTree(resources: IAllResourcesQuery) { export default function useBuildTree(resources: IAllResourcesQuery) {
const objects = resources.allFile.edges.map(({ node: file }: IFileQuery) => const objects = resources.allFile.edges.map(({ node: file }: IFileQuery) =>
traversePathsToFiles(file.relativePath.split("/")) traversePathsToFiles(file.relativePath.split("/"), "/resources")
) )
return join(objects) return join(objects)
+7 -40
View File
@@ -1,15 +1,12 @@
import { graphql, Link, useStaticQuery } from "gatsby" import { graphql, useStaticQuery } from "gatsby"
import React, { FC, HTMLAttributes, memo, useState } from "react" import React, { FC, HTMLAttributes, memo, useState } from "react"
import Scrollbar from "react-perfect-scrollbar"
import "react-perfect-scrollbar/dist/css/styles.css"
import TriangleDown from "../../icons/triangle-down.svg" import TriangleDown from "../../icons/triangle-down.svg"
import Banner from "../../images/tph-banner.svg"
import { IAllResourcesQuery, IFileOrFolder, IFolder } from "../../types" import { IAllResourcesQuery, IFileOrFolder, IFolder } from "../../types"
import { humanize } from "../../utils" import { humanize } from "../../utils"
import { ThemeToggler } from "../ThemeToggler" import { ColumnSidebar } from "../ColumnSidebar"
import useBuildTree from "./../../hooks/useBuildTree"
import useSidebar from "./../../hooks/useSidebar" import useSidebar from "./../../hooks/useSidebar"
import * as SC from "./styles" import * as SC from "./styles"
import useBuildTree from "./useBuildTree"
import useMatchingPath from "./useMatchingPath" import useMatchingPath from "./useMatchingPath"
const ALL_RESOURCES = graphql` const ALL_RESOURCES = graphql`
@@ -93,44 +90,14 @@ const FirstLevelFolder = memo(
} }
) )
interface IMenuItemProps {
to: string
}
const MenuItem: FC<IMenuItemProps> = ({ children, to }) => {
return (
<SC.MenuItem to={to} activeClassName="active">
{children}
</SC.MenuItem>
)
}
export const ResourcesSidebar: FC<HTMLAttributes<HTMLDivElement>> = props => { export const ResourcesSidebar: FC<HTMLAttributes<HTMLDivElement>> = props => {
const resources = useStaticQuery<IAllResourcesQuery>(ALL_RESOURCES) const resources = useStaticQuery<IAllResourcesQuery>(ALL_RESOURCES)
const tree = useBuildTree(resources) const tree = useBuildTree(resources, "/resources")
const sortedTree = tree.sort((a, b) => a.title.localeCompare(b.title)) const sortedTree = tree.sort((a, b) => a.title.localeCompare(b.title))
return ( return (
<SC.ResourcesSidebarWrapper {...props}> <ColumnSidebar {...props}>
<Scrollbar> {sortedTree.map((node, index) => plantTree(node, index, true))}
<Link to="/"> </ColumnSidebar>
<Banner />
</Link>
<SC.Inner>
{sortedTree.map((node, index) => plantTree(node, index, true))}
<SC.Menu>
<MenuItem to="/about">about</MenuItem>
<MenuItem to="/rules">rules</MenuItem>
<MenuItem to="/faq">faq</MenuItem>
<MenuItem to="/">hotbot</MenuItem>
<MenuItem to="/resources">resources</MenuItem>
<MenuItem to="/">tech</MenuItem>
</SC.Menu>
<ThemeToggler />
</SC.Inner>
</Scrollbar>
</SC.ResourcesSidebarWrapper>
) )
} }
@@ -3,32 +3,6 @@ import { transparentize } from "polished"
import styled from "styled-components" import styled from "styled-components"
import ChevronUp from "../../icons/chevron-up.svg" import ChevronUp from "../../icons/chevron-up.svg"
export const ResourcesSidebarWrapper = styled.div`
box-sizing: border-box;
flex: 0 0 320px;
background: ${props => props.theme.sidebar.background};
color: ${props => props.theme.sidebar.foreground};
position: fixed;
top: 0;
bottom: 0;
width: 320px;
@media screen and (max-width: 767px) {
z-index: 100;
width: auto;
/* TODO: maybe find a less "awkward" way to animate this */
left: calc(-100% - 100px);
right: calc(100% - 0px);
transition: left 0.3s, right 0.3s;
}
&.is-open {
left: 0;
right: 100px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
`
export const Children = styled.div` export const Children = styled.div`
padding-left: 16px; padding-left: 16px;
padding-bottom: 8px; padding-bottom: 8px;
@@ -131,10 +105,6 @@ export const TreeWrapper = styled.div<{ collapsed: boolean }>`
export const CollapseToggler = styled(ChevronUp)`` export const CollapseToggler = styled(ChevronUp)``
export const Inner = styled.div`
padding: 30px 0 30px 20px;
`
export const PageLink = styled(Link)` export const PageLink = styled(Link)`
display: block; display: block;
padding: 4px 0 0; padding: 4px 0 0;
@@ -162,27 +132,3 @@ export const PageLink = styled(Link)`
border-color: ${props => transparentize(0.7, props.theme.sidebar.active)}; border-color: ${props => transparentize(0.7, props.theme.sidebar.active)};
} }
` `
export const Menu = styled.nav`
display: flex;
flex-direction: column;
border-top: 1px dashed #a5a5a5;
margin: 20px 0 40px;
padding-top: 20px;
`
export const MenuItem = styled(Link)`
padding: 4px 0;
font-size: 20px;
color: ${props => props.theme.sidebar.foreground};
text-decoration: none;
&:hover,
&:active {
text-decoration: underline;
}
&.active {
font-weight: 700;
}
`
@@ -1,10 +0,0 @@
import { IAllResourcesQuery, IFileQuery } from "../../types"
import { join, traversePaths } from "../../utils"
export default function useBuildTree(resources: IAllResourcesQuery) {
const objects = resources.allFile.edges.map(({ node: file }: IFileQuery) =>
traversePaths(file.relativePath.split("/"))
)
return join(objects)
}
+13
View File
@@ -0,0 +1,13 @@
import { IAllFilesQuery, IFileQuery } from "../types"
import { join, traversePaths } from "../utils"
export default function useBuildTree(
resources: IAllFilesQuery,
basePath: string
) {
const objects = resources.allFile.edges.map(({ node: file }: IFileQuery) =>
traversePaths(file.relativePath.split("/"), basePath)
)
return join(objects)
}
+8
View File
@@ -0,0 +1,8 @@
import React, { FC } from "react"
import { ArchivesSidebar } from "../../components/ArchivesSidebar"
import { ColumnLayout } from "../ColumnLayout"
export const ArchivesLayout: FC = ({ children }) => {
return <ColumnLayout sidebar={ArchivesSidebar} content={children} />
}
+48
View File
@@ -0,0 +1,48 @@
import React, { FC, useMemo, useState } from "react"
import { ColumnSidebar } from "../../components/ColumnSidebar"
import { MobileHeader } from "../../components/MobileHeader"
import { GlobalStyles } from "../../globalStyles"
import { useLockBodyScroll } from "../../hooks/useLockBodyScroll"
import { SidebarProvider } from "../../SidebarProvider"
import { ThemeProvider } from "../../ThemeProvider"
import * as SC from "./styles"
interface IColumnLayoutProps {
sidebar: React.ReactNode
content: React.ReactNode
}
export const ColumnLayout: FC<IColumnLayoutProps> = ({ sidebar, content }) => {
const [activeMobileMenu, setActiveMobileMenu] = useState(false)
const { lock, unlock } = useLockBodyScroll()
function openMenu() {
setActiveMobileMenu(true)
lock()
}
function closeMenu() {
setActiveMobileMenu(false)
unlock()
}
return (
<ThemeProvider>
<SidebarProvider>
<GlobalStyles />
<SC.Main>
<MobileHeader openMenu={openMenu} />
{sidebar({ className: activeMobileMenu ? "is-open" : "" })}
<SC.MainContent>
<SC.Container>{content}</SC.Container>
</SC.MainContent>
</SC.Main>
<SC.Overlay
className={activeMobileMenu ? "is-open" : ""}
onClick={closeMenu}
/>
</SidebarProvider>
</ThemeProvider>
)
}
-7
View File
@@ -1,10 +1,3 @@
/**
* Layout component that queries for data
* with Gatsby's useStaticQuery component
*
* See: https://www.gatsbyjs.org/docs/use-static-query/
*/
import { RouteComponentProps } from "@reach/router" import { RouteComponentProps } from "@reach/router"
import React, { FC, Fragment, useEffect } from "react" import React, { FC, Fragment, useEffect } from "react"
+3 -45
View File
@@ -1,50 +1,8 @@
/** import React, { FC } from "react"
* Layout component that queries for data
* with Gatsby's useStaticQuery component
*
* See: https://www.gatsbyjs.org/docs/use-static-query/
*/
import React, { FC, useState } from "react"
import { MobileHeader } from "../../components/MobileHeader"
import { ResourcesSidebar } from "../../components/ResourcesSidebar" import { ResourcesSidebar } from "../../components/ResourcesSidebar"
import { GlobalStyles } from "../../globalStyles" import { ColumnLayout } from "../ColumnLayout"
import { useLockBodyScroll } from "../../hooks/useLockBodyScroll"
import { SidebarProvider } from "../../SidebarProvider"
import { ThemeProvider } from "../../ThemeProvider"
import * as SC from "./styles"
export const ResourcesLayout: FC = ({ children }) => { export const ResourcesLayout: FC = ({ children }) => {
const [activeMobileMenu, setActiveMobileMenu] = useState(false) return <ColumnLayout sidebar={ResourcesSidebar} content={children} />
const { lock, unlock } = useLockBodyScroll()
function openMenu() {
setActiveMobileMenu(true)
lock()
}
function closeMenu() {
setActiveMobileMenu(false)
unlock()
}
return (
<ThemeProvider>
<SidebarProvider>
<GlobalStyles />
<SC.Main>
<MobileHeader openMenu={openMenu} />
<ResourcesSidebar className={activeMobileMenu ? "is-open" : ""} />
<SC.MainContent>
<SC.Container>{children}</SC.Container>
</SC.MainContent>
</SC.Main>
<SC.Overlay
className={activeMobileMenu ? "is-open" : ""}
onClick={closeMenu}
/>
</SidebarProvider>
</ThemeProvider>
)
} }
+14 -6
View File
@@ -1,6 +1,7 @@
import { WindowLocation } from "@reach/router" import { WindowLocation } from "@reach/router"
import React, { PropsWithChildren } from "react" import React, { PropsWithChildren, useMemo } from "react"
import { LocationProvider } from "../LocationProvider" import { LocationProvider } from "../LocationProvider"
import { ArchivesLayout } from "./ArchivesLayout"
import { Layout } from "./Layout" import { Layout } from "./Layout"
import { ResourcesLayout } from "./ResourcesLayout" import { ResourcesLayout } from "./ResourcesLayout"
@@ -14,13 +15,20 @@ export default function BaseLayout({
} }
location: WindowLocation location: WindowLocation
}>) { }>) {
const ResolvedLayout = useMemo(() => {
switch (pageContext.layout) {
case "resources":
return ResourcesLayout
case "archives":
return ArchivesLayout
default:
return Layout
}
}, [pageContext.layout])
return ( return (
<LocationProvider location={location}> <LocationProvider location={location}>
{pageContext.layout === "resources" ? ( <ResolvedLayout>{children}</ResolvedLayout>
<ResourcesLayout>{children}</ResourcesLayout>
) : (
<Layout>{children}</Layout>
)}
</LocationProvider> </LocationProvider>
) )
} }
+21
View File
@@ -0,0 +1,21 @@
import React, { Fragment } from "react"
import { ColumnLink } from "../components/ColumnLink"
import { SEO } from "../components/SEO"
function ResourcesPage() {
return (
<Fragment>
<SEO title="Tech Spotlights Archives" />
<p>Welcome to the TPH tech spotlights archives.</p>
<p>
This regroups the archives of our occasional, temporary channels that
cover a piece of technology that might be unknown to part of our users.
You can find those on{" "}
<ColumnLink to="/about">The Programmer's Hangout</ColumnLink>.
</p>
</Fragment>
)
}
export default ResourcesPage
+3 -3
View File
@@ -1,7 +1,7 @@
import React, { Fragment } from "react" import React, { Fragment } from "react"
import packageJson from "../../package.json" import packageJson from "../../package.json"
import { ResourcesLink } from "../components/ResourcesLink" import { ColumnLink } from "../components/ColumnLink"
import { ResourcesList } from "../components/ResourcesList" import { ResourcesList } from "../components/ResourcesList"
import { SEO } from "../components/SEO" import { SEO } from "../components/SEO"
@@ -13,11 +13,11 @@ function ResourcesPage() {
<p> <p>
This is meant as a small knowledge base for commonly answered questions This is meant as a small knowledge base for commonly answered questions
on our Discord community,{" "} on our Discord community,{" "}
<ResourcesLink to="/about">The Programmer's Hangout</ResourcesLink>. <ColumnLink to="/about">The Programmer's Hangout</ColumnLink>.
</p> </p>
<p> <p>
All of it is open source, and you can contribute to it on{" "} All of it is open source, and you can contribute to it on{" "}
<ResourcesLink to={packageJson.repository.url}>GitHub</ResourcesLink>. <ColumnLink to={packageJson.repository.url}>GitHub</ColumnLink>.
</p> </p>
<ResourcesList /> <ResourcesList />
</Fragment> </Fragment>
+44
View File
@@ -0,0 +1,44 @@
import { graphql } from "gatsby"
import React, { FC, Fragment } from "react"
import { ColumnHeader } from "../components/ColumnHeader"
import { Markdown } from "../components/Markdown"
import { SEO } from "../components/SEO"
import { humanize } from "../utils"
// @todo maybe find alternative type for data
const Archive: FC<any> = ({ data }) => {
const { relativePath, ctime } = data.file
const { html, excerpt, timeToRead } = data.file.post
const title = humanize(relativePath)
return (
<Fragment>
<SEO title={title} description={excerpt} />
<ColumnHeader
relativePath={relativePath}
basePath="/archives"
title={title}
createdAt={ctime}
timeToRead={timeToRead}
/>
<Markdown content={html} />
</Fragment>
)
}
export default Archive
export const query = graphql`
query Archive($file: String!) {
file(relativePath: { eq: $file }) {
relativePath
ctime
post: childMarkdownRemark {
html
excerpt
timeToRead
}
}
}
`
+3 -2
View File
@@ -1,7 +1,7 @@
import { graphql } from "gatsby" import { graphql } from "gatsby"
import React, { FC, Fragment } from "react" import React, { FC, Fragment } from "react"
import { ColumnHeader } from "../components/ColumnHeader"
import { Markdown } from "../components/Markdown" import { Markdown } from "../components/Markdown"
import { ResourceHeader } from "../components/ResourceHeader"
import { SEO } from "../components/SEO" import { SEO } from "../components/SEO"
import "katex/dist/katex.min.css" import "katex/dist/katex.min.css"
@@ -14,8 +14,9 @@ const LanguagePost: FC<any> = ({ data }) => {
return ( return (
<Fragment> <Fragment>
<SEO title={frontmatter.title} description={excerpt} /> <SEO title={frontmatter.title} description={excerpt} />
<ResourceHeader <ColumnHeader
relativePath={relativePath} relativePath={relativePath}
basePath="/resources"
title={frontmatter.title} title={frontmatter.title}
authors={fields.authors} authors={fields.authors}
createdAt={frontmatter.created_at} createdAt={frontmatter.created_at}
+25 -1
View File
@@ -14,6 +14,12 @@ export interface IFolder {
export type IFileOrFolder = IFile | IFolder export type IFileOrFolder = IFile | IFolder
export interface IFileQuery { export interface IFileQuery {
node: {
relativePath: string
}
}
export interface IFileResourceQuery {
node: { node: {
relativePath: string relativePath: string
childMarkdownRemark: { childMarkdownRemark: {
@@ -25,8 +31,26 @@ export interface IFileQuery {
} }
} }
export interface IAllResourcesQuery { export interface IFileArchiveQuery {
node: {
relativePath: string
}
}
export interface IAllFilesQuery {
allFile: { allFile: {
edges: IFileQuery[] edges: IFileQuery[]
} }
} }
export interface IAllResourcesQuery extends IAllFilesQuery {
allFile: {
edges: IFileResourceQuery[]
}
}
export interface IAllArchivesQuery extends IAllFilesQuery {
allFile: {
edges: IFileArchiveQuery[]
}
}
+2 -2
View File
@@ -32,7 +32,7 @@ export function humanize(str: string): string {
export function traversePaths( export function traversePaths(
[head, ...tail]: string[], [head, ...tail]: string[],
basePath: string = "/resources" basePath: string
): IFileOrFolder { ): IFileOrFolder {
const path = basePath + "/" + head const path = basePath + "/" + head
const isFile = !tail.length const isFile = !tail.length
@@ -55,7 +55,7 @@ export function traversePaths(
export function traversePathsToFiles( export function traversePathsToFiles(
[head, ...tail]: string[], [head, ...tail]: string[],
basePath: string = "/resources", basePath: string,
depth: number = 0 depth: number = 0
): IFileOrFolder { ): IFileOrFolder {
const path = basePath + "/" + head + "/" + tail.join("/") const path = basePath + "/" + head + "/" + tail.join("/")