mirror of
https://github.com/Stone-Red-Code/website.git
synced 2026-09-04 17:25:59 +02:00
Merge pull request #270 from the-programmers-hangout/ui-tweak-language-home
ui: tweak language home
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React from "react"
|
||||
import cx from "classnames"
|
||||
|
||||
import * as SC from "./styles"
|
||||
import { Container } from "../Container"
|
||||
@@ -23,7 +24,13 @@ export const HeaderBarebone: React.FC<IHeaderBareboneProps> = (props) => {
|
||||
<SC.Box>
|
||||
{props.above}
|
||||
|
||||
<SC.Title>{props.title}</SC.Title>
|
||||
<SC.Title
|
||||
className={cx({
|
||||
"has-content-below": props.content,
|
||||
})}
|
||||
>
|
||||
{props.title}
|
||||
</SC.Title>
|
||||
|
||||
{props.content}
|
||||
</SC.Box>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { transparentize, darken } from "polished"
|
||||
import styled from "styled-components"
|
||||
import { fontFamily, modularScale } from "../../design/typography"
|
||||
import { fontFamily } from "../../design/typography"
|
||||
import { WavesTop, WavesBottom } from "../Waves"
|
||||
import Circles from "../../images/circles.svg"
|
||||
|
||||
@@ -104,13 +104,13 @@ export const Box = styled.div`
|
||||
|
||||
export const Title = styled.h1`
|
||||
font-family: ${fontFamily.header};
|
||||
font-size: ${modularScale(6).fontSize}px;
|
||||
line-height: ${modularScale(6).lineHeight}px;
|
||||
font-size: 34px;
|
||||
line-height: 41px;
|
||||
letter-spacing: -1.75px;
|
||||
margin-top: 0;
|
||||
margin: 0;
|
||||
|
||||
& + * {
|
||||
margin-top: 16px;
|
||||
&.has-content-below {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
`
|
||||
|
||||
@@ -5,13 +5,17 @@ interface ILinkProps {
|
||||
to: string
|
||||
}
|
||||
|
||||
export const Link: FC<ILinkProps> = ({ children, to }) => {
|
||||
export const Link: FC<ILinkProps> = ({ children, to, ...props }) => {
|
||||
if (!to.match(/^(https?:\/\/)/)) {
|
||||
return <SC.LinkInternal to={to}>{children}</SC.LinkInternal>
|
||||
return (
|
||||
<SC.LinkInternal {...props} to={to}>
|
||||
{children}
|
||||
</SC.LinkInternal>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<SC.LinkExternal rel="noreferrer" href={to} target="_blank">
|
||||
<SC.LinkExternal {...props} rel="noreferrer" href={to} target="_blank">
|
||||
{children}
|
||||
</SC.LinkExternal>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import React, { FC, HTMLAttributes } from "react"
|
||||
import { Footer } from "../Footer"
|
||||
import { Markdown } from "../Markdown"
|
||||
import { ResourcesList } from "../ResourcesList"
|
||||
import * as SC from "./styles"
|
||||
|
||||
interface IResourcesHomeContent extends HTMLAttributes<HTMLDivElement> {
|
||||
language: string
|
||||
html: any
|
||||
}
|
||||
|
||||
export const ResourcesHomeContent: FC<IResourcesHomeContent> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<SC.Title>Resources</SC.Title>
|
||||
<SC.Intro>
|
||||
Written by and for TPH members, short introduction topics to commonly
|
||||
answered questions.
|
||||
</SC.Intro>
|
||||
<ResourcesList relativeDirectory={props.language} />
|
||||
<SC.Box>
|
||||
<SC.Title>Extra resources</SC.Title>
|
||||
<Markdown content={props.html} />
|
||||
</SC.Box>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import styled from "styled-components"
|
||||
import fontFamily, { BASE_LINE_HEIGHT } from "../../design/typography"
|
||||
import externalLink from "../../icons/external-link.png"
|
||||
|
||||
export const ResourcesHomeContentWrapper = styled.div``
|
||||
|
||||
export const Title = styled.h2`
|
||||
font-size: 24px;
|
||||
font-family: ${fontFamily.header};
|
||||
`
|
||||
|
||||
export const Intro = styled.p`
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
font-family: ${fontFamily.header};
|
||||
`
|
||||
|
||||
// most of this should probably be elsewhere and not attempt to overwrite markdown styles
|
||||
export const Box = styled.div`
|
||||
background: ${(props) => props.theme.sidebar.background};
|
||||
margin: 32px -16px;
|
||||
padding: 16px;
|
||||
|
||||
${Title} {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h6 {
|
||||
margin-top: ${BASE_LINE_HEIGHT}px;
|
||||
}
|
||||
|
||||
h6 ~ h6::before {
|
||||
display: block;
|
||||
margin-bottom: ${BASE_LINE_HEIGHT}px;
|
||||
content: "";
|
||||
height: 1px;
|
||||
background: ${(props) =>
|
||||
props.theme.name === "dark" ? "#34414e" : "#a3beda"};
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul li + li {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
a:not(.anchor) {
|
||||
position: relative;
|
||||
color: #fff !important;
|
||||
font-weight: 400 !important;
|
||||
border-bottom: 1px solid #fff;
|
||||
margin-bottom: 1px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
-webkit-background-clip: none;
|
||||
-webkit-text-fill-color: #fff;
|
||||
border-bottom-width: 2px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
margin-top: -6px;
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
margin-left: 10px;
|
||||
display: block;
|
||||
content: "";
|
||||
/* I would prefer a svg, but could not get it to work */
|
||||
background-image: url("${externalLink}");
|
||||
background-size: 16px 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
`
|
||||
@@ -1,6 +1,7 @@
|
||||
import cx from "classnames"
|
||||
import { graphql, useStaticQuery } from "gatsby"
|
||||
import sort from "ramda/es/sort"
|
||||
import React, { FC, HTMLAttributes, memo } from "react"
|
||||
import React, { FC, HTMLAttributes, memo, useMemo } from "react"
|
||||
import "react-perfect-scrollbar/dist/css/styles.css"
|
||||
import { IAllResourcesQuery, IFileOrFolder, IFolder } from "../../types"
|
||||
import { getPath, humanize } from "../../utils"
|
||||
@@ -26,7 +27,7 @@ const ALL_RESOURCES = graphql`
|
||||
}
|
||||
`
|
||||
|
||||
function plantTree(item: IFileOrFolder, index?: number) {
|
||||
function plantTree(item: IFileOrFolder, single?: boolean) {
|
||||
if (item.type === "file") {
|
||||
// remove the first elements, treat as hardcoded
|
||||
const [, , , ...cleanedUpPath] = item.path.split("/")
|
||||
@@ -34,25 +35,41 @@ function plantTree(item: IFileOrFolder, index?: number) {
|
||||
|
||||
return (
|
||||
<SC.PageLink key={item.title} to={path}>
|
||||
{cleanedUpPath.map((node) => (
|
||||
// TODO: use helper to format path
|
||||
<SC.NodePart key={node}>{humanize(node)}</SC.NodePart>
|
||||
))}
|
||||
{cleanedUpPath.map((node) => humanize(node)).join(" / ")}
|
||||
</SC.PageLink>
|
||||
)
|
||||
}
|
||||
|
||||
return <Language key={item.title} item={item} index={index!} />
|
||||
return <Language key={item.title} item={item} single={Boolean(single)} />
|
||||
}
|
||||
|
||||
const Language = memo(({ item }: { item: IFolder; index: number }) => {
|
||||
return (
|
||||
<SC.TreeWrapper>
|
||||
<SC.LanguageLabel>{humanize(item.title)}</SC.LanguageLabel>
|
||||
<SC.Children>{item.children.map((node) => plantTree(node))}</SC.Children>
|
||||
</SC.TreeWrapper>
|
||||
)
|
||||
})
|
||||
const Language = memo(
|
||||
({ item, single }: { item: IFolder; single: boolean }) => {
|
||||
const children = useMemo(() => {
|
||||
if (single) {
|
||||
return item.children.filter((child) => {
|
||||
const [, , , title] = child.title.split("/")
|
||||
return title !== "intro"
|
||||
})
|
||||
}
|
||||
|
||||
return item.children
|
||||
}, [item.children, single])
|
||||
|
||||
return (
|
||||
<SC.TreeWrapper>
|
||||
{!single && <SC.LanguageLabel>{humanize(item.title)}</SC.LanguageLabel>}
|
||||
<SC.Children className={cx({ "is-single": single })}>
|
||||
{children.map((node) => plantTree(node))}
|
||||
</SC.Children>
|
||||
</SC.TreeWrapper>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
function sortTree(tree: IFileOrFolder[]) {
|
||||
return sort((a, b) => a.title.localeCompare(b.title), tree)
|
||||
}
|
||||
|
||||
interface IResourcesList extends HTMLAttributes<HTMLDivElement> {
|
||||
relativeDirectory?: string
|
||||
@@ -77,11 +94,12 @@ export const ResourcesList: FC<IResourcesList> = (props) => {
|
||||
}
|
||||
|
||||
const tree = useBuildTree(filteredResources)
|
||||
const sortedTree = sort((a, b) => a.title.localeCompare(b.title), tree)
|
||||
const sortedTree = sortTree(tree)
|
||||
const isSingle = Boolean(props.relativeDirectory)
|
||||
|
||||
return (
|
||||
<SC.ResourcesListWrapper {...props}>
|
||||
{sortedTree.map((node, index) => plantTree(node, index))}
|
||||
{sortedTree.map((node) => plantTree(node, isSingle))}
|
||||
</SC.ResourcesListWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Link } from "gatsby"
|
||||
import { transparentize } from "polished"
|
||||
import styled from "styled-components"
|
||||
import { Link } from "../Link"
|
||||
|
||||
export const ResourcesListWrapper = styled.div`
|
||||
box-sizing: border-box;
|
||||
@@ -14,29 +14,9 @@ export const Children = styled.div`
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
overflow: hidden;
|
||||
`
|
||||
|
||||
export const Label = styled.div`
|
||||
user-select: none;
|
||||
position: relative;
|
||||
padding: 4px 0 2px;
|
||||
margin-bottom: 2px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid transparent;
|
||||
align-self: flex-start;
|
||||
|
||||
&:hover {
|
||||
border-color: ${(props) =>
|
||||
transparentize(0.7, props.theme.sidebar.foreground)};
|
||||
}
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
right: 100%;
|
||||
margin-right: 4px;
|
||||
width: 10px;
|
||||
&.is-single {
|
||||
padding-left: 0;
|
||||
}
|
||||
`
|
||||
|
||||
@@ -48,16 +28,6 @@ export const LanguageLabel = styled.div`
|
||||
padding: 4px 15px 4px 0;
|
||||
font-weight: 700;
|
||||
color: ${(props) => transparentize(0.5, 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)};
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const TreeWrapper = styled.div`
|
||||
@@ -65,39 +35,22 @@ export const TreeWrapper = styled.div`
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
margin: 32px 0;
|
||||
|
||||
& + & {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
${Children} {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
& > ${Label} svg {
|
||||
fill: ${(props) => props.theme.sidebar.foreground};
|
||||
}
|
||||
`
|
||||
|
||||
export const PageLink = styled(Link)`
|
||||
display: block;
|
||||
padding: 2px 0;
|
||||
color: ${(props) => props.theme.sidebar.foreground};
|
||||
align-self: flex-start;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: ${(props) =>
|
||||
transparentize(0.7, props.theme.sidebar.foreground)};
|
||||
}
|
||||
font-weight: 400;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
|
||||
& + & {
|
||||
margin-top: 4px;
|
||||
}
|
||||
`
|
||||
|
||||
export const NodePart = styled.span`
|
||||
& + &::before {
|
||||
content: "/";
|
||||
opacity: 0.3;
|
||||
margin: 0 4px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
`
|
||||
|
||||
@@ -5,6 +5,6 @@ created_at: 2020/04/20
|
||||
title: Bot Development
|
||||
---
|
||||
|
||||
## About
|
||||
###### About
|
||||
|
||||
In TPH, we employ the use of various Discord bots to help with a myriad of tasks such as moderation and managing embeds.
|
||||
|
||||
@@ -5,28 +5,28 @@ created_at: 2019/12/15
|
||||
title: C++
|
||||
---
|
||||
|
||||
## Beginner
|
||||
###### Beginner
|
||||
|
||||
- [Get started](https://isocpp.org/get-started)
|
||||
- [Learn C++](http://www.learncpp.com/)
|
||||
- [C++ tutorial](http://www.cplusplus.com/doc/tutorial/)
|
||||
|
||||
## Documentation
|
||||
###### Documentation
|
||||
|
||||
- [C++ reference](http://en.cppreference.com/w/)
|
||||
- [https://isocpp.org/std/the-standard](https://isocpp.org/std/the-standard)
|
||||
- [http://eel.is/c++draft/](http://eel.is/c++draft/)
|
||||
|
||||
## Books
|
||||
###### Books
|
||||
|
||||
- [The definitive C books](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282)
|
||||
|
||||
## Video
|
||||
###### Video
|
||||
|
||||
- [TPH Picks](https://goo.gl/emLpwP)
|
||||
- [Cherno](https://www.youtube.com/playlist?list=PLlrATfBNZ98dudnM48yfGUldqGD0S4FFb)
|
||||
|
||||
## Other
|
||||
###### Other
|
||||
|
||||
- [Super FAQ](https://isocpp.org/wiki/faq)
|
||||
- [Compilers](http://www.stroustrup.com/compilers.html)
|
||||
|
||||
@@ -5,17 +5,17 @@ created_at: 2020/04/23
|
||||
title: Introduction to Cryptography
|
||||
---
|
||||
|
||||
## Popular Science Books
|
||||
###### Popular Science Books
|
||||
|
||||
- [The Codebreakers](https://www.amazon.com/Codebreakers-Comprehensive-History-Communication-Internet/dp/0684831309)
|
||||
- [Seizing the Enigma: The Race to Break the German U-Boats Codes](https://www.amazon.com/Seizing-Enigma-German-U-Boats-1939-1943/dp/0395427398)
|
||||
|
||||
## Books
|
||||
###### Books
|
||||
|
||||
- [Handbook of Applied Cryptography - Free](http://cacr.uwaterloo.ca/hac/)
|
||||
- [Serious Cryptography](https://nostarch.com/seriouscrypto)
|
||||
- [Cryptograpy Engineering](https://www.schneier.com/books/cryptography_engineering/)
|
||||
|
||||
## Video
|
||||
###### Video
|
||||
|
||||
- [Coursera](https://www.coursera.org/learn/crypto)
|
||||
|
||||
@@ -5,30 +5,30 @@ created_at: 2019/12/15
|
||||
title: Java
|
||||
---
|
||||
|
||||
## Documentation
|
||||
###### Documentation
|
||||
|
||||
- [Java docs](https://docs.oracle.com/en/java/javase/11/)
|
||||
|
||||
## Free Books
|
||||
###### Free Books
|
||||
|
||||
- [https://goo.gl/WZTC9C](https://goo.gl/WZTC9C)
|
||||
|
||||
## Video
|
||||
###### Video
|
||||
|
||||
- [EJ Media](https://goo.gl/tC1KwC)
|
||||
- [Derek Banas](https://goo.gl/9eVFkE)
|
||||
- [Free With Registration | Cave of Programming](https://goo.gl/NXTo4H)
|
||||
|
||||
## Guides
|
||||
###### Guides
|
||||
|
||||
- [Learn By Examples | Beginnersbook.com](https://goo.gl/3nDWeQ)
|
||||
|
||||
## Useful Repositories
|
||||
###### Useful Repositories
|
||||
|
||||
- [Frameworks](https://goo.gl/EoLvrH)
|
||||
- [Design patterns](https://goo.gl/wT7SfQ)
|
||||
|
||||
## Other
|
||||
###### Other
|
||||
|
||||
- [History of Java](https://en.wikipedia.org/wiki/Java_version_history)
|
||||
- [Wikibooks](https://en.wikibooks.org/wiki/Java_Programming/History)
|
||||
|
||||
@@ -5,7 +5,7 @@ created_at: 2019/12/15
|
||||
title: Javascript
|
||||
---
|
||||
|
||||
## Documentation
|
||||
###### Documentation
|
||||
|
||||
- [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
||||
- [jQuery](https://contribute.jquery.org/documentation/)
|
||||
@@ -13,7 +13,7 @@ title: Javascript
|
||||
- [Typescript](https://www.typescriptlang.org/docs/home.html)
|
||||
- [Discord.js](https://discord.js.org/#/docs/main/stable/general/welcome)
|
||||
|
||||
## Tutorials
|
||||
###### Tutorials
|
||||
|
||||
- [Eloquent](https://eloquentjavascript.net/)
|
||||
- [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS)
|
||||
@@ -21,12 +21,12 @@ title: Javascript
|
||||
- [Modern JS](https://javascript.info/)
|
||||
- [Evie's Accelerated JS Tutorial](https://evie.gitbook.io/js/)
|
||||
|
||||
## Discord.js
|
||||
###### Discord.js
|
||||
|
||||
- [An Idiot's Guide](https://anidiots.guide/)
|
||||
- [Discord.js Guide](https://discordjs.guide/)
|
||||
|
||||
## Other
|
||||
###### Other
|
||||
|
||||
- [You might not need jQuery](https://youmightnotneedjquery.com/)
|
||||
- [You might not need jQuery 2](https://github.com/you-dont-need-x/you-dont-need-jquery)
|
||||
|
||||
@@ -5,17 +5,17 @@ created_at: 2019/12/15
|
||||
title: Kotlin
|
||||
---
|
||||
|
||||
## About
|
||||
###### About
|
||||
|
||||
One line language description: Kotlin is a general-purpose programming language that can run on a JVM or be transpiled to Javascript code. It can use functional, procedural, and OO paradigms, and can interleave them. Primarily developed by JetBrains.
|
||||
|
||||
## Links
|
||||
###### Links
|
||||
|
||||
- [Official Website](https://www.kotlinlang.org/)
|
||||
- [Official Docs](https://kotlinlang.org/docs/reference/)
|
||||
- [Tutorials](https://kotlinlang.org/docs/tutorials/)
|
||||
- [Mooc (not free)](https://www.udemy.com/kotlin-course/)
|
||||
|
||||
## Recommended reading
|
||||
###### Recommended reading
|
||||
|
||||
- Kotlin in Action by Dmitry Jemerov and Svetlana Isakova
|
||||
|
||||
@@ -5,7 +5,7 @@ created_at: 2019/12/15
|
||||
title: PHP
|
||||
---
|
||||
|
||||
## Useful links and documentation
|
||||
###### Useful links and documentation
|
||||
|
||||
- [Official documentation](https://secure.php.net/manual/en/)
|
||||
- [Laracasts PHP tutorials](http://laracasts.com/)
|
||||
@@ -13,7 +13,7 @@ title: PHP
|
||||
- [PHP Standard Recommendations](https://www.php-fig.org/psr/)
|
||||
- [OWASP Cheatsheet](https://cheatsheetseries.owasp.org/)
|
||||
|
||||
## Useful Libraries
|
||||
###### Useful Libraries
|
||||
|
||||
- [Composer package manager](https://getcomposer.org/)
|
||||
- [phpDocumentor](https://www.phpdoc.org/)
|
||||
|
||||
@@ -5,29 +5,29 @@ created_at: 2019/12/15
|
||||
title: Python
|
||||
---
|
||||
|
||||
## Beginner
|
||||
###### Beginner
|
||||
|
||||
- [Official tutorial](https://docs.python.org/3/tutorial/)
|
||||
- [Automate the boring stuff](https://automatetheboringstuff.com/)
|
||||
- [Beginner's guide](https://wiki.python.org/moin/BeginnersGuide/NonProgrammers)
|
||||
|
||||
## Documentation
|
||||
###### Documentation
|
||||
|
||||
- [https://docs.python.org/3/](https://docs.python.org/3/)
|
||||
- [https://www.python.org/dev/](https://www.python.org/dev/)
|
||||
|
||||
## Video
|
||||
###### Video
|
||||
|
||||
- [Socratica](https://goo.gl/8xKVKE)
|
||||
- [thenewboston](https://goo.gl/9EqF2J)
|
||||
|
||||
## Books
|
||||
###### Books
|
||||
|
||||
- [Free](https://goo.gl/Lxhp7i)
|
||||
- [Python Crash Course - paid](https://goo.gl/XQ7Nx6)
|
||||
- [Advanced - paid](https://wiki.python.org/moin/AdvancedBooks)
|
||||
|
||||
## Other
|
||||
###### Other
|
||||
|
||||
- [TalkPython | Podcast](https://goo.gl/xwieUA)
|
||||
- [Exercises](http://www.practicepython.org/)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -1,11 +1,9 @@
|
||||
import { graphql } from "gatsby"
|
||||
import React, { FC, Fragment } from "react"
|
||||
import { Header } from "../components/Header"
|
||||
import { Markdown } from "../components/Markdown"
|
||||
import { ResourcesList } from "../components/ResourcesList"
|
||||
import { ResourcesHomeContent } from "../components/ResourcesHomeContent"
|
||||
import { SEO } from "../components/SEO"
|
||||
import { PageContent } from "../components/PageContent"
|
||||
import { Footer } from "../components/Footer"
|
||||
import useSidebar from "../hooks/useSidebar"
|
||||
|
||||
// @todo maybe find alternative type for data
|
||||
@@ -36,11 +34,7 @@ const LanguageHome: FC<any> = ({ data, pageContext }) => {
|
||||
/>
|
||||
<PageContent
|
||||
content={
|
||||
<>
|
||||
<Markdown content={html} />
|
||||
<ResourcesList relativeDirectory={pageContext.language} />
|
||||
<Footer />
|
||||
</>
|
||||
<ResourcesHomeContent language={pageContext.language} html={html} />
|
||||
}
|
||||
recommendedReading={frontmatter.recommended_reading}
|
||||
externalResources={frontmatter.external_resources}
|
||||
|
||||
Reference in New Issue
Block a user