Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.

Update docs site design after review (#595)

* Update typography colours

All headings are now #444444 (heading)
All sidebar/legend items are now #444444 (passive)

* Fix jumpy legend position

* Fix stripes and sidebar layout

* Reduce font-size on mobile to 15px effectively

* Add sidebar collapsing sections and active styling

* Update sidebar size and padding

* Fix links in mdx using react-router-dom Link

* Fix Loading page design

* Add basic implementation for <ScrollToTop />

* Fix internal links on index pages

This will need further fixes in the plugin

* Fix internal links on /docs/

* Update react-static-plugin-md-pages to fix lost state

* Add usePrefetch hook to home screen to increase load consistency

* Upgrade react-static-plugin-md-pages to fix page update

* Add missing suspense rethrow to custom catch code

* update <Loading /> so that it isn't affected by throws/suspense

* remove duplicate <Article /> parts

* WIP: enable the 404 page on the built app

* Upgrade react-static-plugin-md-pages to remove page data requirement

* Fix path resolution for internal links properly

* Fix homepage links in staging and add link to logo

* Fix local relative internal links on same level

* fix markdown links from other pages

* Fix several hash anchor links

Co-authored-by: Will Golledge <will.golledge@formidable.com>
Co-authored-by: wgolledge <wiggiumg@gmail.com>

+1 -1
docs/advanced/README.md
···
In this chapter we'll dive into various topics of "advanced" `urql` usage. This is admittedly a
catch-all chapter of various use-cases that can only be covered after [the "Concepts"
-
chapter.](../coconcepts/README.md)
- **Subscriptions** covers how to use `useSubscription` and how to set up GraphQL subscriptions with
`urql`.
···
In this chapter we'll dive into various topics of "advanced" `urql` usage. This is admittedly a
catch-all chapter of various use-cases that can only be covered after [the "Concepts"
+
chapter.](../concepts/README.md)
- **Subscriptions** covers how to use `useSubscription` and how to set up GraphQL subscriptions with
`urql`.
+1 -1
docs/advanced/subscriptions.md
···
## React & Preact
The `useSubscription` hooks comes with a similar API to `useQuery`, which [we've learned about in
-
the "Queries" page in the "Basics" section.](../basics/querying-data.md)
Its usage is extremely similar in that it accepts options, which may contain `query` and
`variables`. However, it also accepts a second argument, which is a reducer function, similar to
···
## React & Preact
The `useSubscription` hooks comes with a similar API to `useQuery`, which [we've learned about in
+
the "Queries" page in the "Basics" section.](../basics/queries.md)
Its usage is extremely similar in that it accepts options, which may contain `query` and
`variables`. However, it also accepts a second argument, which is a reducer function, similar to
+1 -1
docs/graphcache/computed-queries.md
···
would be the full `Todo` object.
- `arguments` – The arguments used in this field.
- `cache` – This is the normalized cache. The cache provides us with `resolve`, `readQuery` and `readFragment` methods,
-
read more about this [below](#cache.resolve).
- `info` – This contains the fragments used in the query and the field arguments in the query.
## Cache parameter
···
would be the full `Todo` object.
- `arguments` – The arguments used in this field.
- `cache` – This is the normalized cache. The cache provides us with `resolve`, `readQuery` and `readFragment` methods,
+
read more about this [below](#resolve).
- `info` – This contains the fragments used in the query and the field arguments in the query.
## Cache parameter
+15
packages/react-urql/core/yarn.lock
···
···
+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+
# yarn lockfile v1
+
+
+
"@urql/core@^1.7.0":
+
version "1.9.2"
+
resolved "https://registry.yarnpkg.com/@urql/core/-/core-1.9.2.tgz#bc2880a279dd5ada26527063c7a173fda26abb58"
+
integrity sha512-0eitjW/HlUAkMTwiWFQyQkcmwAlnWlscH3/AKU1Y5MJUzzehAkOffHFQ9eD5st/0TlKSvehbz/eUAHDq/CnX8w==
+
dependencies:
+
wonka "^4.0.7"
+
+
wonka@^4.0.7:
+
version "4.0.7"
+
resolved "https://registry.yarnpkg.com/wonka/-/wonka-4.0.7.tgz#b4934685bd2449367bd72ce7770bfe3e6cc8a68b"
+
integrity sha512-Uhyl2cgWCUksYtU0Jt8MSzKUqK4BVUrewWxnn1YlKL3Zco4sDcCUDkbgH0i762HJs1rtsq03cfzsCWxJKaDgVg==
+1 -1
packages/site/package.json
···
"react-router-ga": "^1.0.0",
"react-scroll": "^1.7.15",
"react-static": "^7.2.3",
-
"react-static-plugin-md-pages": "^0.1.3",
"styled-components": "^5.0.1"
},
"devDependencies": {
···
"react-router-ga": "^1.0.0",
"react-scroll": "^1.7.15",
"react-static": "^7.2.3",
+
"react-static-plugin-md-pages": "^0.1.7",
"styled-components": "^5.0.1"
},
"devDependencies": {
-1
packages/site/src/app.js
···
import * as theme from './styles/theme';
import Analytics from './google-analytics';
import { Loading } from './components/loading';
-
// TODO: import NotFound from './screens/404';
const App = () => {
return (
···
import * as theme from './styles/theme';
import Analytics from './google-analytics';
import { Loading } from './components/loading';
const App = () => {
return (
+13
packages/site/src/assets/chevron.js
···
···
+
import React from 'react';
+
+
const SvgChevron = props => (
+
<svg viewBox="0 0 12 10" {...props}>
+
<path
+
d="M1.41 1L6 5.33 10.59 1 12 2.34 6 8 0 2.34z"
+
fill="currentColor"
+
fillRule="nonzero"
+
/>
+
</svg>
+
);
+
+
export default SvgChevron;
+8
packages/site/src/assets/chevron.svg
···
···
+
<?xml version="1.0" encoding="UTF-8"?>
+
<svg viewBox="0 0 12 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+
<g id="chevron-down" transform="translate(0.000000, 1.000000)" fill="currentColor" fill-rule="nonzero">
+
<polygon id="Path" points="1.41 0 6 4.33018868 10.59 0 12 1.33962264 6 7 0 1.33962264"></polygon>
+
</g>
+
</g>
+
</svg>
+22 -24
packages/site/src/components/loading.js
···
import React from 'react';
import styled, { keyframes } from 'styled-components';
-
import { Container as DocsContainer } from '../screens/docs';
-
import Header from '../screens/docs/header';
const Container = styled.div`
height: 100vh;
width: 100%;
`;
const Loader = styled.div`
position: relative;
margin: 0 auto;
width: ${p => p.theme.spacing.xl};
top: calc(50% - ${p => p.theme.spacing.xl});
&:before {
content: '';
display: block;
···
stroke-linecap: round;
`;
-
export const Loading = () => {
-
return (
-
<DocsContainer>
-
<Container>
-
<Header />
-
<Loader>
-
<Svg className="circular" viewBox="25 25 50 50">
-
<Circle
-
className="path"
-
cx="50"
-
cy="50"
-
r="20"
-
fill="none"
-
strokeWidth="2"
-
strokeMiterlimit="10"
-
/>
-
</Svg>
-
</Loader>
-
</Container>
-
</DocsContainer>
-
);
-
};
···
import React from 'react';
import styled, { keyframes } from 'styled-components';
+
import Docs from '../screens/docs';
const Container = styled.div`
height: 100vh;
width: 100%;
`;
+
const Loader = styled.div`
position: relative;
margin: 0 auto;
width: ${p => p.theme.spacing.xl};
top: calc(50% - ${p => p.theme.spacing.xl});
+
&:before {
content: '';
display: block;
···
stroke-linecap: round;
`;
+
export const Loading = () => (
+
<Docs isLoading>
+
<Container>
+
<Loader>
+
<Svg className="circular" viewBox="25 25 50 50">
+
<Circle
+
className="path"
+
cx="50"
+
cy="50"
+
r="20"
+
fill="none"
+
strokeWidth="2"
+
strokeMiterlimit="10"
+
/>
+
</Svg>
+
</Loader>
+
</Container>
+
</Docs>
+
);
+22 -1
packages/site/src/components/mdx.js
···
import React from 'react';
import styled, { css } from 'styled-components';
import { MDXProvider } from '@mdx-js/react';
-
import Highlight, { Prism } from 'prism-react-renderer';
import nightOwlLight from 'prism-react-renderer/themes/nightOwlLight';
const getLanguage = className => {
const res = className.match(/language-(\w+)/);
···
</TableOverflow>
);
const components = {
pre: Pre,
img: Image,
···
table: TableWithOverflow,
th: TableHeader,
td: TableCell,
};
export const MDXComponents = ({ children }) => (
···
import React from 'react';
+
import * as path from 'path';
import styled, { css } from 'styled-components';
import { MDXProvider } from '@mdx-js/react';
+
import { useLocation, Link } from 'react-router-dom';
+
import { useMarkdownPage } from 'react-static-plugin-md-pages';
import Highlight, { Prism } from 'prism-react-renderer';
import nightOwlLight from 'prism-react-renderer/themes/nightOwlLight';
+
+
import { relative } from './sidebar';
const getLanguage = className => {
const res = className.match(/language-(\w+)/);
···
</TableOverflow>
);
+
const MdLink = ({ href, children }) => {
+
const location = useLocation();
+
const currentPage = useMarkdownPage();
+
+
if (!/^\w+:/.test(href) && !href.startsWith('#')) {
+
const hasTrailingSlash = location.pathname.endsWith('/');
+
const from = !hasTrailingSlash ? currentPage.path + '/' : currentPage.path;
+
const to = hasTrailingSlash
+
? path.join(path.dirname(currentPage.originalPath), href)
+
: path.join(currentPage.path, href);
+
return <Link to={relative(from, to)}>{children}</Link>;
+
}
+
+
return <a href={href}>{children}</a>;
+
};
+
const components = {
pre: Pre,
img: Image,
···
table: TableWithOverflow,
th: TableHeader,
td: TableCell,
+
a: MdLink,
};
export const MDXComponents = ({ children }) => (
+47 -10
packages/site/src/components/navigation.js
···
import styled from 'styled-components';
-
import { Link } from 'react-router-dom';
export const SidebarContainer = styled.div`
display: ${p => (p.hidden ? 'none' : 'block')};
···
display: block;
position: relative;
width: ${p => p.theme.layout.sidebar};
}
`;
···
line-height: ${p => p.theme.lineHeights.body};
font-size: ${p => p.theme.fontSizes.small};
-
padding: ${p => p.theme.spacing.md};
-
padding-right: ${p => p.theme.spacing.sm};
-
background-color: ${p => p.theme.colors.bg};
border-right: 1px solid ${p => p.theme.colors.border};
border-top: 1px solid ${p => p.theme.colors.border};
···
@media ${({ theme }) => theme.media.sm} {
border: none;
background: none;
-
padding-top: ${p => p.theme.spacing.lg};
width: ${p => p.theme.layout.sidebar};
}
`;
-
export const SidebarNavItem = styled(Link)`
display: block;
margin: ${p => p.theme.spacing.xs} 0;
-
color: ${p => p.theme.colors.accent};
font-weight: ${p => p.theme.fontWeights.heading};
text-decoration: none;
width: 100%;
`;
export const SidebarNavSubItemWrapper = styled.div`
···
margin-bottom: ${p => p.theme.spacing.xs};
`;
-
export const SidebarNavSubItem = styled(Link)`
display: block;
-
color: ${p => p.theme.colors.heading};
font-weight: ${p => p.theme.fontWeights.body};
text-decoration: none;
margin-top: ${p => p.theme.spacing.xs};
-
opacity: 0.7;
&:first-child {
margin-top: 0;
}
`;
···
import styled from 'styled-components';
+
import { NavLink } from 'react-router-dom';
+
+
import ChevronIcon from '../assets/chevron';
export const SidebarContainer = styled.div`
display: ${p => (p.hidden ? 'none' : 'block')};
···
display: block;
position: relative;
width: ${p => p.theme.layout.sidebar};
+
margin-left: calc(2 * ${p => p.theme.layout.stripes});
}
`;
···
line-height: ${p => p.theme.lineHeights.body};
font-size: ${p => p.theme.fontSizes.small};
+
padding: ${p => p.theme.spacing.sm} ${p => p.theme.spacing.md};
background-color: ${p => p.theme.colors.bg};
border-right: 1px solid ${p => p.theme.colors.border};
border-top: 1px solid ${p => p.theme.colors.border};
···
@media ${({ theme }) => theme.media.sm} {
border: none;
background: none;
+
padding-top: ${p => p.theme.spacing.md};
width: ${p => p.theme.layout.sidebar};
}
`;
+
export const SidebarNavItem = styled(NavLink).attrs(() => ({
+
activeClassName: 'active',
+
}))`
display: block;
margin: ${p => p.theme.spacing.xs} 0;
+
color: ${p => p.theme.colors.text};
font-weight: ${p => p.theme.fontWeights.heading};
text-decoration: none;
width: 100%;
+
+
&:hover {
+
color: ${p => p.theme.colors.accent};
+
}
+
+
&.active {
+
color: ${p => p.theme.colors.accent};
+
}
+
`;
+
+
export const ChevronItem = styled(ChevronIcon)`
+
display: inline-block;
+
color: inherit;
+
vertical-align: baseline;
+
margin-top: 0.08em;
+
margin-left: 0.3em;
+
padding: 0.08em;
+
width: 1em;
+
height: 1em;
+
+
position: relative;
+
top: 0.16em;
+
+
${SidebarNavItem}.active & {
+
transform: rotate(180deg);
+
}
`;
export const SidebarNavSubItemWrapper = styled.div`
···
margin-bottom: ${p => p.theme.spacing.xs};
`;
+
export const SidebarNavSubItem = styled(NavLink).attrs(() => ({}))`
display: block;
+
color: ${p => p.theme.colors.passive};
font-weight: ${p => p.theme.fontWeights.body};
text-decoration: none;
margin-top: ${p => p.theme.spacing.xs};
&:first-child {
margin-top: 0;
+
}
+
+
&:hover {
+
color: ${p => p.theme.colors.accent};
+
}
+
+
&.active {
+
color: ${p => p.theme.colors.accent};
+
font-weight: ${p => p.theme.fontWeights.heading};
}
`;
+23
packages/site/src/components/scroll-to-top.js
···
···
+
import React, { useEffect, useRef } from 'react';
+
import { useLocation } from 'react-router-dom';
+
import { useMarkdownPage } from 'react-static-plugin-md-pages';
+
+
export const ScrollToTop = () => {
+
const inputRef = useRef(null);
+
const location = useLocation();
+
const md = useMarkdownPage();
+
+
const hash =
+
location.hash ||
+
(location.pathname && location.pathname.match(/#[a-z|-]+/));
+
+
useEffect(() => {
+
if (hash && md) {
+
inputRef.current.click();
+
} else {
+
window.scrollTo(0, 0);
+
}
+
}, [hash, md]);
+
+
return <a href={hash} ref={inputRef} />;
+
};
+61 -27
packages/site/src/components/sidebar.js
···
import React, { Fragment, useMemo } from 'react';
import styled from 'styled-components';
-
import { useLocation } from 'react-router-dom';
import * as path from 'path';
import { useMarkdownTree, useMarkdownPage } from 'react-static-plugin-md-pages';
···
SidebarContainer,
SidebarWrapper,
SideBarStripes,
} from './navigation';
import logoSidebar from '../assets/sidebar-badge.svg';
const HeroLogo = styled.img.attrs(() => ({
src: logoSidebar,
alt: 'urql',
}))`
display: none;
width: ${p => p.theme.layout.logo};
-
margin-bottom: ${p => p.theme.spacing.sm};
-
align-self: center;
@media ${p => p.theme.media.sm} {
display: block;
···
flex-direction: column;
padding-top: ${p => p.theme.spacing.xs};
padding-bottom: ${p => p.theme.spacing.lg};
-
padding-left: ${p => p.theme.spacing.sm};
`;
-
const relative = (from, to) => {
if (!from || !to) return null;
-
let pathname = path.relative(path.dirname(from), to);
if (!pathname)
-
pathname = path.join(path.relative(from, to), path.basename(to));
-
if (from.endsWith('/')) pathname = '../' + pathname;
return { pathname };
};
-
const Sidebar = ({ sidebarOpen }) => {
const location = useLocation();
const currentPage = useMarkdownPage();
const tree = useMarkdownTree();
-
const pathname = location.pathname.endsWith('/')
-
? currentPage.path + '/'
-
: currentPage.path;
-
const sidebarItems = useMemo(() => {
-
if (!currentPage || !tree || !tree.children) {
return null;
}
let children = tree.children;
if (tree.frontmatter && tree.originalPath) {
children = [{ ...tree, children: undefined }, ...children];
}
return children.map(page => {
return (
<Fragment key={page.key}>
-
<SidebarNavItem to={relative(pathname, page.path)}>
{page.frontmatter.title}
</SidebarNavItem>
-
{page.children && page.children.length ? (
<SidebarNavSubItemWrapper>
-
{page.children.map(childPage => (
<SidebarNavSubItem
to={relative(pathname, childPage.path)}
key={childPage.key}
>
···
</Fragment>
);
});
-
}, [currentPage, tree, pathname]);
-
return (
-
<SidebarContainer hidden={!sidebarOpen}>
-
<SideBarStripes />
-
<SidebarWrapper>
-
<HeroLogo />
-
<ContentWrapper>{sidebarItems}</ContentWrapper>
-
</SidebarWrapper>
-
</SidebarContainer>
-
);
};
export default Sidebar;
···
+
/* eslint-disable react-hooks/rules-of-hooks */
+
import React, { Fragment, useMemo } from 'react';
import styled from 'styled-components';
+
import { useBasepath } from 'react-static';
+
import { Link, useLocation } from 'react-router-dom';
import * as path from 'path';
import { useMarkdownTree, useMarkdownPage } from 'react-static-plugin-md-pages';
···
SidebarContainer,
SidebarWrapper,
SideBarStripes,
+
ChevronItem,
} from './navigation';
import logoSidebar from '../assets/sidebar-badge.svg';
+
const HeroLogoLink = styled(Link)`
+
display: flex;
+
flex-direction: row;
+
justify-content: center;
+
margin-bottom: ${p => p.theme.spacing.sm};
+
align-self: center;
+
`;
+
const HeroLogo = styled.img.attrs(() => ({
src: logoSidebar,
alt: 'urql',
}))`
display: none;
width: ${p => p.theme.layout.logo};
@media ${p => p.theme.media.sm} {
display: block;
···
flex-direction: column;
padding-top: ${p => p.theme.spacing.xs};
padding-bottom: ${p => p.theme.spacing.lg};
`;
+
export const relative = (from, to) => {
if (!from || !to) return null;
+
let [toPath, hash] = to.split('#');
+
let pathname = path.relative(path.dirname(from), toPath);
if (!pathname)
+
pathname = path.join(path.relative(from, toPath), path.basename(toPath));
+
if (from.endsWith('/')) pathname = '../' + pathname + '/';
+
if (!pathname.endsWith('/')) pathname += '/';
+
if (hash) pathname += `#${hash}`;
return { pathname };
};
+
export const SidebarStyling = ({ children, sidebarOpen }) => {
+
const basepath = useBasepath() || '';
+
const homepage = basepath ? `/${basepath}/` : '/';
+
+
return (
+
<>
+
<SideBarStripes />
+
<SidebarContainer hidden={!sidebarOpen}>
+
<SidebarWrapper>
+
<HeroLogoLink to={homepage}>
+
<HeroLogo />
+
</HeroLogoLink>
+
<ContentWrapper>{children}</ContentWrapper>
+
</SidebarWrapper>
+
</SidebarContainer>
+
</>
+
);
+
};
+
+
const Sidebar = props => {
const location = useLocation();
const currentPage = useMarkdownPage();
const tree = useMarkdownTree();
const sidebarItems = useMemo(() => {
+
if (!currentPage || !tree || !tree.children || !location) {
return null;
}
+
const pathname = location.pathname.endsWith('/')
+
? currentPage.path + '/'
+
: currentPage.path;
+
let children = tree.children;
if (tree.frontmatter && tree.originalPath) {
children = [{ ...tree, children: undefined }, ...children];
}
return children.map(page => {
+
const pageChildren = page.children || [];
+
+
const isActive = pageChildren.length
+
? currentPage.path.startsWith(page.path)
+
: currentPage.path === page.path;
+
return (
<Fragment key={page.key}>
+
<SidebarNavItem
+
to={relative(pathname, page.path)}
+
isActive={() => isActive}
+
>
{page.frontmatter.title}
+
{pageChildren.length ? <ChevronItem /> : null}
</SidebarNavItem>
+
{pageChildren.length && isActive ? (
<SidebarNavSubItemWrapper>
+
{pageChildren.map(childPage => (
<SidebarNavSubItem
+
isActive={() => childPage.path === currentPage.path}
to={relative(pathname, childPage.path)}
key={childPage.key}
>
···
</Fragment>
);
});
+
}, [currentPage, tree, location]);
+
return <SidebarStyling {...props}>{sidebarItems}</SidebarStyling>;
};
export default Sidebar;
+1 -1
packages/site/src/screens/404/404.js
···
import React from 'react';
const NotFound = props => {
-
return <h1>404! HELP I NEED DESIGNS</h1>;
};
export default NotFound;
···
import React from 'react';
const NotFound = props => {
+
return <h1>404! That page does not exist :(</h1>;
};
export default NotFound;
+7 -157
packages/site/src/screens/404/index.js
···
-
import React, { forwardRef } from 'react';
-
import styled from 'styled-components';
-
import PropTypes from 'prop-types';
-
import { withRouteData } from 'react-static';
-
import { Link } from 'react-router-dom';
-
import NotFoundPage from './404';
-
import Sidebar from '../../components/sidebar';
-
import constants from '../../constants';
-
import burger from '../../assets/burger.svg';
-
import logoFormidableDark from '../../assets/logo_formidable_dark.svg';
-
-
const Container = styled.div`
-
display: flex;
-
flex-direction: row;
-
width: 100%;
-
`;
-
-
const Wrapper = styled.div`
-
align-items: center;
-
display: flex;
-
flex-direction: row;
-
height: 6rem;
-
width: 100%;
-
position: fixed;
-
left: 19rem;
-
background: white;
-
z-index: 1;
-
padding-right: 3rem;
-
box-shadow: 0 5px 10px -5px lightgrey;
-
-
@media (max-width: 768px) {
-
box-shadow: 0 5px 10px -5px lightgrey;
-
margin-left: 2.5rem;
-
right: 0;
-
width: calc(100% - 2rem);
-
justify-content: flex-start;
-
left: 0;
-
}
-
`;
-
-
const HeaderLogo = styled.img`
-
position: relative;
-
right: 25rem;
-
-
@media (max-width: 768px) {
-
right: 7rem;
-
padding-left: 2rem;
-
}
-
@media (max-width: 600px) {
-
display: none;
-
}
-
`;
-
-
const CollapsedMenu = styled.div`
-
cursor: pointer;
-
padding-left: 2.5rem;
-
display: none;
-
-
@media (max-width: 768px) {
-
display: block;
-
visibility: ${props => (props.overlay ? 'hidden' : 'visible')};
-
padding-left: 2.5rem;
-
position: absolute;
-
left: 0;
-
}
-
@media (max-width: 600px) {
-
padding-left: 2.5rem;
-
position: absolute;
-
left: 0;
-
}
-
`;
-
-
const DocsTitle = styled.h2`
-
font-size: 3rem;
-
top: 0.2rem;
-
flex: auto;
-
width: 100%;
-
letter-spacing: 0.5rem;
-
margin: 0;
-
position: relative;
-
left: 10rem;
-
-
@media (max-width: 768px) {
-
font-size: 3rem;
-
left: 6.5rem;
-
margin: 0;
-
}
-
@media (max-width: 600px) {
-
left: 6.5rem;
-
}
-
`;
-
-
// eslint-disable-next-line react/display-name
-
const SideBarWithRef = forwardRef((props, ref) => {
return (
-
<div ref={ref}>
-
<Sidebar {...props} />
-
</div>
);
-
});
-
-
/* eslint-disable react/no-multi-comp */
-
class NotFound extends React.Component {
-
constructor(props) {
-
super(props);
-
this.closeSidebar = this.closeSidebar.bind(this);
-
this.state = { openSidebar: false };
-
this.sidebarRef = React.createRef();
-
}
-
-
openSidebar() {
-
this.setState({ openSidebar: true });
-
}
-
-
closeSidebar() {
-
this.setState({ openSidebar: false });
-
}
-
-
render() {
-
return (
-
<Container
-
onClick={event => {
-
return !this.sidebarRef.current.contains(event.target) &&
-
this.state.openSidebar
-
? this.closeSidebar()
-
: null;
-
}}
-
>
-
<Wrapper noPadding>
-
<CollapsedMenu overlay={this.state.openSidebar}>
-
<img src={burger} alt="Menu" onClick={() => this.openSidebar()} />
-
</CollapsedMenu>
-
<DocsTitle>
-
<Link to={'/'} style={{ color: '#3b3b3b' }}>
-
{constants.docsTitle}
-
</Link>
-
</DocsTitle>
-
<Link to={'https://formidable.com'}>
-
<HeaderLogo src={logoFormidableDark} alt="Formidable Logo" />
-
</Link>
-
</Wrapper>
-
<SideBarWithRef
-
overlay={this.state.openSidebar}
-
closeSidebar={this.closeSidebar}
-
ref={this.sidebarRef}
-
/>
-
<NotFoundPage />
-
</Container>
-
);
-
}
-
}
-
-
NotFound.propTypes = {
-
params: PropTypes.object,
};
-
NotFound.defaultProps = {
-
params: null,
-
};
-
-
export default withRouteData(NotFound);
···
+
import React from 'react';
+
import Docs from '../docs';
import NotFoundPage from './404';
+
const NotFound = () => {
return (
+
<Docs isLoading>
+
<NotFoundPage />
+
</Docs>
);
};
+
export default NotFound;
+20 -14
packages/site/src/screens/docs/article.js
···
import React from 'react';
import styled from 'styled-components';
import { useMarkdownPage } from 'react-static-plugin-md-pages';
import { MDXComponents } from '../../components/mdx';
···
}))`
flex: 1;
width: 100%;
-
position: sticky;
display: flex;
flex-direction: row-reverse;
`;
const Content = styled.article.attrs(() => ({
···
display: block;
position: sticky;
top: ${p => p.theme.layout.header};
-
max-height: 100vh;
width: 100%;
max-width: ${p => p.theme.layout.legend};
-
margin: 0 ${p => p.theme.spacing.md};
-
padding: ${p => p.theme.spacing.lg} 0;
}
`;
···
> a {
font-size: ${p => p.theme.fontSizes.small};
font-weight: ${p => p.theme.fontWeights.body};
-
color: ${p => p.theme.colors.heading};
text-decoration: none;
-
opacity: 0.7;
}
`;
const SectionList = () => {
const page = useMarkdownPage();
-
if (!page) return null;
const headings = page.headings.filter(x => x.depth > 1);
if (headings.length === 0) return null;
···
);
};
-
const Article = ({ children }) => (
<Container>
-
<Legend>
-
<SectionList />
-
</Legend>
-
<Content>
<MDXComponents>{children}</MDXComponents>
-
</Content>
-
</Container>
);
export default Article;
···
+
/* eslint-disable react-hooks/rules-of-hooks */
+
import React from 'react';
import styled from 'styled-components';
import { useMarkdownPage } from 'react-static-plugin-md-pages';
+
import { ScrollToTop } from '../../components/scroll-to-top';
import { MDXComponents } from '../../components/mdx';
···
}))`
flex: 1;
width: 100%;
display: flex;
flex-direction: row-reverse;
+
align-items: flex-start;
`;
const Content = styled.article.attrs(() => ({
···
display: block;
position: sticky;
top: ${p => p.theme.layout.header};
width: 100%;
max-width: ${p => p.theme.layout.legend};
+
padding: ${p => p.theme.spacing.lg} ${p => p.theme.spacing.md};
+
margin: 0;
}
`;
···
> a {
font-size: ${p => p.theme.fontSizes.small};
font-weight: ${p => p.theme.fontWeights.body};
+
color: ${p => p.theme.colors.passive};
text-decoration: none;
}
`;
const SectionList = () => {
const page = useMarkdownPage();
+
if (!page || !page.headings) return null;
const headings = page.headings.filter(x => x.depth > 1);
if (headings.length === 0) return null;
···
);
};
+
export const ArticleStyling = ({ children, SectionList }) => (
<Container>
+
<Legend>{SectionList && <SectionList />}</Legend>
+
<Content>{children}</Content>
+
</Container>
+
);
+
+
const Article = ({ children }) => (
+
<>
+
<ScrollToTop />
+
<ArticleStyling SectionList={SectionList}>
<MDXComponents>{children}</MDXComponents>
+
</ArticleStyling>
+
</>
);
export default Article;
+2 -1
packages/site/src/screens/docs/header.js
···
const Header = () => {
const basepath = useBasepath() || '';
return (
<Fixed>
···
<BlockLink href="https://formidable.com/">
<FormidableLogo />
</BlockLink>
-
<ProjectWording to={`/${basepath}/`}>urql</ProjectWording>
</Wrapper>
</Fixed>
);
···
const Header = () => {
const basepath = useBasepath() || '';
+
const homepage = basepath ? `/${basepath}/` : '/';
return (
<Fixed>
···
<BlockLink href="https://formidable.com/">
<FormidableLogo />
</BlockLink>
+
<ProjectWording to={homepage}>urql</ProjectWording>
</Wrapper>
</Fixed>
);
+18 -21
packages/site/src/screens/docs/index.js
···
import React, { useState } from 'react';
import styled from 'styled-components';
-
import PropTypes from 'prop-types';
-
import { withRouteData } from 'react-static';
-
import Article from './article';
import Header from './header';
-
import Sidebar from '../../components/sidebar';
import burger from '../../assets/burger.svg';
import closeButton from '../../assets/close.svg';
-
export const Container = styled.div`
display: flex;
flex-direction: row;
···
}
`;
-
const Docs = props => {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
···
sidebarOpen={sidebarOpen}
onClick={() => setSidebarOpen(prev => !prev)}
/>
-
<Sidebar sidebarOpen={sidebarOpen} />
-
<Article>{props.children}</Article>
</Container>
</>
);
};
-
Docs.propTypes = {
-
location: PropTypes.object,
-
params: PropTypes.object,
-
sidebarHeaders: PropTypes.array,
-
slug: PropTypes.string,
-
toc: PropTypes.object,
-
};
-
-
Docs.defaultProps = {
-
params: null,
-
};
-
-
export default withRouteData(Docs);
···
import React, { useState } from 'react';
import styled from 'styled-components';
+
import Article, { ArticleStyling } from './article';
import Header from './header';
+
import Sidebar, { SidebarStyling } from '../../components/sidebar';
import burger from '../../assets/burger.svg';
import closeButton from '../../assets/close.svg';
+
const Container = styled.div`
+
position: relative;
display: flex;
flex-direction: row;
···
}
`;
+
const Docs = ({ isLoading, children }) => {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
···
sidebarOpen={sidebarOpen}
onClick={() => setSidebarOpen(prev => !prev)}
/>
+
{/* load just the styles if Suspense fallback in use */}
+
{isLoading ? (
+
<>
+
<SidebarStyling sidebarOpen={sidebarOpen} />
+
<ArticleStyling>{children}</ArticleStyling>
+
</>
+
) : (
+
<>
+
<Sidebar sidebarOpen={sidebarOpen} />
+
<Article>{children}</Article>
+
</>
+
)}
</Container>
</>
);
};
+
export default Docs;
+19 -16
packages/site/src/screens/home/index.js
···
import React from 'react';
import styled from 'styled-components';
import Features from './features';
import GetStarted from './get-started';
···
width: 100%;
`;
-
class Home extends React.Component {
-
render() {
-
return (
-
<Container>
-
<Header />
-
<Features
-
featureArray={content.features}
-
components={content.components}
-
/>
-
<GetStarted getStartedObj={content.getStarted} />
-
<MoreOSS ossArray={content.oss} />
-
<Footer />
-
</Container>
-
);
-
}
-
}
export default Home;
···
import React from 'react';
import styled from 'styled-components';
+
import { usePrefetch } from 'react-static';
+
import { useMarkdownTree } from 'react-static-plugin-md-pages';
import Features from './features';
import GetStarted from './get-started';
···
width: 100%;
`;
+
const Home = () => {
+
const ref = usePrefetch('docs');
+
useMarkdownTree();
+
+
return (
+
<Container ref={ref}>
+
<Header />
+
<Features
+
featureArray={content.features}
+
components={content.components}
+
/>
+
<GetStarted getStartedObj={content.getStarted} />
+
<MoreOSS ossArray={content.oss} />
+
<Footer />
+
</Container>
+
);
+
};
export default Home;
-12
packages/site/src/scroll-to-top.js
···
-
import { useEffect } from 'react';
-
import { useLocation } from 'react-router';
-
-
export const ScrollToTop = () => {
-
const { pathname } = useLocation();
-
-
useEffect(() => {
-
window.scrollTo(0, 0);
-
}, [pathname]);
-
-
return null;
-
};
···
+5 -1
packages/site/src/styles/global.js
···
background: ${p => p.theme.colors.passiveBg};
color: ${p => p.theme.colors.text};
font-family: ${p => p.theme.fonts.body};
-
font-size: ${p => p.theme.fontSizes.body};
line-height: ${p => p.theme.lineHeights.body};
font-weight: ${p => p.theme.fontWeights.body};
text-rendering: optimizeLegibility;
margin: 0;
padding: 0;
}
a {
···
background: ${p => p.theme.colors.passiveBg};
color: ${p => p.theme.colors.text};
font-family: ${p => p.theme.fonts.body};
line-height: ${p => p.theme.lineHeights.body};
font-weight: ${p => p.theme.fontWeights.body};
text-rendering: optimizeLegibility;
margin: 0;
padding: 0;
+
+
font-size: ${p => p.theme.fontSizes.bodySmall};
+
@media ${p => p.theme.media.lg} {
+
font-size: ${p => p.theme.fontSizes.body};
+
}
}
a {
+5 -4
packages/site/src/styles/theme.js
···
border: '#ececec',
activeBorder: '#a2b1ff',
text: '#000000',
-
heading: '#050617',
accent: '#566ac8',
code: '#403f53',
-
passive: '#9999a6',
};
export const layout = {
page: '144rem',
header: '4.8rem',
-
stripes: '1rem',
-
sidebar: '28rem',
legend: '22rem',
logo: '12rem',
};
···
export const fontSizes = {
small: '0.9em',
body: '1.8rem',
code: '0.8em',
h1: '3.45em',
h2: '2.11em',
···
border: '#ececec',
activeBorder: '#a2b1ff',
text: '#000000',
+
heading: '#444444',
accent: '#566ac8',
code: '#403f53',
+
passive: '#444444',
};
export const layout = {
page: '144rem',
header: '4.8rem',
+
stripes: '0.7rem',
+
sidebar: '26rem',
legend: '22rem',
logo: '12rem',
};
···
export const fontSizes = {
small: '0.9em',
body: '1.8rem',
+
bodySmall: '1.5rem',
code: '0.8em',
h1: '3.45em',
h2: '2.11em',
+4
packages/site/static.config.js
···
path: '/',
template: require.resolve('./src/screens/home'),
},
],
};
···
path: '/',
template: require.resolve('./src/screens/home'),
},
+
{
+
path: '404',
+
template: require.resolve('./src/screens/404'),
+
},
],
};
+5 -9
yarn.lock
···
source-map "^0.6.1"
supports-color "^6.1.0"
-
preact@^10.3.1:
-
version "10.3.1"
-
resolved "https://registry.yarnpkg.com/preact/-/preact-10.3.1.tgz#70a2cc5484ca727c992216dfc528907d240e0a05"
-
-
preact@^10.3.3:
version "10.3.3"
resolved "https://registry.yarnpkg.com/preact/-/preact-10.3.3.tgz#31a949cdc89dd1cf72bc2b94f80ad55d1ac8c7e6"
integrity sha512-8EWUuHuLhX48uK0acnnXwBL/ZyrgeadnyhxG6hFI85BhwmquyGwWzfj2SylCNdEyltkdgbY3FZzQOM2mG1leAg==
···
dependencies:
object-is "^1.0.2"
-
react-static-plugin-md-pages@^0.1.3:
-
version "0.1.3"
-
resolved "https://registry.yarnpkg.com/react-static-plugin-md-pages/-/react-static-plugin-md-pages-0.1.3.tgz#2c019e9e5e407b5d7701669b9b4581d506662ab5"
-
integrity sha512-aONsWlmeZECJgAz5Er7Jw83RlGLXgf08OFMmn2e3+BADhbheKp7ZWvC50l3AP8jwRr6wslxCO/2kwx99GC0u6A==
dependencies:
"@mdx-js/mdx" "^1.5.5"
"@mdx-js/react" "^1.5.5"
···
source-map "^0.6.1"
supports-color "^6.1.0"
+
preact@^10.3.1, preact@^10.3.3:
version "10.3.3"
resolved "https://registry.yarnpkg.com/preact/-/preact-10.3.3.tgz#31a949cdc89dd1cf72bc2b94f80ad55d1ac8c7e6"
integrity sha512-8EWUuHuLhX48uK0acnnXwBL/ZyrgeadnyhxG6hFI85BhwmquyGwWzfj2SylCNdEyltkdgbY3FZzQOM2mG1leAg==
···
dependencies:
object-is "^1.0.2"
+
react-static-plugin-md-pages@^0.1.7:
+
version "0.1.7"
+
resolved "https://registry.yarnpkg.com/react-static-plugin-md-pages/-/react-static-plugin-md-pages-0.1.7.tgz#13f3daf1ce32fef9a77d037600572aeb752340c8"
+
integrity sha512-Y17VFyrOBMi2FqfumB5a50RHu2TbckC9LL2GMotJjL0PhbdpJBhgL6vbjEdJXoJxjk2JR93/dyZZcJmXhSL5MA==
dependencies:
"@mdx-js/mdx" "^1.5.5"
"@mdx-js/react" "^1.5.5"