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:
|
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.
|
- **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.
|
- **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).
|
- **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.
|
- **Modern authentication:** Using GitHub, GitLab, or Bitbucket and JSON web tokens.
|
||||||
* **Flexible content types:** Specify an unlimited number of content types with custom fields.
|
- **Flexible content types:** Specify an unlimited number of content types with custom fields.
|
||||||
* **Fully extensible:** Create custom-styled previews, UI widgets, and editor plugins.
|
- **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.)
|
- 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.
|
- [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:
|
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.
|
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.
|
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/).
|
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.
|
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.
|
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.
|
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.
|
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.
|
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).
|
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) => {
|
const CommunitySection = ({ section }: CommunitySectionProps) => {
|
||||||
return (
|
return (
|
||||||
<StyledCommunitySection>
|
<StyledCommunitySection>
|
||||||
<Typography variant="h5" component="h3">
|
<Typography variant="h3">
|
||||||
{section.title}
|
{section.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
<List sx={{ width: '100%', bgcolor: 'background.paper' }}>
|
<List sx={{ width: '100%', bgcolor: 'background.paper' }}>
|
||||||
|
@ -10,6 +10,14 @@ const DocsContent = styled('div')(
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
${theme.breakpoints.between('sm', 'lg')} {
|
||||||
|
padding: 0 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
${theme.breakpoints.down('sm')} {
|
||||||
|
padding: 0 32px;
|
||||||
|
}
|
||||||
|
|
||||||
& time {
|
& time {
|
||||||
color: #9b9b9b;
|
color: #9b9b9b;
|
||||||
}
|
}
|
||||||
@ -54,7 +62,7 @@ const DocsContent = styled('div')(
|
|||||||
color: ${theme.palette.text.primary};
|
color: ${theme.palette.text.primary};
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
${theme.breakpoints.up('lg')} {
|
||||||
& h1 {
|
& h1 {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
@ -66,13 +74,13 @@ const DocsContent = styled('div')(
|
|||||||
line-height: 36px;
|
line-height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 600px) and (max-width: 1200px) {
|
${theme.breakpoints.between('sm', 'lg')} {
|
||||||
& h1 {
|
& h1 {
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
${theme.breakpoints.down('sm')} {
|
||||||
& h1 {
|
& h1 {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
@ -84,13 +92,13 @@ const DocsContent = styled('div')(
|
|||||||
line-height: 26px;
|
line-height: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 600px) and (max-width: 1200px) {
|
${theme.breakpoints.between('sm', 'lg')} {
|
||||||
& h2 {
|
& h2 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
${theme.breakpoints.down('sm')} {
|
||||||
& h2 {
|
& h2 {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
@ -102,13 +110,13 @@ const DocsContent = styled('div')(
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 600px) and (max-width: 1200px) {
|
${theme.breakpoints.between('sm', 'lg')} {
|
||||||
& h3 {
|
& h3 {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
${theme.breakpoints.down('sm')} {
|
||||||
& h3 {
|
& h3 {
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
}
|
}
|
||||||
@ -119,7 +127,7 @@ const DocsContent = styled('div')(
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
${theme.breakpoints.down('lg')} {
|
||||||
& h4 {
|
& h4 {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
@ -130,7 +138,7 @@ const DocsContent = styled('div')(
|
|||||||
line-height: 19px;
|
line-height: 19px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
${theme.breakpoints.down('lg')} {
|
||||||
& h5 {
|
& h5 {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
@ -141,7 +149,7 @@ const DocsContent = styled('div')(
|
|||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
${theme.breakpoints.down('lg')} {
|
||||||
& h6 {
|
& h6 {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
@ -295,7 +303,7 @@ const DocsContent = styled('div')(
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 800px) {
|
${theme.breakpoints.down('md')} {
|
||||||
& h2,
|
& h2,
|
||||||
& h3,
|
& h3,
|
||||||
& h4,
|
& h4,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import List from '@mui/material/List';
|
import List from '@mui/material/List';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
|
||||||
import DocsLeftNavGroup from './DocsLeftNavGroup';
|
import DocsLeftNavGroup from './DocsLeftNavGroup';
|
||||||
|
|
||||||
@ -9,6 +10,8 @@ export interface DocsLeftNavProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DocsLeftNav = ({ docsGroups }: DocsLeftNavProps) => {
|
const DocsLeftNav = ({ docsGroups }: DocsLeftNavProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List
|
<List
|
||||||
component="nav"
|
component="nav"
|
||||||
@ -23,6 +26,9 @@ const DocsLeftNav = ({ docsGroups }: DocsLeftNavProps) => {
|
|||||||
bottom: 0,
|
bottom: 0,
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
paddingBottom: '24px',
|
paddingBottom: '24px',
|
||||||
|
[theme.breakpoints.down('lg')]: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
dense
|
dense
|
||||||
>
|
>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import LinkIcon from '@mui/icons-material/Link';
|
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 Link from 'next/link';
|
||||||
|
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
@ -8,10 +9,14 @@ const StyledLink = styled('a')(
|
|||||||
({ theme }) => `
|
({ theme }) => `
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin-left: -28px;
|
margin-left: -28px;
|
||||||
top: 0;
|
top: -1px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
color: ${theme.palette.secondary.main};
|
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 Header2 = ({ children = '' }: Header2Props) => {
|
||||||
const anchor = getAnchor(String(children));
|
const anchor = getAnchor(String(children));
|
||||||
const link = `#${anchor}`;
|
const link = `#${anchor}`;
|
||||||
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<h2 id={anchor}>
|
<Typography
|
||||||
|
variant="h2"
|
||||||
|
id={anchor}
|
||||||
|
sx={{
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
marginLeft: '8px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Link href={link} className="anchor-link">
|
<Link href={link} className="anchor-link">
|
||||||
<StyledLink href={link}>
|
<StyledLink href={link}>
|
||||||
<LinkIcon fontSize="medium" />
|
<LinkIcon
|
||||||
|
fontSize="medium"
|
||||||
|
sx={{
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
fontSize: '20px',
|
||||||
|
height: '20px',
|
||||||
|
width: '20px',
|
||||||
|
marginTop: '2px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
</Link>
|
</Link>
|
||||||
{children}
|
{children}
|
||||||
</h2>
|
</Typography>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import LinkIcon from '@mui/icons-material/Link';
|
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 Link from 'next/link';
|
||||||
|
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
@ -11,7 +12,12 @@ const StyledLink = styled('a')(
|
|||||||
top: 0;
|
top: 0;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
color: ${theme.palette.text.primary};
|
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 Header3 = ({ children = '' }: Header3Props) => {
|
||||||
const anchor = getAnchor(String(children));
|
const anchor = getAnchor(String(children));
|
||||||
const link = `#${anchor}`;
|
const link = `#${anchor}`;
|
||||||
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<h3 id={anchor}>
|
<Typography
|
||||||
|
variant="h3"
|
||||||
|
id={anchor}
|
||||||
|
sx={{
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
marginLeft: '8px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Link href={link} className="anchor-link">
|
<Link href={link} className="anchor-link">
|
||||||
<StyledLink href={link}>
|
<StyledLink href={link}>
|
||||||
<LinkIcon fontSize="small" />
|
<LinkIcon
|
||||||
|
fontSize="small"
|
||||||
|
sx={{
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
fontSize: '20px',
|
||||||
|
height: '20px',
|
||||||
|
width: '20px',
|
||||||
|
marginTop: '2px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
</Link>
|
</Link>
|
||||||
{children}
|
{children}
|
||||||
</h3>
|
</Typography>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,12 +2,18 @@ import { styled } from '@mui/material/styles';
|
|||||||
|
|
||||||
import type { NestedHeading } from './DocsTableOfContents';
|
import type { NestedHeading } from './DocsTableOfContents';
|
||||||
|
|
||||||
const StyledList = styled('ul')`
|
const StyledList = styled('ul')(
|
||||||
display: flex;
|
({ theme }) => `
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
list-style-type: none;
|
flex-direction: column;
|
||||||
padding: 0;
|
list-style-type: none;
|
||||||
`;
|
padding: 0;
|
||||||
|
|
||||||
|
${theme.breakpoints.down('lg')} {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
const StyledListItem = styled('li')(
|
const StyledListItem = styled('li')(
|
||||||
({ theme }) => `
|
({ theme }) => `
|
||||||
|
@ -38,8 +38,8 @@ const useHeadingsData = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const headingElements = Array.from(
|
const headingElements = Array.from(
|
||||||
document.querySelectorAll('main h2, main h3'),
|
document.querySelectorAll<HTMLHeadingElement>('main h2, main h3'),
|
||||||
) as HTMLHeadingElement[];
|
);
|
||||||
|
|
||||||
// Created a list of headings, with H3s nested
|
// Created a list of headings, with H3s nested
|
||||||
const newNestedHeadings = getNestedHeadings(headingElements);
|
const newNestedHeadings = getNestedHeadings(headingElements);
|
||||||
@ -50,10 +50,10 @@ const useHeadingsData = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const useIntersectionObserver = (setActiveId: (activeId: string) => void) => {
|
const useIntersectionObserver = (setActiveId: (activeId: string) => void) => {
|
||||||
const headingElementsRef = useRef({});
|
const headingElementsRef = useRef<Record<string, IntersectionObserverEntry>>({});
|
||||||
const { asPath } = useRouter();
|
const { asPath } = useRouter();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const headingElements = Array.from(document.querySelectorAll('h2, h3'));
|
const headingElements = Array.from(document.querySelectorAll<HTMLHeadingElement>('h2, h3'));
|
||||||
|
|
||||||
if (headingElementsRef.current) {
|
if (headingElementsRef.current) {
|
||||||
headingElementsRef.current = {};
|
headingElementsRef.current = {};
|
||||||
@ -105,16 +105,26 @@ const useIntersectionObserver = (setActiveId: (activeId: string) => void) => {
|
|||||||
}, [setActiveId, asPath]);
|
}, [setActiveId, asPath]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledNav = styled('nav')`
|
const StyledNav = styled('nav')(
|
||||||
width: 100%;
|
({ theme }) => `
|
||||||
padding: 0 16px 16px 0;
|
width: 100%;
|
||||||
align-self: flex-start;
|
padding: 0 16px 16px 0;
|
||||||
position: sticky;
|
align-self: flex-start;
|
||||||
top: 0;
|
position: sticky;
|
||||||
max-height: calc(100vh - 72px);
|
top: 0;
|
||||||
overflow-y: auto;
|
max-height: calc(100vh - 72px);
|
||||||
top: 16px;
|
overflow-y: auto;
|
||||||
`;
|
top: 16px;
|
||||||
|
|
||||||
|
${theme.breakpoints.between('md', 'lg')} {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
${theme.breakpoints.down('md')} {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
const DocsTableOfContents = () => {
|
const DocsTableOfContents = () => {
|
||||||
const [activeId, setActiveId] = useState<string>();
|
const [activeId, setActiveId] = useState<string>();
|
||||||
|
@ -2,14 +2,20 @@ import { styled } from '@mui/material/styles';
|
|||||||
|
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
const StyledContainer = styled('div')`
|
const StyledContainer = styled('div')(
|
||||||
max-width: 1280px;
|
({ theme }) => `
|
||||||
width: 100%;
|
max-width: 1280px;
|
||||||
padding: 0 40px;
|
width: 100%;
|
||||||
display: flex;
|
padding: 0 40px;
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
`;
|
align-items: center;
|
||||||
|
|
||||||
|
${theme.breakpoints.down('md')} {
|
||||||
|
padding: 0 32px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
export interface PageProps {
|
export interface PageProps {
|
||||||
children: ReactNode;
|
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 MenuIcon from '@mui/icons-material/Menu';
|
||||||
import SearchIcon from '@mui/icons-material/Search';
|
|
||||||
import AppBar from '@mui/material/AppBar';
|
import AppBar from '@mui/material/AppBar';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import { styled } from '@mui/material/styles';
|
import { styled } from '@mui/material/styles';
|
||||||
import TextField from '@mui/material/TextField';
|
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import Image from 'next/image';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Brightness4Icon from '@mui/icons-material/Brightness4';
|
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||||
import Brightness7Icon from '@mui/icons-material/Brightness7';
|
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 { ButtonTypeMap } from '@mui/material/Button';
|
||||||
import type { ExtendButtonBase } from '@mui/material/ButtonBase';
|
import type { ExtendButtonBase } from '@mui/material/ButtonBase';
|
||||||
import type { PaletteMode } from '@mui/material';
|
import type { DocsGroup, MenuItem } from '../../interface';
|
||||||
|
|
||||||
const StyledAppBar = styled(AppBar)(
|
const StyledAppBar = styled(AppBar)(
|
||||||
({ theme }) => `
|
({ theme }) => `
|
||||||
@ -21,17 +25,47 @@ const StyledAppBar = styled(AppBar)(
|
|||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const StyledToolbar = styled(Toolbar)`
|
const StyledToolbar = styled(Toolbar)(
|
||||||
gap: 16px;
|
({ 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;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledMenuButton = styled(IconButton)(
|
const StyledMenuButton = styled(IconButton)(
|
||||||
({ theme }) => `
|
({ theme }) => `
|
||||||
${theme.breakpoints.up('md')} {
|
${theme.breakpoints.up('lg')} {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
height: 0;
|
height: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
@ -40,102 +74,136 @@ const StyledMenuButton = styled(IconButton)(
|
|||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const StyledGap = styled('div')`
|
const StyledDesktopGap = styled('div')(
|
||||||
flex-grow: 1;
|
({ theme }) => `
|
||||||
`;
|
flex-grow: 1;
|
||||||
|
|
||||||
const StyledSearchBox = styled(TextField)`
|
${theme.breakpoints.down('lg')} {
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
display: none;
|
||||||
border-radius: 4px;
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
.MuiSvgIcon-root {
|
const StyledDesktopLink = styled(Button)(
|
||||||
color: rgba(255, 255, 255, 0.8);
|
({ theme }) => `
|
||||||
}
|
|
||||||
|
|
||||||
.MuiInputBase-root {
|
|
||||||
color: white;
|
color: white;
|
||||||
}
|
|
||||||
|
|
||||||
.MuiOutlinedInput-notchedOutline {
|
&:hover {
|
||||||
border: none;
|
color: rgba(255, 255, 255, 0.6);
|
||||||
}
|
}
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledLink = styled(Button)`
|
${theme.breakpoints.down('lg')} {
|
||||||
color: white;
|
display: none;
|
||||||
|
}
|
||||||
&:hover {
|
`,
|
||||||
color: rgba(255, 255, 255, 0.6);
|
) as ExtendButtonBase<ButtonTypeMap<{}, 'a'>>;
|
||||||
}
|
|
||||||
` as ExtendButtonBase<ButtonTypeMap<{}, 'a'>>;
|
|
||||||
|
|
||||||
const StyledImageLink = styled('a')`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledImage = styled(Image)`
|
|
||||||
cursor: pointer;
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
mode: PaletteMode;
|
mode: PaletteMode;
|
||||||
|
docsGroups: DocsGroup[];
|
||||||
toggleColorMode: () => void;
|
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 (
|
return (
|
||||||
<StyledAppBar position="fixed">
|
<>
|
||||||
<StyledToolbar>
|
<StyledAppBar position="fixed">
|
||||||
<StyledMenuButton size="large" edge="start" color="inherit" aria-label="menu">
|
<StyledToolbar>
|
||||||
<MenuIcon fontSize="large" />
|
<StyledMenuButton
|
||||||
</StyledMenuButton>
|
size="large"
|
||||||
<Link href="/">
|
edge="start"
|
||||||
<StyledImageLink>
|
color="inherit"
|
||||||
<StyledImage src="/static-cms-logo.svg" width={182} height={72} />
|
aria-label="menu"
|
||||||
</StyledImageLink>
|
onClick={handleDrawerToggle}
|
||||||
</Link>
|
>
|
||||||
<StyledSearchBox
|
<MenuIcon fontSize="large" />
|
||||||
placeholder="Search the docs"
|
</StyledMenuButton>
|
||||||
variant="outlined"
|
<Logo />
|
||||||
size="small"
|
<Search />
|
||||||
InputProps={{
|
<StyledIconsWrapper>
|
||||||
startAdornment: <SearchIcon />,
|
<IconButton
|
||||||
}}
|
sx={{ ml: 1 }}
|
||||||
/>
|
onClick={toggleColorMode}
|
||||||
<IconButton
|
color="inherit"
|
||||||
sx={{ ml: 1 }}
|
title={mode === 'dark' ? 'Turn on the light' : 'Turn off the light'}
|
||||||
onClick={toggleColorMode}
|
>
|
||||||
color="inherit"
|
{mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
|
||||||
title={mode === 'dark' ? 'Turn on the light' : 'Turn off the light'}
|
</IconButton>
|
||||||
>
|
<StyledDesktopGap />
|
||||||
{mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
|
<StyledGithubLink
|
||||||
</IconButton>
|
href="https://github.com/StaticJsCMS/static-cms"
|
||||||
<StyledGap />
|
aria-label="Star StaticJsCMS/static-cms on GitHub"
|
||||||
<StyledGithubLink
|
>
|
||||||
href="https://github.com/StaticJsCMS/static-cms"
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
aria-label="Star StaticJsCMS/static-cms on GitHub"
|
<StyledGithubImage
|
||||||
>
|
alt="Star StaticJsCMS/static-cms on GitHub"
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
src="https://img.shields.io/github/stars/StaticJsCMS/static-cms?style=social"
|
||||||
<img
|
/>
|
||||||
alt="Star StaticJsCMS/static-cms on GitHub"
|
</StyledGithubLink>
|
||||||
src="https://img.shields.io/github/stars/StaticJsCMS/static-cms?style=social"
|
<IconButton
|
||||||
/>
|
href="https://github.com/StaticJsCMS/static-cms"
|
||||||
</StyledGithubLink>
|
color="inherit"
|
||||||
<Link href="/docs/intro">
|
>
|
||||||
<StyledLink component="a">Docs</StyledLink>
|
<GitHubIcon />
|
||||||
</Link>
|
</IconButton>
|
||||||
<Link href="/docs/contributor-guide">
|
</StyledIconsWrapper>
|
||||||
<StyledLink component="a">Contributing</StyledLink>
|
{items.map(item => {
|
||||||
</Link>
|
let url = '#';
|
||||||
<Link href="/community">
|
if ('url' in item) {
|
||||||
<StyledLink component="a">Community</StyledLink>
|
url = item.url;
|
||||||
</Link>
|
} else if (item.groups.length > 0 && item.groups[0].links.length > 0) {
|
||||||
{/* <Link href="/blog">
|
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>
|
<StyledLink component="a">Blog</StyledLink>
|
||||||
</Link> */}
|
</Link> */}
|
||||||
</StyledToolbar>
|
</StyledToolbar>
|
||||||
</StyledAppBar>
|
</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 Header from './Header';
|
||||||
|
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
import type { DocsGroup } from '../../interface';
|
||||||
|
|
||||||
const StyledPageContentWrapper = styled('div')`
|
const StyledPageContentWrapper = styled('div')`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -35,6 +36,7 @@ export interface PageProps {
|
|||||||
image?: string;
|
image?: string;
|
||||||
};
|
};
|
||||||
fullWidth?: boolean;
|
fullWidth?: boolean;
|
||||||
|
docsGroups: DocsGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Page = ({
|
const Page = ({
|
||||||
@ -45,6 +47,7 @@ const Page = ({
|
|||||||
description,
|
description,
|
||||||
pageDetails,
|
pageDetails,
|
||||||
fullWidth = false,
|
fullWidth = false,
|
||||||
|
docsGroups,
|
||||||
}: PageProps) => {
|
}: PageProps) => {
|
||||||
const scrollableArea = useRef<HTMLDivElement | null>(null);
|
const scrollableArea = useRef<HTMLDivElement | null>(null);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -89,7 +92,11 @@ const Page = ({
|
|||||||
description={description}
|
description={description}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<Header mode={theme.palette.mode} toggleColorMode={colorMode.toggleColorMode} />
|
<Header
|
||||||
|
mode={theme.palette.mode}
|
||||||
|
docsGroups={docsGroups}
|
||||||
|
toggleColorMode={colorMode.toggleColorMode}
|
||||||
|
/>
|
||||||
<StyledPageContentWrapper ref={scrollableArea}>{content}</StyledPageContentWrapper>
|
<StyledPageContentWrapper ref={scrollableArea}>{content}</StyledPageContentWrapper>
|
||||||
</ThemeProvider>
|
</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 subtitle: string;
|
||||||
readonly sections: CommunityLinksSection[];
|
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 { SUMMARY_MIN_PARAGRAPH_LENGTH } from '../constants';
|
||||||
import menu from './menu';
|
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');
|
const docsDirectory = path.join(process.cwd(), 'content/docs');
|
||||||
|
|
||||||
@ -97,3 +102,11 @@ export function fetchDocsContent(): [DocsPage[], DocsGroup[]] {
|
|||||||
|
|
||||||
return docsCache;
|
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 Container from '../components/layout/Container';
|
||||||
import Page from '../components/layout/Page';
|
import Page from '../components/layout/Page';
|
||||||
import communityData from '../lib/community';
|
import communityData from '../lib/community';
|
||||||
|
import { getDocsMenuStaticProps } from '../lib/docs';
|
||||||
|
|
||||||
const StyledCommunityContent = styled('div')`
|
import type { DocsMenuProps } from '../lib/docs';
|
||||||
width: 100%;
|
|
||||||
padding-top: 72px;
|
|
||||||
min-height: calc(100vh - 72px);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 80px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledTitle = styled('div')`
|
const StyledCommunityContent = styled('div')(
|
||||||
width: 100%;
|
({ theme }) => `
|
||||||
display: flex;
|
width: 100%;
|
||||||
flex-direction: column;
|
padding-top: 72px;
|
||||||
align-items: flex-start;
|
min-height: calc(100vh - 72px);
|
||||||
gap: 16px;
|
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')(
|
const StyledCommunityLinks = styled('section')(
|
||||||
({ theme }) => `
|
({ theme }) => `
|
||||||
@ -32,6 +53,14 @@ const StyledCommunityLinks = styled('section')(
|
|||||||
background: ${theme.palette.mode === 'light' ? '#dddee2' : '#242424'};
|
background: ${theme.palette.mode === 'light' ? '#dddee2' : '#242424'};
|
||||||
padding: 64px 0 32px;
|
padding: 64px 0 32px;
|
||||||
flex-grow: 1;
|
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;
|
gap: 40px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Community = () => {
|
const Community = ({ docsGroups }: DocsMenuProps) => {
|
||||||
return (
|
return (
|
||||||
<Page url="/community" fullWidth>
|
<Page url="/community" docsGroups={docsGroups} fullWidth>
|
||||||
<StyledCommunityContent>
|
<StyledCommunityContent>
|
||||||
<Container>
|
<Container>
|
||||||
<StyledTitle>
|
<StyledTitle>
|
||||||
<Typography variant="h1" color="secondary">
|
<Typography variant="h1" color="secondary">
|
||||||
{communityData.title}
|
{communityData.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h5" component="h2" color="text.primary">
|
<Typography variant="h2" color="text.primary">
|
||||||
{communityData.subtitle}
|
{communityData.subtitle}
|
||||||
</Typography>
|
</Typography>
|
||||||
</StyledTitle>
|
</StyledTitle>
|
||||||
@ -72,3 +101,5 @@ const Community = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default Community;
|
export default Community;
|
||||||
|
|
||||||
|
export const getStaticProps = getDocsMenuStaticProps;
|
||||||
|
@ -26,19 +26,40 @@ const StyledDocsView = styled('div')(
|
|||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
|
|
||||||
${theme.breakpoints.down('lg')} {
|
${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')`
|
const StyledDocsContentWrapper = styled('main')(
|
||||||
width: 100%;
|
({ theme }) => `
|
||||||
display: flex;
|
width: 100%;
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
margin: 0;
|
align-items: center;
|
||||||
margin-bottom: 40px;
|
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 {
|
interface DocsProps {
|
||||||
docsGroups: DocsGroup[];
|
docsGroups: DocsGroup[];
|
||||||
@ -52,7 +73,13 @@ const Docs = ({ docsGroups, title, slug, description = '', source }: DocsProps)
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page title={title} url={`/docs/${slug}`} description={description} fullWidth>
|
<Page
|
||||||
|
title={title}
|
||||||
|
url={`/docs/${slug}`}
|
||||||
|
description={description}
|
||||||
|
docsGroups={docsGroups}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
<DocsLeftNav docsGroups={docsGroups} />
|
<DocsLeftNav docsGroups={docsGroups} />
|
||||||
<StyledDocsView className={theme.palette.mode}>
|
<StyledDocsView className={theme.palette.mode}>
|
||||||
<StyledDocsContentWrapper>
|
<StyledDocsContentWrapper>
|
||||||
|
@ -3,29 +3,36 @@ import Card from '@mui/material/Card';
|
|||||||
import CardActionArea from '@mui/material/CardActionArea';
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
import CardContent from '@mui/material/CardContent';
|
import CardContent from '@mui/material/CardContent';
|
||||||
import Chip from '@mui/material/Chip';
|
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 Typography from '@mui/material/Typography';
|
||||||
import format from 'date-fns/format';
|
import format from 'date-fns/format';
|
||||||
import parseISO from 'date-fns/parseISO';
|
import parseISO from 'date-fns/parseISO';
|
||||||
import Image from 'next/image';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import Container from '../components/layout/Container';
|
import Container from '../components/layout/Container';
|
||||||
import Page from '../components/layout/Page';
|
import Page from '../components/layout/Page';
|
||||||
import config from '../lib/config';
|
import config from '../lib/config';
|
||||||
|
import { getDocsMenuStaticProps } from '../lib/docs';
|
||||||
import homepageData from '../lib/homepage';
|
import homepageData from '../lib/homepage';
|
||||||
import releases from '../lib/releases';
|
import releases from '../lib/releases';
|
||||||
|
|
||||||
import type { NextPage } from 'next';
|
import type { DocsMenuProps } from '../lib/docs';
|
||||||
|
|
||||||
const StyledHomagePageContent = styled('div')`
|
const StyledHomagePageContent = styled('div')(
|
||||||
width: 100%;
|
({ theme }) => `
|
||||||
padding-top: 72px;
|
width: 100%;
|
||||||
display: flex;
|
padding-top: 72px;
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
gap: 88px;
|
flex-direction: column;
|
||||||
align-items: center;
|
gap: 88px;
|
||||||
`;
|
align-items: center;
|
||||||
|
|
||||||
|
${theme.breakpoints.down('md')} {
|
||||||
|
padding-top: 32px;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
const StyledIntroSection = styled('section')`
|
const StyledIntroSection = styled('section')`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -42,19 +49,32 @@ const StyledIntroSectionContent = styled('div')`
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledOverviewSection = styled('section')`
|
const StyledOverviewSection = styled('section')(
|
||||||
width: 100%;
|
({ theme }) => `
|
||||||
display: flex;
|
width: 100%;
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
`;
|
align-items: center;
|
||||||
|
|
||||||
const StyledOverviewSectionContent = styled('div')`
|
${theme.breakpoints.down('md')} {
|
||||||
width: 100%;
|
margin-top: 64px;
|
||||||
display: grid;
|
}
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
`,
|
||||||
gap: 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')`
|
const StyledOverviewList = styled('div')`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -73,27 +93,61 @@ const StyledImageWrapper = styled('div')`
|
|||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledCallToActionSection = styled('section')`
|
const StyledCallToActionSection = styled('section')(
|
||||||
width: 100%;
|
({ theme }) => `
|
||||||
display: flex;
|
width: 100%;
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
height: 0;
|
align-items: center;
|
||||||
overflow: visible;
|
height: 0;
|
||||||
z-index: 1;
|
overflow: visible;
|
||||||
`;
|
z-index: 1;
|
||||||
|
|
||||||
const StyledCallToActionCard = styled(Card)`
|
${theme.breakpoints.down('md')} {
|
||||||
width: 80%;
|
height: auto;
|
||||||
`;
|
margin-top: 64px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
const StyledCallToActionCardContent = styled(CardContent)`
|
const StyledCallToActionContainer = styled('div')(
|
||||||
display: flex;
|
({ theme }) => `
|
||||||
align-items: flex-start;
|
max-width: 1280px;
|
||||||
padding: 24px 40px;
|
width: 100%;
|
||||||
line-height: 30px;
|
padding: 0 40px;
|
||||||
gap: 24px;
|
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')`
|
const StyledCallToActionText = styled('div')`
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@ -107,15 +161,25 @@ const StyledReleasesSection = styled('section')(
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
background: ${theme.palette.mode === 'light' ? '#dddee2' : '#242424'};
|
background: ${theme.palette.mode === 'light' ? '#dddee2' : '#242424'};
|
||||||
padding: 64px 0;
|
padding: 64px 0;
|
||||||
|
|
||||||
|
${theme.breakpoints.down('md')} {
|
||||||
|
padding: 48px 0;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const StyledReleasesSectionContent = styled('div')`
|
const StyledReleasesSectionContent = styled('div')(
|
||||||
width: 100%;
|
({ theme }) => `
|
||||||
display: grid;
|
width: 100%;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
display: grid;
|
||||||
gap: 48px;
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
`;
|
gap: 48px;
|
||||||
|
|
||||||
|
${theme.breakpoints.down('md')} {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
const StyledReleaseCardContent = styled(CardContent)`
|
const StyledReleaseCardContent = styled(CardContent)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -124,29 +188,48 @@ const StyledReleaseCardContent = styled(CardContent)`
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledFeaturesSection = styled('section')`
|
const StyledFeaturesSection = styled('section')(
|
||||||
width: 100%;
|
({ theme }) => `
|
||||||
display: flex;
|
width: 100%;
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
padding-bottom: 80px;
|
align-items: center;
|
||||||
`;
|
padding-bottom: 80px;
|
||||||
|
|
||||||
const StyledFeaturesSectionContent = styled('div')`
|
${theme.breakpoints.down('md')} {
|
||||||
width: 100%;
|
height: auto;
|
||||||
display: grid;
|
margin-top: 48px;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
}
|
||||||
gap: 48px;
|
`,
|
||||||
`;
|
);
|
||||||
|
|
||||||
const StyledFeaturesSectionIntro = styled('div')`
|
const StyledFeaturesSectionIntro = styled('div')(
|
||||||
width: 100%;
|
({ theme }) => `
|
||||||
display: flex;
|
width: 100%;
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
gap: 8px;
|
align-items: center;
|
||||||
padding: 32px 0 104px;
|
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')`
|
const StyledFeature = styled('div')`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -164,9 +247,11 @@ const StyledFeatureText = styled('div')`
|
|||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Home: NextPage = () => {
|
const Home = ({ docsGroups }: DocsMenuProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page url="/" fullWidth>
|
<Page url="/" docsGroups={docsGroups} fullWidth>
|
||||||
<StyledHomagePageContent>
|
<StyledHomagePageContent>
|
||||||
<StyledIntroSection>
|
<StyledIntroSection>
|
||||||
<Container>
|
<Container>
|
||||||
@ -174,7 +259,7 @@ const Home: NextPage = () => {
|
|||||||
<Typography variant="h1" color="secondary">
|
<Typography variant="h1" color="secondary">
|
||||||
{homepageData.title}
|
{homepageData.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h5" component="h2" color="text.primary">
|
<Typography variant="h2" color="text.primary">
|
||||||
{homepageData.subtitle}
|
{homepageData.subtitle}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Link href={homepageData.get_started.url}>
|
<Link href={homepageData.get_started.url}>
|
||||||
@ -191,7 +276,7 @@ const Home: NextPage = () => {
|
|||||||
<StyledOverviewList>
|
<StyledOverviewList>
|
||||||
{homepageData.overviews.map(overview => (
|
{homepageData.overviews.map(overview => (
|
||||||
<StyledOverview key={overview.title}>
|
<StyledOverview key={overview.title}>
|
||||||
<Typography variant="h6" component="h3" color="text.primary">
|
<Typography variant="h3" color="text.primary">
|
||||||
{overview.title}
|
{overview.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" color="text.secondary">
|
<Typography variant="body1" color="text.secondary">
|
||||||
@ -201,13 +286,14 @@ const Home: NextPage = () => {
|
|||||||
))}
|
))}
|
||||||
</StyledOverviewList>
|
</StyledOverviewList>
|
||||||
<StyledImageWrapper>
|
<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>
|
</StyledImageWrapper>
|
||||||
</StyledOverviewSectionContent>
|
</StyledOverviewSectionContent>
|
||||||
</Container>
|
</Container>
|
||||||
</StyledOverviewSection>
|
</StyledOverviewSection>
|
||||||
<StyledCallToActionSection>
|
<StyledCallToActionSection>
|
||||||
<Container>
|
<StyledCallToActionContainer>
|
||||||
<StyledCallToActionCard raised>
|
<StyledCallToActionCard raised>
|
||||||
<StyledCallToActionCardContent>
|
<StyledCallToActionCardContent>
|
||||||
<StyledCallToActionText>
|
<StyledCallToActionText>
|
||||||
@ -231,7 +317,7 @@ const Home: NextPage = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
</StyledCallToActionCardContent>
|
</StyledCallToActionCardContent>
|
||||||
</StyledCallToActionCard>
|
</StyledCallToActionCard>
|
||||||
</Container>
|
</StyledCallToActionContainer>
|
||||||
</StyledCallToActionSection>
|
</StyledCallToActionSection>
|
||||||
<StyledReleasesSection>
|
<StyledReleasesSection>
|
||||||
<Container>
|
<Container>
|
||||||
@ -272,10 +358,16 @@ const Home: NextPage = () => {
|
|||||||
<Container>
|
<Container>
|
||||||
<StyledFeaturesSectionIntro>
|
<StyledFeaturesSectionIntro>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h4"
|
variant="h2"
|
||||||
component="h3"
|
|
||||||
color="text.primary"
|
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}
|
{homepageData.features_intro.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -283,11 +375,15 @@ const Home: NextPage = () => {
|
|||||||
variant="subtitle1"
|
variant="subtitle1"
|
||||||
component="div"
|
component="div"
|
||||||
color="text.secondary"
|
color="text.secondary"
|
||||||
sx={{ textAlign: 'center' }}
|
sx={{
|
||||||
|
textAlign: 'center',
|
||||||
|
[theme.breakpoints.down('md')]: {
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: '24px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{homepageData.features_intro.subtitle1}
|
{homepageData.features_intro.subtitle1} {homepageData.features_intro.subtitle2}
|
||||||
<br />
|
|
||||||
{homepageData.features_intro.subtitle2}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
</StyledFeaturesSectionIntro>
|
</StyledFeaturesSectionIntro>
|
||||||
<StyledFeaturesSectionContent>
|
<StyledFeaturesSectionContent>
|
||||||
@ -318,3 +414,5 @@ const Home: NextPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
|
||||||
|
export const getStaticProps = getDocsMenuStaticProps;
|
||||||
|
@ -2,71 +2,93 @@ import darkScrollbar from '@mui/material/darkScrollbar';
|
|||||||
import { createTheme } from '@mui/material/styles';
|
import { createTheme } from '@mui/material/styles';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import type { PaletteMode, ThemeOptions } from '@mui/material';
|
import type { Components, PaletteMode, PaletteOptions, Theme } from '@mui/material';
|
||||||
|
|
||||||
const commonThemeProps: ThemeOptions = {};
|
|
||||||
|
|
||||||
const useCreateTheme = (mode: PaletteMode) => {
|
const useCreateTheme = (mode: PaletteMode) => {
|
||||||
return useMemo(
|
const theme = useMemo(() => createTheme(), []);
|
||||||
|
|
||||||
|
const palette: PaletteOptions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
mode === 'light'
|
mode === 'light'
|
||||||
? createTheme({
|
? {
|
||||||
...commonThemeProps,
|
mode,
|
||||||
palette: {
|
primary: {
|
||||||
mode,
|
main: '#3764be',
|
||||||
primary: {
|
|
||||||
main: '#3764be',
|
|
||||||
},
|
|
||||||
secondary: {
|
|
||||||
main: '#3764be',
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
default: '#f9f9f9',
|
|
||||||
paper: '#f9f9f9',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
typography: {
|
secondary: {
|
||||||
fontFamily:
|
main: '#3764be',
|
||||||
"'Roboto', -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif",
|
|
||||||
h1: {
|
|
||||||
fontSize: '42px',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
background: {
|
||||||
: createTheme({
|
default: '#f9f9f9',
|
||||||
...commonThemeProps,
|
paper: '#f9f9f9',
|
||||||
components: {
|
|
||||||
MuiCssBaseline: {
|
|
||||||
styleOverrides: {
|
|
||||||
body: darkScrollbar(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
palette: {
|
}
|
||||||
mode,
|
: {
|
||||||
primary: {
|
mode,
|
||||||
main: '#3A69C7',
|
primary: {
|
||||||
},
|
main: '#3A69C7',
|
||||||
secondary: {
|
|
||||||
main: '#5ecffb',
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
default: '#2e3034',
|
|
||||||
paper: '#2e3034',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
typography: {
|
secondary: {
|
||||||
fontFamily:
|
main: '#5ecffb',
|
||||||
"'Roboto', -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif",
|
|
||||||
h1: {
|
|
||||||
fontSize: '42px',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}),
|
background: {
|
||||||
|
default: '#2e3034',
|
||||||
|
paper: '#2e3034',
|
||||||
|
},
|
||||||
|
},
|
||||||
[mode],
|
[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;
|
export default useCreateTheme;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user