Skip to content

Commit

Permalink
feat: Added ArticleNavigation component, which provides next/prev nav…
Browse files Browse the repository at this point in the history
…igation between articles
  • Loading branch information
Aleksei committed Jan 15, 2024
1 parent 2960c4d commit dacad10
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 45 deletions.
28 changes: 28 additions & 0 deletions src/components/ArticleNavigations/ArticleNavigations.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@use '~@gravity-ui/page-constructor/styles/variables.scss' as pcVariables;
@use '../../variables.scss';

$block: '.#{variables.$ns}article-navigations';

#{$block} {
display: flex;
gap: 24px;
&__item {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 4px;
&_full {
justify-content: space-between;
padding-left: 20px;
}
&-button {
width: 56px;
height: 100%;
border-radius: 11px;
overflow: hidden;
}
}
}
52 changes: 52 additions & 0 deletions src/components/ArticleNavigations/ArticleNavigations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {ArrowLeft, ArrowRight} from '@gravity-ui/icons';
import {Button, Flex, Icon, Text} from '@gravity-ui/uikit';
import {block} from 'src/utils';

const b = block('article-navigations');

import './ArticleNavigations.scss';

interface ArticleNavigationsProps {
previousTitle: string;
nextTitle: string;
nextHandler: () => void;
prevHandler: () => void;
}

export const ArticleNavigations = ({
previousTitle,
nextTitle,
nextHandler,
prevHandler,
}: ArticleNavigationsProps) => {
return (
<div className={b()}>
<div className={b('item')}>
<Button onClick={prevHandler} className={b('item-button')} width="max">
<Icon data={ArrowLeft} size={16} />
</Button>
<Flex direction="column" gap="1" style={{padding: '8px 0'}}>
<Text variant="body-short" color="light-complementary">
Previous
</Text>
<Text variant="body-2" color="primary">
{previousTitle}
</Text>
</Flex>
</div>
<div className={b('item', {full: true})}>
<Flex direction="column" gap="1" style={{padding: '8px 0'}}>
<Text variant="body-short" color="light-complementary">
Next
</Text>
<Text variant="body-2" color="primary">
{nextTitle}
</Text>
</Flex>
<Button onClick={nextHandler} className={b('item-button')} width="max">
<Icon data={ArrowRight} size={16} />
</Button>
</div>
</div>
);
};
1 change: 1 addition & 0 deletions src/components/ArticleNavigations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {ArticleNavigations} from './ArticleNavigations';
4 changes: 4 additions & 0 deletions src/components/Component/Component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ $block: '.#{variables.$ns}component';
border-radius: 10px;
}

&__navigation {
margin-top: 40px;
}

&__header {
display: flex;
align-items: center;
Expand Down
73 changes: 71 additions & 2 deletions src/components/Component/Component.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import {Button, Icon, Tabs} from '@gravity-ui/uikit';
import {URL} from 'next/dist/compiled/@edge-runtime/primitives/url';
import {useRouter} from 'next/router';
import React from 'react';
import React, {useMemo} from 'react';
import {CONTENT_WRAPPER_ID} from 'src/constants';

import figmaIcon from '../../assets/icons/figma.svg';
import githubIcon from '../../assets/icons/github.svg';
import {MDXRenderer} from '../../components/MDXRenderer/MDXRenderer';
import {Component as ComponentType} from '../../content/components/types';
import {block, getRouteFromReadmeUrl} from '../../utils';
import {ArticleNavigations} from '../ArticleNavigations';
import {Section} from '../NavigationLayout/types';
import {SandboxBlock} from '../SandboxBlock';

import './Component.scss';
Expand All @@ -34,9 +37,15 @@ export type ComponentProps = {
libId: string;
component: ComponentType;
readmeContent: string;
sections: Section[];
};

export const Component: React.FC<ComponentProps> = ({libId, component, readmeContent}) => {
export const Component: React.FC<ComponentProps> = ({
libId,
component,
readmeContent,
sections,
}) => {
const router = useRouter();
const {tabId} = router.query;

Expand Down Expand Up @@ -66,6 +75,56 @@ export const Component: React.FC<ComponentProps> = ({libId, component, readmeCon
[component.content?.readmeUrl],
);

const currentSection = useMemo(
() => sections.find((item) => item.id === libId),
[libId, sections],
);

const currentIndex = useMemo(() => {
if (!currentSection) {
return null;
}
return currentSection.subSections.findIndex((item) => item.id === component.id);
}, [currentSection, component.id]);

const nextSection = useMemo(() => {
if (!currentSection) {
return null;
}
const nextIndex = (currentIndex + 1) % currentSection.subSections.length;
return currentSection.subSections[nextIndex];
}, [currentIndex, currentSection]);

const prevSection = useMemo(() => {
if (!currentSection) {
return null;
}
const prevIndex =
(currentIndex - 1 + currentSection.subSections.length) %
currentSection.subSections.length;
return currentSection.subSections[prevIndex];
}, [currentIndex, currentSection]);

const scrollTop = React.useCallback(() => {
const content = document.getElementById(CONTENT_WRAPPER_ID);
if (content) {
content.scrollTo({
top: 0,
behavior: 'smooth',
});
}
}, []);

const onNextHandler = () => {
router.push(nextSection.url);
scrollTop();
};

const onPrevHandler = () => {
router.push(prevSection.url);
scrollTop();
};

return (
<div className={b()}>
<div className={b('header')}>
Expand Down Expand Up @@ -140,6 +199,16 @@ export const Component: React.FC<ComponentProps> = ({libId, component, readmeCon
rewriteLinks={rewriteLinks}
withComponents
/>
{typeof window !== 'undefined' ? (

Check warning on line 202 in src/components/Component/Component.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected negated condition
<div className={b('navigation')}>
<ArticleNavigations
prevHandler={onPrevHandler}
nextHandler={onNextHandler}
previousTitle={prevSection.title}
nextTitle={nextSection.title}
/>
</div>
) : null}
</>
)}
</div>
Expand Down
20 changes: 3 additions & 17 deletions src/components/ComponentsLayout/ComponentsLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,20 @@
import React, {useMemo} from 'react';
import React from 'react';

import {libs} from '../../content/components';
import {NavigationLayout, Section} from '../NavigationLayout/NavigationLayout';

export type ComponentsLayoutProps = {
libId: string;
componentId?: string;
children?: React.ReactNode;
sections: Section[];
};

export const ComponentsLayout: React.FC<ComponentsLayoutProps> = ({
libId,
componentId,
children,
sections,
}) => {
const sections = useMemo<Section[]>(() => {
return libs.map((lib) => ({
id: lib.id,
title: lib.title,
// url: `/components/${lib.id}`, // "Overview" link
subSections: lib.components.map((component) => ({
id: component.id,
title: component.title,
url:
component.isComingSoon === true ? '#' : `/components/${lib.id}/${component.id}`,
isComingSoon: component.isComingSoon,
})),
}));
}, []);

return (
<NavigationLayout
sections={sections}
Expand Down
3 changes: 3 additions & 0 deletions src/components/DesignArticle/DesignArticle.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ $block: '.#{variables.$ns}design-article';
line-height: 48px;
font-weight: 600;
}
&__content {
margin-bottom: 40px;
}
}
20 changes: 19 additions & 1 deletion src/components/DesignArticle/DesignArticle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';

import {Article} from '../../content/design/types';
import {block} from '../../utils';
import {ArticleNavigations} from '../ArticleNavigations/index';
import {MDXRenderer} from '../MDXRenderer/MDXRenderer';

import './DesignArticle.scss';
Expand All @@ -10,15 +11,32 @@ const b = block('design-article');

export type DesignArticleProps = {
article: Article;
articleId?: string;
previousTitle: string;
nextTitle: string;
nextHandler: () => void;
prevHandler: () => void;
};

export const DesignArticle: React.FC<DesignArticleProps> = ({article}) => {
export const DesignArticle: React.FC<DesignArticleProps> = ({
article,
previousTitle,
nextTitle,
nextHandler,
prevHandler,
}) => {
return (
<div className={b()}>
<h1 className={b('title')}>{article.title}</h1>
<div className={b('content')}>
<MDXRenderer text={article.content} />
</div>
<ArticleNavigations
prevHandler={prevHandler}
nextHandler={nextHandler}
previousTitle={previousTitle}
nextTitle={nextTitle}
/>
</div>
);
};
26 changes: 8 additions & 18 deletions src/components/DesignLayout/DesignLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
import React, {useMemo} from 'react';
import React from 'react';

import {sections as designSections} from '../../content/design';
import {NavigationLayout, Section} from '../NavigationLayout/NavigationLayout';

export type DesignLayoutProps = {
sectionId: string;
articleId?: string;
children?: React.ReactNode;
sections: Section[];
};

export const DesignLayout: React.FC<DesignLayoutProps> = ({sectionId, articleId, children}) => {
const sections = useMemo<Section[]>(() => {
const result: Section[] = designSections.map((section) => ({
id: section.id,
title: section.title,
// Uncomment it to show overview tab
// url: `/design/${section.id}`,
subSections: section.articles.map((article) => ({
id: article.id,
title: article.title,
url: `/design/${section.id}/${article.id}`,
})),
}));
return result;
}, []);

export const DesignLayout: React.FC<DesignLayoutProps> = ({
sectionId,
articleId,
children,
sections,
}) => {
return (
<NavigationLayout
sections={sections}
Expand Down
26 changes: 24 additions & 2 deletions src/pages/components/[libId]/[componentId].tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {GetStaticPaths, GetStaticProps} from 'next';
import {useMemo} from 'react';
import {Section} from 'src/components/NavigationLayout/types';

import {Component} from '../../../components/Component/Component';
import {ComponentsLayout} from '../../../components/ComponentsLayout/ComponentsLayout';
Expand Down Expand Up @@ -62,10 +64,30 @@ export const ComponentPage = ({
return null;
}

const sections = useMemo<Section[]>(() => {
return libs.map((lib) => ({

Check warning on line 68 in src/pages/components/[libId]/[componentId].tsx

View workflow job for this annotation

GitHub Actions / Verify Files

'lib' is already declared in the upper scope on line 60 column 11
id: lib.id,
title: lib.title,
// url: `/components/${lib.id}`, // "Overview" link
subSections: lib.components.map((component) => ({

Check warning on line 72 in src/pages/components/[libId]/[componentId].tsx

View workflow job for this annotation

GitHub Actions / Verify Files

'component' is already declared in the upper scope on line 61 column 11
id: component.id,
title: component.title,
url:
component.isComingSoon === true ? '#' : `/components/${lib.id}/${component.id}`,
isComingSoon: component.isComingSoon,
})),
}));
}, []);

return (
<Layout title={`${lib.title}${component.title}`}>
<ComponentsLayout libId={libId} componentId={componentId}>
<Component libId={libId} component={component} readmeContent={readmeContent} />
<ComponentsLayout libId={libId} componentId={componentId} sections={sections}>
<Component
libId={libId}
component={component}
readmeContent={readmeContent}
sections={sections}
/>
</ComponentsLayout>
</Layout>
);
Expand Down
Loading

0 comments on commit dacad10

Please sign in to comment.