Feature/website mobile version (#52)
This commit is contained in:
parent
8c8a59093d
commit
1b522152d0
@ -8,14 +8,14 @@ Static CMS is an open source content management system for your Git workflow tha
|
||||
|
||||
At its core, Static CMS is an open-source React app that acts as a wrapper for the Git workflow, using the GitHub, GitLab, or Bitbucket API. This provides many advantages, including:
|
||||
|
||||
* **Fast, web-based UI:** With rich-text editing, real-time preview, and drag-and-drop media uploads.
|
||||
* **Platform agnostic:** Works with most static site generators.
|
||||
* **Easy installation:** Add two files to your site and hook up the backend by including those files in your build process or linking to our Content Delivery Network (CDN).
|
||||
* **Modern authentication:** Using GitHub, GitLab, or Bitbucket and JSON web tokens.
|
||||
* **Flexible content types:** Specify an unlimited number of content types with custom fields.
|
||||
* **Fully extensible:** Create custom-styled previews, UI widgets, and editor plugins.
|
||||
- **Fast, web-based UI:** With rich-text editing, real-time preview, and drag-and-drop media uploads.
|
||||
- **Platform agnostic:** Works with most static site generators.
|
||||
- **Easy installation:** Add two files to your site and hook up the backend by including those files in your build process or linking to our Content Delivery Network (CDN).
|
||||
- **Modern authentication:** Using GitHub, GitLab, or Bitbucket and JSON web tokens.
|
||||
- **Flexible content types:** Specify an unlimited number of content types with custom fields.
|
||||
- **Fully extensible:** Create custom-styled previews, UI widgets, and editor plugins.
|
||||
|
||||
### Find out more
|
||||
## Find out more
|
||||
|
||||
- Get a feel for the UI in the [demo site](https://cms-demo.netlify.com). (No login required. Click the login button to go straight to the CMS editor UI.)
|
||||
- [Start with a template](/docs/start-with-a-template/) to make a Static CMS-enabled site of your own.
|
||||
|
@ -6,29 +6,29 @@ weight: 1
|
||||
|
||||
The process for adding Static CMS to a static site can be divided into four main steps:
|
||||
|
||||
### Install Static CMS
|
||||
## Install Static CMS
|
||||
|
||||
This is a single page app available at the `/admin` route of your website.
|
||||
Check out the [general overview](/docs/intro/) to see what the installation process entails.
|
||||
|
||||
### Set up a backend
|
||||
## Set up a backend
|
||||
|
||||
This serves two purpose: Secure access to your website's Static CMS and allows it to read and update content files in your repo. More information about configuring the backend can be found [here](/docs/backends-overview/).
|
||||
|
||||
### Configure Static CMS using a configuration file
|
||||
## Configure Static CMS using a configuration file
|
||||
|
||||
For starters, you can get by with a basic configuration that includes required information like Git provider, branch and folders to save files to.
|
||||
|
||||
Once you've gotten the hang of it, you can use the file to build whatever collections and content modeling you want. Check out the [this section](/docs/configuration-options/#configuration-file) for full details about all available configuration options.
|
||||
|
||||
### Render the content provided by Static CMS as web pages
|
||||
## Render the content provided by Static CMS as web pages
|
||||
|
||||
Static CMS manages your content, and provides admin features, but it doesn't deliver content. It only makes your content available through an API.
|
||||
|
||||
It is up to developers to determine how to build the raw content into something useful and delightful on the frontend.
|
||||
|
||||
To learn how to query raw content managed by Static CMS and reformat them for delivery to end users, please refer the dedicated section for your site generator in the Table of Content.
|
||||
___
|
||||
### Local development
|
||||
|
||||
## Local development
|
||||
|
||||
If you are experimenting with Static CMS or testing things out, you can connect it to a local Git repository instead of a live one. Learn how to do it [here](/docs/beta-features/#working-with-a-local-git-repository).
|
||||
|
@ -23,7 +23,7 @@ interface CommunitySectionProps {
|
||||
const CommunitySection = ({ section }: CommunitySectionProps) => {
|
||||
return (
|
||||
<StyledCommunitySection>
|
||||
<Typography variant="h5" component="h3">
|
||||
<Typography variant="h3">
|
||||
{section.title}
|
||||
</Typography>
|
||||
<List sx={{ width: '100%', bgcolor: 'background.paper' }}>
|
||||
|
@ -10,6 +10,14 @@ const DocsContent = styled('div')(
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
${theme.breakpoints.between('sm', 'lg')} {
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
${theme.breakpoints.down('sm')} {
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
& time {
|
||||
color: #9b9b9b;
|
||||
}
|
||||
@ -54,7 +62,7 @@ const DocsContent = styled('div')(
|
||||
color: ${theme.palette.text.primary};
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
${theme.breakpoints.up('lg')} {
|
||||
& h1 {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
@ -66,13 +74,13 @@ const DocsContent = styled('div')(
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) and (max-width: 1200px) {
|
||||
${theme.breakpoints.between('sm', 'lg')} {
|
||||
& h1 {
|
||||
font-size: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
${theme.breakpoints.down('sm')} {
|
||||
& h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
@ -84,13 +92,13 @@ const DocsContent = styled('div')(
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) and (max-width: 1200px) {
|
||||
${theme.breakpoints.between('sm', 'lg')} {
|
||||
& h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
${theme.breakpoints.down('sm')} {
|
||||
& h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
@ -102,13 +110,13 @@ const DocsContent = styled('div')(
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) and (max-width: 1200px) {
|
||||
${theme.breakpoints.between('sm', 'lg')} {
|
||||
& h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
${theme.breakpoints.down('sm')} {
|
||||
& h3 {
|
||||
font-size: 17px;
|
||||
}
|
||||
@ -119,7 +127,7 @@ const DocsContent = styled('div')(
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
${theme.breakpoints.down('lg')} {
|
||||
& h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
@ -130,7 +138,7 @@ const DocsContent = styled('div')(
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
${theme.breakpoints.down('lg')} {
|
||||
& h5 {
|
||||
font-size: 15px;
|
||||
}
|
||||
@ -141,7 +149,7 @@ const DocsContent = styled('div')(
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
${theme.breakpoints.down('lg')} {
|
||||
& h6 {
|
||||
font-size: 14px;
|
||||
}
|
||||
@ -295,7 +303,7 @@ const DocsContent = styled('div')(
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
${theme.breakpoints.down('md')} {
|
||||
& h2,
|
||||
& h3,
|
||||
& h4,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import List from '@mui/material/List';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
|
||||
import DocsLeftNavGroup from './DocsLeftNavGroup';
|
||||
|
||||
@ -9,6 +10,8 @@ export interface DocsLeftNavProps {
|
||||
}
|
||||
|
||||
const DocsLeftNav = ({ docsGroups }: DocsLeftNavProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<List
|
||||
component="nav"
|
||||
@ -23,6 +26,9 @@ const DocsLeftNav = ({ docsGroups }: DocsLeftNavProps) => {
|
||||
bottom: 0,
|
||||
overflowY: 'auto',
|
||||
paddingBottom: '24px',
|
||||
[theme.breakpoints.down('lg')]: {
|
||||
display: 'none',
|
||||
},
|
||||
}}
|
||||
dense
|
||||
>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import LinkIcon from '@mui/icons-material/Link';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Link from 'next/link';
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
@ -8,10 +9,14 @@ const StyledLink = styled('a')(
|
||||
({ theme }) => `
|
||||
position: absolute;
|
||||
margin-left: -28px;
|
||||
top: 0;
|
||||
top: -1px;
|
||||
font-weight: 300;
|
||||
color: ${theme.palette.secondary.main};
|
||||
transform: rotateZ(-45deg)
|
||||
transform: rotateZ(-45deg);
|
||||
|
||||
${theme.breakpoints.down('sm')} {
|
||||
margin-left: -22px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
@ -29,15 +34,34 @@ interface Header2Props {
|
||||
const Header2 = ({ children = '' }: Header2Props) => {
|
||||
const anchor = getAnchor(String(children));
|
||||
const link = `#${anchor}`;
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<h2 id={anchor}>
|
||||
<Typography
|
||||
variant="h2"
|
||||
id={anchor}
|
||||
sx={{
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
marginLeft: '8px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Link href={link} className="anchor-link">
|
||||
<StyledLink href={link}>
|
||||
<LinkIcon fontSize="medium" />
|
||||
<LinkIcon
|
||||
fontSize="medium"
|
||||
sx={{
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
fontSize: '20px',
|
||||
height: '20px',
|
||||
width: '20px',
|
||||
marginTop: '2px',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</StyledLink>
|
||||
</Link>
|
||||
{children}
|
||||
</h2>
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import LinkIcon from '@mui/icons-material/Link';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Link from 'next/link';
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
@ -11,7 +12,12 @@ const StyledLink = styled('a')(
|
||||
top: 0;
|
||||
font-weight: 300;
|
||||
color: ${theme.palette.text.primary};
|
||||
transform: rotateZ(-45deg)
|
||||
transform: rotateZ(-45deg);
|
||||
|
||||
${theme.breakpoints.down('sm')} {
|
||||
margin-left: -22px;
|
||||
top: -1px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
@ -29,15 +35,34 @@ interface Header3Props {
|
||||
const Header3 = ({ children = '' }: Header3Props) => {
|
||||
const anchor = getAnchor(String(children));
|
||||
const link = `#${anchor}`;
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<h3 id={anchor}>
|
||||
<Typography
|
||||
variant="h3"
|
||||
id={anchor}
|
||||
sx={{
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
marginLeft: '8px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Link href={link} className="anchor-link">
|
||||
<StyledLink href={link}>
|
||||
<LinkIcon fontSize="small" />
|
||||
<LinkIcon
|
||||
fontSize="small"
|
||||
sx={{
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
fontSize: '20px',
|
||||
height: '20px',
|
||||
width: '20px',
|
||||
marginTop: '2px',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</StyledLink>
|
||||
</Link>
|
||||
{children}
|
||||
</h3>
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -2,12 +2,18 @@ import { styled } from '@mui/material/styles';
|
||||
|
||||
import type { NestedHeading } from './DocsTableOfContents';
|
||||
|
||||
const StyledList = styled('ul')`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
`;
|
||||
const StyledList = styled('ul')(
|
||||
({ theme }) => `
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
|
||||
${theme.breakpoints.down('lg')} {
|
||||
margin-top: 0;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledListItem = styled('li')(
|
||||
({ theme }) => `
|
||||
|
@ -38,8 +38,8 @@ const useHeadingsData = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const headingElements = Array.from(
|
||||
document.querySelectorAll('main h2, main h3'),
|
||||
) as HTMLHeadingElement[];
|
||||
document.querySelectorAll<HTMLHeadingElement>('main h2, main h3'),
|
||||
);
|
||||
|
||||
// Created a list of headings, with H3s nested
|
||||
const newNestedHeadings = getNestedHeadings(headingElements);
|
||||
@ -50,10 +50,10 @@ const useHeadingsData = () => {
|
||||
};
|
||||
|
||||
const useIntersectionObserver = (setActiveId: (activeId: string) => void) => {
|
||||
const headingElementsRef = useRef({});
|
||||
const headingElementsRef = useRef<Record<string, IntersectionObserverEntry>>({});
|
||||
const { asPath } = useRouter();
|
||||
useEffect(() => {
|
||||
const headingElements = Array.from(document.querySelectorAll('h2, h3'));
|
||||
const headingElements = Array.from(document.querySelectorAll<HTMLHeadingElement>('h2, h3'));
|
||||
|
||||
if (headingElementsRef.current) {
|
||||
headingElementsRef.current = {};
|
||||
@ -105,16 +105,26 @@ const useIntersectionObserver = (setActiveId: (activeId: string) => void) => {
|
||||
}, [setActiveId, asPath]);
|
||||
};
|
||||
|
||||
const StyledNav = styled('nav')`
|
||||
width: 100%;
|
||||
padding: 0 16px 16px 0;
|
||||
align-self: flex-start;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
max-height: calc(100vh - 72px);
|
||||
overflow-y: auto;
|
||||
top: 16px;
|
||||
`;
|
||||
const StyledNav = styled('nav')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
padding: 0 16px 16px 0;
|
||||
align-self: flex-start;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
max-height: calc(100vh - 72px);
|
||||
overflow-y: auto;
|
||||
top: 16px;
|
||||
|
||||
${theme.breakpoints.between('md', 'lg')} {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const DocsTableOfContents = () => {
|
||||
const [activeId, setActiveId] = useState<string>();
|
||||
|
@ -2,14 +2,20 @@ import { styled } from '@mui/material/styles';
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
const StyledContainer = styled('div')`
|
||||
max-width: 1280px;
|
||||
width: 100%;
|
||||
padding: 0 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
`;
|
||||
const StyledContainer = styled('div')(
|
||||
({ theme }) => `
|
||||
max-width: 1280px;
|
||||
width: 100%;
|
||||
padding: 0 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
padding: 0 32px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
export interface PageProps {
|
||||
children: ReactNode;
|
||||
|
@ -1,19 +1,23 @@
|
||||
import Brightness4Icon from '@mui/icons-material/Brightness4';
|
||||
import Brightness7Icon from '@mui/icons-material/Brightness7';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Button from '@mui/material/Button';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import Brightness4Icon from '@mui/icons-material/Brightness4';
|
||||
import Brightness7Icon from '@mui/icons-material/Brightness7';
|
||||
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import Logo from './Logo';
|
||||
import NavigationDrawer from './mobile-drawer/NavigationDrawer';
|
||||
import Search from './Search';
|
||||
|
||||
import type { PaletteMode } from '@mui/material';
|
||||
import type { ButtonTypeMap } from '@mui/material/Button';
|
||||
import type { ExtendButtonBase } from '@mui/material/ButtonBase';
|
||||
import type { PaletteMode } from '@mui/material';
|
||||
import type { DocsGroup, MenuItem } from '../../interface';
|
||||
|
||||
const StyledAppBar = styled(AppBar)(
|
||||
({ theme }) => `
|
||||
@ -21,17 +25,47 @@ const StyledAppBar = styled(AppBar)(
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledToolbar = styled(Toolbar)`
|
||||
gap: 16px;
|
||||
`;
|
||||
const StyledToolbar = styled(Toolbar)(
|
||||
({ theme }) => `
|
||||
gap: 16px;
|
||||
height: 72px;
|
||||
|
||||
const StyledGithubLink = styled('a')`
|
||||
${theme.breakpoints.down('lg')} {
|
||||
justify-content: space-between;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledIconsWrapper = styled('div')(
|
||||
({ theme }) => `
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
${theme.breakpoints.up('lg')} {
|
||||
flex-grow: 1;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledGithubLink = styled('a')(
|
||||
({ theme }) => `
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
${theme.breakpoints.down('lg')} {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledGithubImage = styled('img')`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledMenuButton = styled(IconButton)(
|
||||
({ theme }) => `
|
||||
${theme.breakpoints.up('md')} {
|
||||
${theme.breakpoints.up('lg')} {
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
width: 0;
|
||||
@ -40,102 +74,136 @@ const StyledMenuButton = styled(IconButton)(
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledGap = styled('div')`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
const StyledDesktopGap = styled('div')(
|
||||
({ theme }) => `
|
||||
flex-grow: 1;
|
||||
|
||||
const StyledSearchBox = styled(TextField)`
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
${theme.breakpoints.down('lg')} {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
.MuiSvgIcon-root {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.MuiInputBase-root {
|
||||
const StyledDesktopLink = styled(Button)(
|
||||
({ theme }) => `
|
||||
color: white;
|
||||
}
|
||||
|
||||
.MuiOutlinedInput-notchedOutline {
|
||||
border: none;
|
||||
}
|
||||
`;
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
const StyledLink = styled(Button)`
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
` as ExtendButtonBase<ButtonTypeMap<{}, 'a'>>;
|
||||
|
||||
const StyledImageLink = styled('a')`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const StyledImage = styled(Image)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
${theme.breakpoints.down('lg')} {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
) as ExtendButtonBase<ButtonTypeMap<{}, 'a'>>;
|
||||
|
||||
interface HeaderProps {
|
||||
mode: PaletteMode;
|
||||
docsGroups: DocsGroup[];
|
||||
toggleColorMode: () => void;
|
||||
}
|
||||
|
||||
const Header = ({ mode, toggleColorMode }: HeaderProps) => {
|
||||
const Header = ({ mode, docsGroups, toggleColorMode }: HeaderProps) => {
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
|
||||
const handleDrawerToggle = useCallback(() => {
|
||||
setMobileOpen(!mobileOpen);
|
||||
}, [mobileOpen]);
|
||||
|
||||
const items: MenuItem[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: 'Docs',
|
||||
path: '/docs',
|
||||
groups: docsGroups.map(group => ({
|
||||
title: group.title,
|
||||
links: group.links.map(link => ({
|
||||
title: link.title,
|
||||
url: `/docs/${link.slug}`,
|
||||
})),
|
||||
})),
|
||||
},
|
||||
{
|
||||
title: 'Contributing',
|
||||
url: '/docs/contributor-guide',
|
||||
},
|
||||
{
|
||||
title: 'Community',
|
||||
url: '/community',
|
||||
},
|
||||
],
|
||||
[docsGroups],
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledAppBar position="fixed">
|
||||
<StyledToolbar>
|
||||
<StyledMenuButton size="large" edge="start" color="inherit" aria-label="menu">
|
||||
<MenuIcon fontSize="large" />
|
||||
</StyledMenuButton>
|
||||
<Link href="/">
|
||||
<StyledImageLink>
|
||||
<StyledImage src="/static-cms-logo.svg" width={182} height={72} />
|
||||
</StyledImageLink>
|
||||
</Link>
|
||||
<StyledSearchBox
|
||||
placeholder="Search the docs"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
InputProps={{
|
||||
startAdornment: <SearchIcon />,
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
sx={{ ml: 1 }}
|
||||
onClick={toggleColorMode}
|
||||
color="inherit"
|
||||
title={mode === 'dark' ? 'Turn on the light' : 'Turn off the light'}
|
||||
>
|
||||
{mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
|
||||
</IconButton>
|
||||
<StyledGap />
|
||||
<StyledGithubLink
|
||||
href="https://github.com/StaticJsCMS/static-cms"
|
||||
aria-label="Star StaticJsCMS/static-cms on GitHub"
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
alt="Star StaticJsCMS/static-cms on GitHub"
|
||||
src="https://img.shields.io/github/stars/StaticJsCMS/static-cms?style=social"
|
||||
/>
|
||||
</StyledGithubLink>
|
||||
<Link href="/docs/intro">
|
||||
<StyledLink component="a">Docs</StyledLink>
|
||||
</Link>
|
||||
<Link href="/docs/contributor-guide">
|
||||
<StyledLink component="a">Contributing</StyledLink>
|
||||
</Link>
|
||||
<Link href="/community">
|
||||
<StyledLink component="a">Community</StyledLink>
|
||||
</Link>
|
||||
{/* <Link href="/blog">
|
||||
<>
|
||||
<StyledAppBar position="fixed">
|
||||
<StyledToolbar>
|
||||
<StyledMenuButton
|
||||
size="large"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="menu"
|
||||
onClick={handleDrawerToggle}
|
||||
>
|
||||
<MenuIcon fontSize="large" />
|
||||
</StyledMenuButton>
|
||||
<Logo />
|
||||
<Search />
|
||||
<StyledIconsWrapper>
|
||||
<IconButton
|
||||
sx={{ ml: 1 }}
|
||||
onClick={toggleColorMode}
|
||||
color="inherit"
|
||||
title={mode === 'dark' ? 'Turn on the light' : 'Turn off the light'}
|
||||
>
|
||||
{mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
|
||||
</IconButton>
|
||||
<StyledDesktopGap />
|
||||
<StyledGithubLink
|
||||
href="https://github.com/StaticJsCMS/static-cms"
|
||||
aria-label="Star StaticJsCMS/static-cms on GitHub"
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<StyledGithubImage
|
||||
alt="Star StaticJsCMS/static-cms on GitHub"
|
||||
src="https://img.shields.io/github/stars/StaticJsCMS/static-cms?style=social"
|
||||
/>
|
||||
</StyledGithubLink>
|
||||
<IconButton
|
||||
href="https://github.com/StaticJsCMS/static-cms"
|
||||
color="inherit"
|
||||
>
|
||||
<GitHubIcon />
|
||||
</IconButton>
|
||||
</StyledIconsWrapper>
|
||||
{items.map(item => {
|
||||
let url = '#';
|
||||
if ('url' in item) {
|
||||
url = item.url;
|
||||
} else if (item.groups.length > 0 && item.groups[0].links.length > 0) {
|
||||
url = item.groups[0].links[0].url;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link key={`desktop-${item.title}-${url}`} href={url}>
|
||||
<StyledDesktopLink component="a">{item.title}</StyledDesktopLink>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
{/* <Link href="/blog">
|
||||
<StyledLink component="a">Blog</StyledLink>
|
||||
</Link> */}
|
||||
</StyledToolbar>
|
||||
</StyledAppBar>
|
||||
</StyledToolbar>
|
||||
</StyledAppBar>
|
||||
<NavigationDrawer
|
||||
key="mobile-navigation-drawer"
|
||||
items={items}
|
||||
mobileOpen={mobileOpen}
|
||||
onMobileOpenToggle={handleDrawerToggle}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
51
website/src/components/layout/Logo.tsx
Normal file
51
website/src/components/layout/Logo.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { styled } from '@mui/material/styles';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import transientOptions from '../../util/transientOptions';
|
||||
|
||||
interface StyledImageLinkProps {
|
||||
$inDrawer: boolean;
|
||||
}
|
||||
|
||||
const StyledImageLink = styled(
|
||||
'a',
|
||||
transientOptions,
|
||||
)<StyledImageLinkProps>(
|
||||
({ theme, $inDrawer }) => `
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
${
|
||||
!$inDrawer
|
||||
? `
|
||||
${theme.breakpoints.down('lg')} {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
`
|
||||
: ''
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledImage = styled(Image)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
interface LogoProps {
|
||||
inDrawer?: boolean;
|
||||
}
|
||||
|
||||
const Logo = ({ inDrawer = false }: LogoProps) => {
|
||||
return (
|
||||
<Link href="/">
|
||||
<StyledImageLink $inDrawer={inDrawer}>
|
||||
<StyledImage src="/static-cms-logo.svg" width={182} height={72} />
|
||||
</StyledImageLink>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logo;
|
@ -12,6 +12,7 @@ import Container from './Container';
|
||||
import Header from './Header';
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import type { DocsGroup } from '../../interface';
|
||||
|
||||
const StyledPageContentWrapper = styled('div')`
|
||||
display: flex;
|
||||
@ -35,6 +36,7 @@ export interface PageProps {
|
||||
image?: string;
|
||||
};
|
||||
fullWidth?: boolean;
|
||||
docsGroups: DocsGroup[];
|
||||
}
|
||||
|
||||
const Page = ({
|
||||
@ -45,6 +47,7 @@ const Page = ({
|
||||
description,
|
||||
pageDetails,
|
||||
fullWidth = false,
|
||||
docsGroups,
|
||||
}: PageProps) => {
|
||||
const scrollableArea = useRef<HTMLDivElement | null>(null);
|
||||
const theme = useTheme();
|
||||
@ -89,7 +92,11 @@ const Page = ({
|
||||
description={description}
|
||||
/>
|
||||
) : null}
|
||||
<Header mode={theme.palette.mode} toggleColorMode={colorMode.toggleColorMode} />
|
||||
<Header
|
||||
mode={theme.palette.mode}
|
||||
docsGroups={docsGroups}
|
||||
toggleColorMode={colorMode.toggleColorMode}
|
||||
/>
|
||||
<StyledPageContentWrapper ref={scrollableArea}>{content}</StyledPageContentWrapper>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
41
website/src/components/layout/Search.tsx
Normal file
41
website/src/components/layout/Search.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import TextField from '@mui/material/TextField';
|
||||
|
||||
const StyledSearchBox = styled(TextField)(
|
||||
({ theme }) => `
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
|
||||
.MuiSvgIcon-root {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.MuiInputBase-root {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.MuiOutlinedInput-notchedOutline {
|
||||
border: none;
|
||||
}
|
||||
|
||||
${theme.breakpoints.down('lg')} {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const Search = () => {
|
||||
return (
|
||||
<StyledSearchBox
|
||||
placeholder="Search the docs"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
InputProps={{
|
||||
startAdornment: <SearchIcon />,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Search;
|
133
website/src/components/layout/mobile-drawer/MobileNavItem.tsx
Normal file
133
website/src/components/layout/mobile-drawer/MobileNavItem.tsx
Normal file
@ -0,0 +1,133 @@
|
||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||
import Collapse from '@mui/material/Collapse';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Link from 'next/link';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import ListSubheader from '@mui/material/ListSubheader';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import MobileNavLink from './MobileNavLink';
|
||||
|
||||
import type { MouseEvent } from 'react';
|
||||
import type { MenuItem, MenuLink, MenuLinkGroup } from '../../../interface';
|
||||
|
||||
interface MobileNavItemProps {
|
||||
item: MenuItem;
|
||||
}
|
||||
|
||||
function isMenuLinkGroup(link: MenuItem): link is MenuLinkGroup {
|
||||
return 'groups' in link;
|
||||
}
|
||||
|
||||
const MobileNavItem = ({ item }: MobileNavItemProps) => {
|
||||
const theme = useTheme();
|
||||
const { asPath } = useRouter();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if ('url' in item) {
|
||||
return asPath === item.url;
|
||||
}
|
||||
|
||||
return asPath.startsWith(item.path);
|
||||
}, [asPath, item]);
|
||||
|
||||
const [open, setOpen] = useState(selected);
|
||||
|
||||
const handleOnClick = useCallback(
|
||||
(link: MenuItem | MenuLink) => (event: MouseEvent) => {
|
||||
if (isMenuLinkGroup(link)) {
|
||||
event.stopPropagation();
|
||||
setOpen(!open);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[open],
|
||||
);
|
||||
|
||||
const url = useMemo(() => {
|
||||
if (isMenuLinkGroup(item)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return item.url;
|
||||
}, [item]);
|
||||
|
||||
const wrappedLink = useMemo(() => {
|
||||
const button = (
|
||||
<ListItemButton
|
||||
key={`drawer-nav-item-${item.title}`}
|
||||
onClick={handleOnClick(item)}
|
||||
selected={selected}
|
||||
>
|
||||
<ListItemText primary={item.title} />
|
||||
{isMenuLinkGroup(item) ? (
|
||||
<ExpandLessIcon
|
||||
sx={{
|
||||
transform: `rotateZ(${open ? 0 : 90}deg)`,
|
||||
transition: theme.transitions.create(['transform']),
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</ListItemButton>
|
||||
);
|
||||
|
||||
if (!url) {
|
||||
return button;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link target={url?.startsWith('http') ? '_blank' : undefined} href={url}>
|
||||
{button}
|
||||
</Link>
|
||||
);
|
||||
}, [handleOnClick, item, open, selected, theme.transitions, url]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{wrappedLink}
|
||||
{isMenuLinkGroup(item) ? (
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
{item.groups.map(group => (
|
||||
<List
|
||||
key={group.title}
|
||||
component="div"
|
||||
subheader={
|
||||
<ListSubheader
|
||||
component="div"
|
||||
id="nested-list-subheader"
|
||||
sx={{
|
||||
lineHeight: '32px',
|
||||
textTransform: 'uppercase',
|
||||
top: '-1px',
|
||||
}}
|
||||
>
|
||||
{group.title}
|
||||
</ListSubheader>
|
||||
}
|
||||
disablePadding
|
||||
sx={{
|
||||
marginTop: '8px',
|
||||
'&:not(:first-child)': {
|
||||
marginTop: '20px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{group.links.map(link => (
|
||||
<MobileNavLink
|
||||
key={`drawer-nav-item-${item.title}-sub-item-${link.title}`}
|
||||
link={link}
|
||||
onClick={handleOnClick(link)}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
))}
|
||||
</Collapse>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileNavItem;
|
@ -0,0 +1,36 @@
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import type { MouseEvent } from 'react';
|
||||
import type { MenuLink } from '../../../interface';
|
||||
|
||||
interface MobileNavLinkProps {
|
||||
link: MenuLink;
|
||||
onClick: (event: MouseEvent) => void;
|
||||
}
|
||||
|
||||
const MobileNavLink = ({ link, onClick }: MobileNavLinkProps) => {
|
||||
const { title, url } = link;
|
||||
const { asPath } = useRouter();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
return asPath === url;
|
||||
}, [asPath, url]);
|
||||
|
||||
return (
|
||||
<Link href={url} target={url.startsWith('http') ? '_blank' : undefined}>
|
||||
<ListItemButton
|
||||
sx={{ paddingLeft: '24px', paddingTop: '4px', paddingBottom: '4px' }}
|
||||
onClick={onClick}
|
||||
selected={selected}
|
||||
>
|
||||
<ListItemText primary={title} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileNavLink;
|
@ -0,0 +1,99 @@
|
||||
import Divider from '@mui/material/Divider';
|
||||
import List from '@mui/material/List';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import SwipeableDrawer from '@mui/material/SwipeableDrawer';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import MobileNavItem from './MobileNavItem';
|
||||
import Logo from '../Logo';
|
||||
|
||||
import type { MenuItem } from '../../../interface';
|
||||
|
||||
const DRAWER_WIDTH = 300;
|
||||
|
||||
const StyledDrawerContents = styled('div')`
|
||||
padding-top: 16px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const StyledLogoWrapper = styled('div')`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
interface NavigationDrawerProps {
|
||||
items: MenuItem[];
|
||||
mobileOpen: boolean;
|
||||
onMobileOpenToggle: () => void;
|
||||
}
|
||||
|
||||
const NavigationDrawer = ({ items, mobileOpen, onMobileOpenToggle }: NavigationDrawerProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const iOS = useMemo(
|
||||
() => typeof navigator !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent),
|
||||
[],
|
||||
);
|
||||
|
||||
const drawer = useMemo(
|
||||
() => (
|
||||
<StyledDrawerContents key="drawer-nav-contents" onClick={onMobileOpenToggle}>
|
||||
<StyledLogoWrapper key="drawer-nav-logo-wrapper">
|
||||
<Logo key="drawer-nav-logo" inDrawer />
|
||||
</StyledLogoWrapper>
|
||||
<Divider key="drawer-nav-divider" sx={{ borderColor: 'rgba(255, 255, 255, 0.8)', pt: 2 }} />
|
||||
<List key="drawer-nav-list">
|
||||
{items.map(item => (
|
||||
<MobileNavItem key={`drawer-nav-item-${item.title}`} item={item} />
|
||||
))}
|
||||
</List>
|
||||
</StyledDrawerContents>
|
||||
),
|
||||
[items, onMobileOpenToggle],
|
||||
);
|
||||
|
||||
const container = useMemo(
|
||||
() => (typeof window !== 'undefined' ? window.document.body : undefined),
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<SwipeableDrawer
|
||||
key="swipable-drawer"
|
||||
disableBackdropTransition={!iOS}
|
||||
disableDiscovery={iOS}
|
||||
container={container}
|
||||
variant="temporary"
|
||||
open={mobileOpen}
|
||||
onOpen={onMobileOpenToggle}
|
||||
onClose={onMobileOpenToggle}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile.
|
||||
}}
|
||||
sx={{
|
||||
display: 'none',
|
||||
[theme.breakpoints.down('lg')]: {
|
||||
display: 'block',
|
||||
},
|
||||
width: '80%',
|
||||
maxWidth: DRAWER_WIDTH,
|
||||
'& .MuiBackdrop-root': {
|
||||
width: '100%',
|
||||
},
|
||||
'& .MuiDrawer-paper': {
|
||||
boxSizing: 'border-box',
|
||||
width: '80%',
|
||||
maxWidth: DRAWER_WIDTH,
|
||||
background: '#2e3034',
|
||||
},
|
||||
'& .MuiListSubheader-root': {
|
||||
textAlign: 'left',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{drawer}
|
||||
</SwipeableDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavigationDrawer;
|
@ -119,3 +119,21 @@ export interface CommunityData {
|
||||
readonly subtitle: string;
|
||||
readonly sections: CommunityLinksSection[];
|
||||
}
|
||||
|
||||
export interface MenuLink {
|
||||
readonly title: string;
|
||||
readonly url: string;
|
||||
}
|
||||
|
||||
export interface MenuLinkSubGroup {
|
||||
readonly title: string;
|
||||
readonly links: MenuLink[];
|
||||
}
|
||||
|
||||
export interface MenuLinkGroup {
|
||||
readonly title: string;
|
||||
readonly path: string;
|
||||
readonly groups: MenuLinkSubGroup[];
|
||||
}
|
||||
|
||||
export type MenuItem = MenuLinkGroup | MenuLink;
|
||||
|
@ -6,7 +6,12 @@ import path from 'path';
|
||||
import { SUMMARY_MIN_PARAGRAPH_LENGTH } from '../constants';
|
||||
import menu from './menu';
|
||||
|
||||
import type { FileMatter, DocsPage, DocsData, DocsGroup, DocsGroupLink } from '../interface';
|
||||
import type { GetStaticProps } from 'next';
|
||||
import type { DocsData, DocsGroup, DocsGroupLink, DocsPage, FileMatter } from '../interface';
|
||||
|
||||
export interface DocsMenuProps {
|
||||
docsGroups: DocsGroup[];
|
||||
}
|
||||
|
||||
const docsDirectory = path.join(process.cwd(), 'content/docs');
|
||||
|
||||
@ -97,3 +102,11 @@ export function fetchDocsContent(): [DocsPage[], DocsGroup[]] {
|
||||
|
||||
return docsCache;
|
||||
}
|
||||
|
||||
export const getDocsMenuStaticProps: GetStaticProps = (): { props: DocsMenuProps } => {
|
||||
return {
|
||||
props: {
|
||||
docsGroups: fetchDocsContent()[1],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -5,23 +5,44 @@ import CommunitySection from '../components/community/CommunitySection';
|
||||
import Container from '../components/layout/Container';
|
||||
import Page from '../components/layout/Page';
|
||||
import communityData from '../lib/community';
|
||||
import { getDocsMenuStaticProps } from '../lib/docs';
|
||||
|
||||
const StyledCommunityContent = styled('div')`
|
||||
width: 100%;
|
||||
padding-top: 72px;
|
||||
min-height: calc(100vh - 72px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 80px;
|
||||
`;
|
||||
import type { DocsMenuProps } from '../lib/docs';
|
||||
|
||||
const StyledTitle = styled('div')`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
`;
|
||||
const StyledCommunityContent = styled('div')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
padding-top: 72px;
|
||||
min-height: calc(100vh - 72px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 80px;
|
||||
|
||||
${theme.breakpoints.between('md', 'lg')} {
|
||||
padding-top: 48px;
|
||||
gap: 56px
|
||||
}
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
padding-top: 32px;
|
||||
gap: 40px
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledTitle = styled('div')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
|
||||
${theme.breakpoints.down('lg')} {
|
||||
gap: 8px
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledCommunityLinks = styled('section')(
|
||||
({ theme }) => `
|
||||
@ -32,6 +53,14 @@ const StyledCommunityLinks = styled('section')(
|
||||
background: ${theme.palette.mode === 'light' ? '#dddee2' : '#242424'};
|
||||
padding: 64px 0 32px;
|
||||
flex-grow: 1;
|
||||
|
||||
${theme.breakpoints.between('md', 'lg')} {
|
||||
padding: 48px 0 32px;
|
||||
}
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
padding: 40px 0 32px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
@ -43,16 +72,16 @@ const StyledCommunityLinksContent = styled('div')`
|
||||
gap: 40px;
|
||||
`;
|
||||
|
||||
const Community = () => {
|
||||
const Community = ({ docsGroups }: DocsMenuProps) => {
|
||||
return (
|
||||
<Page url="/community" fullWidth>
|
||||
<Page url="/community" docsGroups={docsGroups} fullWidth>
|
||||
<StyledCommunityContent>
|
||||
<Container>
|
||||
<StyledTitle>
|
||||
<Typography variant="h1" color="secondary">
|
||||
{communityData.title}
|
||||
</Typography>
|
||||
<Typography variant="h5" component="h2" color="text.primary">
|
||||
<Typography variant="h2" color="text.primary">
|
||||
{communityData.subtitle}
|
||||
</Typography>
|
||||
</StyledTitle>
|
||||
@ -72,3 +101,5 @@ const Community = () => {
|
||||
};
|
||||
|
||||
export default Community;
|
||||
|
||||
export const getStaticProps = getDocsMenuStaticProps;
|
||||
|
@ -26,19 +26,40 @@ const StyledDocsView = styled('div')(
|
||||
padding-top: 16px;
|
||||
|
||||
${theme.breakpoints.down('lg')} {
|
||||
grid-template-columns: unset
|
||||
margin-left: 0;
|
||||
padding-top: 24px;
|
||||
overflow-x: hidden;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledDocsContentWrapper = styled('main')`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
const StyledDocsContentWrapper = styled('main')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
margin-bottom: 40px;
|
||||
|
||||
${theme.breakpoints.between('md', 'lg')} {
|
||||
width: calc(100vw - 250px);
|
||||
}
|
||||
|
||||
${theme.breakpoints.down('lg')} {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
width: 100vw;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
interface DocsProps {
|
||||
docsGroups: DocsGroup[];
|
||||
@ -52,7 +73,13 @@ const Docs = ({ docsGroups, title, slug, description = '', source }: DocsProps)
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Page title={title} url={`/docs/${slug}`} description={description} fullWidth>
|
||||
<Page
|
||||
title={title}
|
||||
url={`/docs/${slug}`}
|
||||
description={description}
|
||||
docsGroups={docsGroups}
|
||||
fullWidth
|
||||
>
|
||||
<DocsLeftNav docsGroups={docsGroups} />
|
||||
<StyledDocsView className={theme.palette.mode}>
|
||||
<StyledDocsContentWrapper>
|
||||
|
@ -3,29 +3,36 @@ import Card from '@mui/material/Card';
|
||||
import CardActionArea from '@mui/material/CardActionArea';
|
||||
import CardContent from '@mui/material/CardContent';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import format from 'date-fns/format';
|
||||
import parseISO from 'date-fns/parseISO';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import Container from '../components/layout/Container';
|
||||
import Page from '../components/layout/Page';
|
||||
import config from '../lib/config';
|
||||
import { getDocsMenuStaticProps } from '../lib/docs';
|
||||
import homepageData from '../lib/homepage';
|
||||
import releases from '../lib/releases';
|
||||
|
||||
import type { NextPage } from 'next';
|
||||
import type { DocsMenuProps } from '../lib/docs';
|
||||
|
||||
const StyledHomagePageContent = styled('div')`
|
||||
width: 100%;
|
||||
padding-top: 72px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 88px;
|
||||
align-items: center;
|
||||
`;
|
||||
const StyledHomagePageContent = styled('div')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
padding-top: 72px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 88px;
|
||||
align-items: center;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
padding-top: 32px;
|
||||
gap: 0;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledIntroSection = styled('section')`
|
||||
width: 100%;
|
||||
@ -42,19 +49,32 @@ const StyledIntroSectionContent = styled('div')`
|
||||
align-items: flex-start;
|
||||
`;
|
||||
|
||||
const StyledOverviewSection = styled('section')`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
`;
|
||||
const StyledOverviewSection = styled('section')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
const StyledOverviewSectionContent = styled('div')`
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 64px;
|
||||
`;
|
||||
${theme.breakpoints.down('md')} {
|
||||
margin-top: 64px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledOverviewSectionContent = styled('div')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 64px;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 24px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledOverviewList = styled('div')`
|
||||
display: flex;
|
||||
@ -73,27 +93,61 @@ const StyledImageWrapper = styled('div')`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const StyledCallToActionSection = styled('section')`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
z-index: 1;
|
||||
`;
|
||||
const StyledCallToActionSection = styled('section')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
z-index: 1;
|
||||
|
||||
const StyledCallToActionCard = styled(Card)`
|
||||
width: 80%;
|
||||
`;
|
||||
${theme.breakpoints.down('md')} {
|
||||
height: auto;
|
||||
margin-top: 64px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledCallToActionCardContent = styled(CardContent)`
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 24px 40px;
|
||||
line-height: 30px;
|
||||
gap: 24px;
|
||||
`;
|
||||
const StyledCallToActionContainer = styled('div')(
|
||||
({ theme }) => `
|
||||
max-width: 1280px;
|
||||
width: 100%;
|
||||
padding: 0 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
padding: 0;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledCallToActionCard = styled(Card)(
|
||||
({ theme }) => `
|
||||
width: 80%;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledCallToActionCardContent = styled(CardContent)(
|
||||
({ theme }) => `
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 24px 40px;
|
||||
line-height: 30px;
|
||||
gap: 24px;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
flex-direction: column;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledCallToActionText = styled('div')`
|
||||
flex-grow: 1;
|
||||
@ -107,15 +161,25 @@ const StyledReleasesSection = styled('section')(
|
||||
align-items: center;
|
||||
background: ${theme.palette.mode === 'light' ? '#dddee2' : '#242424'};
|
||||
padding: 64px 0;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
padding: 48px 0;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledReleasesSectionContent = styled('div')`
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 48px;
|
||||
`;
|
||||
const StyledReleasesSectionContent = styled('div')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 48px;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledReleaseCardContent = styled(CardContent)`
|
||||
width: 100%;
|
||||
@ -124,29 +188,48 @@ const StyledReleaseCardContent = styled(CardContent)`
|
||||
gap: 8px;
|
||||
`;
|
||||
|
||||
const StyledFeaturesSection = styled('section')`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 80px;
|
||||
`;
|
||||
const StyledFeaturesSection = styled('section')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 80px;
|
||||
|
||||
const StyledFeaturesSectionContent = styled('div')`
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 48px;
|
||||
`;
|
||||
${theme.breakpoints.down('md')} {
|
||||
height: auto;
|
||||
margin-top: 48px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledFeaturesSectionIntro = styled('div')`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 32px 0 104px;
|
||||
`;
|
||||
const StyledFeaturesSectionIntro = styled('div')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 32px 0 104px;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
padding: 32px 0 48px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledFeaturesSectionContent = styled('div')(
|
||||
({ theme }) => `
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 48px;
|
||||
|
||||
${theme.breakpoints.down('md')} {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const StyledFeature = styled('div')`
|
||||
width: 100%;
|
||||
@ -164,9 +247,11 @@ const StyledFeatureText = styled('div')`
|
||||
padding: 0 16px;
|
||||
`;
|
||||
|
||||
const Home: NextPage = () => {
|
||||
const Home = ({ docsGroups }: DocsMenuProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Page url="/" fullWidth>
|
||||
<Page url="/" docsGroups={docsGroups} fullWidth>
|
||||
<StyledHomagePageContent>
|
||||
<StyledIntroSection>
|
||||
<Container>
|
||||
@ -174,7 +259,7 @@ const Home: NextPage = () => {
|
||||
<Typography variant="h1" color="secondary">
|
||||
{homepageData.title}
|
||||
</Typography>
|
||||
<Typography variant="h5" component="h2" color="text.primary">
|
||||
<Typography variant="h2" color="text.primary">
|
||||
{homepageData.subtitle}
|
||||
</Typography>
|
||||
<Link href={homepageData.get_started.url}>
|
||||
@ -191,7 +276,7 @@ const Home: NextPage = () => {
|
||||
<StyledOverviewList>
|
||||
{homepageData.overviews.map(overview => (
|
||||
<StyledOverview key={overview.title}>
|
||||
<Typography variant="h6" component="h3" color="text.primary">
|
||||
<Typography variant="h3" color="text.primary">
|
||||
{overview.title}
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
@ -201,13 +286,14 @@ const Home: NextPage = () => {
|
||||
))}
|
||||
</StyledOverviewList>
|
||||
<StyledImageWrapper>
|
||||
<Image layout="fill" src="/img/screenshot-editor.webp" />
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/img/screenshot-editor.webp" />
|
||||
</StyledImageWrapper>
|
||||
</StyledOverviewSectionContent>
|
||||
</Container>
|
||||
</StyledOverviewSection>
|
||||
<StyledCallToActionSection>
|
||||
<Container>
|
||||
<StyledCallToActionContainer>
|
||||
<StyledCallToActionCard raised>
|
||||
<StyledCallToActionCardContent>
|
||||
<StyledCallToActionText>
|
||||
@ -231,7 +317,7 @@ const Home: NextPage = () => {
|
||||
</Link>
|
||||
</StyledCallToActionCardContent>
|
||||
</StyledCallToActionCard>
|
||||
</Container>
|
||||
</StyledCallToActionContainer>
|
||||
</StyledCallToActionSection>
|
||||
<StyledReleasesSection>
|
||||
<Container>
|
||||
@ -272,10 +358,16 @@ const Home: NextPage = () => {
|
||||
<Container>
|
||||
<StyledFeaturesSectionIntro>
|
||||
<Typography
|
||||
variant="h4"
|
||||
component="h3"
|
||||
variant="h2"
|
||||
color="text.primary"
|
||||
sx={{ display: 'flex', alignItems: 'center', gap: '8px' }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{homepageData.features_intro.title}
|
||||
</Typography>
|
||||
@ -283,11 +375,15 @@ const Home: NextPage = () => {
|
||||
variant="subtitle1"
|
||||
component="div"
|
||||
color="text.secondary"
|
||||
sx={{ textAlign: 'center' }}
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
textAlign: 'center',
|
||||
marginTop: '24px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{homepageData.features_intro.subtitle1}
|
||||
<br />
|
||||
{homepageData.features_intro.subtitle2}
|
||||
{homepageData.features_intro.subtitle1} {homepageData.features_intro.subtitle2}
|
||||
</Typography>
|
||||
</StyledFeaturesSectionIntro>
|
||||
<StyledFeaturesSectionContent>
|
||||
@ -318,3 +414,5 @@ const Home: NextPage = () => {
|
||||
};
|
||||
|
||||
export default Home;
|
||||
|
||||
export const getStaticProps = getDocsMenuStaticProps;
|
||||
|
@ -2,71 +2,93 @@ import darkScrollbar from '@mui/material/darkScrollbar';
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import type { PaletteMode, ThemeOptions } from '@mui/material';
|
||||
|
||||
const commonThemeProps: ThemeOptions = {};
|
||||
import type { Components, PaletteMode, PaletteOptions, Theme } from '@mui/material';
|
||||
|
||||
const useCreateTheme = (mode: PaletteMode) => {
|
||||
return useMemo(
|
||||
const theme = useMemo(() => createTheme(), []);
|
||||
|
||||
const palette: PaletteOptions = useMemo(
|
||||
() =>
|
||||
mode === 'light'
|
||||
? createTheme({
|
||||
...commonThemeProps,
|
||||
palette: {
|
||||
mode,
|
||||
primary: {
|
||||
main: '#3764be',
|
||||
},
|
||||
secondary: {
|
||||
main: '#3764be',
|
||||
},
|
||||
background: {
|
||||
default: '#f9f9f9',
|
||||
paper: '#f9f9f9',
|
||||
},
|
||||
? {
|
||||
mode,
|
||||
primary: {
|
||||
main: '#3764be',
|
||||
},
|
||||
typography: {
|
||||
fontFamily:
|
||||
"'Roboto', -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif",
|
||||
h1: {
|
||||
fontSize: '42px',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
secondary: {
|
||||
main: '#3764be',
|
||||
},
|
||||
})
|
||||
: createTheme({
|
||||
...commonThemeProps,
|
||||
components: {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: {
|
||||
body: darkScrollbar(),
|
||||
},
|
||||
},
|
||||
background: {
|
||||
default: '#f9f9f9',
|
||||
paper: '#f9f9f9',
|
||||
},
|
||||
palette: {
|
||||
mode,
|
||||
primary: {
|
||||
main: '#3A69C7',
|
||||
},
|
||||
secondary: {
|
||||
main: '#5ecffb',
|
||||
},
|
||||
background: {
|
||||
default: '#2e3034',
|
||||
paper: '#2e3034',
|
||||
},
|
||||
}
|
||||
: {
|
||||
mode,
|
||||
primary: {
|
||||
main: '#3A69C7',
|
||||
},
|
||||
typography: {
|
||||
fontFamily:
|
||||
"'Roboto', -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif",
|
||||
h1: {
|
||||
fontSize: '42px',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
secondary: {
|
||||
main: '#5ecffb',
|
||||
},
|
||||
}),
|
||||
background: {
|
||||
default: '#2e3034',
|
||||
paper: '#2e3034',
|
||||
},
|
||||
},
|
||||
[mode],
|
||||
);
|
||||
|
||||
const components: Components<Omit<Theme, 'components'>> | undefined = useMemo(
|
||||
() =>
|
||||
mode === 'light'
|
||||
? {}
|
||||
: {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: {
|
||||
body: darkScrollbar(),
|
||||
},
|
||||
},
|
||||
},
|
||||
[mode],
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
palette,
|
||||
typography: {
|
||||
fontFamily: "'Roboto', -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif",
|
||||
h1: {
|
||||
fontSize: '42px',
|
||||
fontWeight: 'bold',
|
||||
lineHeight: 1.3,
|
||||
marginBottom: '16px',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
fontSize: '30px',
|
||||
},
|
||||
},
|
||||
h2: {
|
||||
fontSize: '24px',
|
||||
lineHeight: 1.3,
|
||||
position: 'relative',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
fontSize: '20px',
|
||||
},
|
||||
},
|
||||
h3: {
|
||||
fontSize: '20px',
|
||||
lineHeight: 1.3,
|
||||
position: 'relative',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
fontSize: '18px',
|
||||
},
|
||||
},
|
||||
},
|
||||
components,
|
||||
}),
|
||||
[components, palette, theme.breakpoints],
|
||||
);
|
||||
};
|
||||
|
||||
export default useCreateTheme;
|
||||
|
Loading…
x
Reference in New Issue
Block a user