diff --git a/src/components/ArticleNavigation/ArticleNavigation.scss b/src/components/ArticleNavigation/ArticleNavigation.scss
new file mode 100644
index 000000000000..c3be46574b28
--- /dev/null
+++ b/src/components/ArticleNavigation/ArticleNavigation.scss
@@ -0,0 +1,60 @@
+@use '~@gravity-ui/page-constructor/styles/variables.scss' as pcVariables;
+@use '../../variables.scss';
+
+$block: '.#{variables.$ns}article-navigation';
+
+#{$block} {
+ display: flex;
+ gap: var(--g-spacing-6);
+ @media (max-width: map-get(pcVariables.$gridBreakpoints, 'md') - 1) {
+ gap: var(--g-spacing-5);
+ }
+ &__content {
+ padding: var(--g-spacing-2) 0;
+ overflow: hidden;
+ width: 100%;
+ @media (max-width: map-get(pcVariables.$gridBreakpoints, 'md') - 1) {
+ padding: var(--g-spacing-3) 0;
+ }
+ &-title {
+ @media (max-width: map-get(pcVariables.$gridBreakpoints, 'md') - 1) {
+ display: none;
+ }
+ }
+ }
+ &__button {
+ cursor: pointer;
+ display: flex;
+ gap: var(--g-spacing-3);
+ width: 50%;
+ border-radius: 16px;
+ border: 1px solid var(--g-color-sfx-fade);
+ padding: var(--g-spacing-1);
+ justify-content: space-between;
+ &_reverse {
+ flex-direction: row-reverse;
+ }
+ &_right {
+ margin-left: auto;
+ padding-left: var(--g-spacing-5);
+ }
+ &_left {
+ margin-right: auto;
+ padding-right: var(--g-spacing-5);
+ }
+ &-icon {
+ flex: none;
+ width: 56px;
+ height: 100%;
+ border-radius: 12px;
+ background: var(--g-color-base-generic);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--g-color-text-primary);
+ @media (max-width: map-get(pcVariables.$gridBreakpoints, 'md') - 1) {
+ width: 40px;
+ }
+ }
+ }
+}
diff --git a/src/components/ArticleNavigation/ArticleNavigation.tsx b/src/components/ArticleNavigation/ArticleNavigation.tsx
new file mode 100644
index 000000000000..45453ae89d03
--- /dev/null
+++ b/src/components/ArticleNavigation/ArticleNavigation.tsx
@@ -0,0 +1,95 @@
+import {ArrowLeft, ArrowRight} from '@gravity-ui/icons';
+import {Flex, Icon, Link, Text} from '@gravity-ui/uikit';
+import {useCallback} from 'react';
+import {CONTENT_WRAPPER_ID} from 'src/constants';
+import {block} from 'src/utils';
+
+import {SubSection} from '../NavigationLayout/types';
+
+const b = block('article-navigation');
+
+import './ArticleNavigation.scss';
+
+interface ArticleNavigationProps {
+ prevSection: SubSection | null;
+ nextSection: SubSection | null;
+}
+
+export const ArticleNavigation = ({prevSection, nextSection}: ArticleNavigationProps) => {
+ const scrollTop = useCallback(() => {
+ const content = document.getElementById(CONTENT_WRAPPER_ID);
+ if (content) {
+ content.scrollTo({
+ top: 0,
+ behavior: 'smooth',
+ });
+ }
+ }, []);
+
+ const nextHandler = () => {
+ if (!nextSection) {
+ return;
+ }
+ scrollTop();
+ };
+
+ const prevHandler = () => {
+ if (!prevSection) {
+ return;
+ }
+ scrollTop();
+ };
+
+ return (
+
+ {prevSection && (
+
+
+
+
+
+
+ Previous
+
+
+ {prevSection.title}
+
+
+
+ )}
+ {nextSection && (
+
+
+
+
+
+
+ Next
+
+
+ {nextSection.title}
+
+
+
+ )}
+
+ );
+};
diff --git a/src/components/ArticleNavigation/index.ts b/src/components/ArticleNavigation/index.ts
new file mode 100644
index 000000000000..b6d4f0b45b01
--- /dev/null
+++ b/src/components/ArticleNavigation/index.ts
@@ -0,0 +1 @@
+export {ArticleNavigation} from './ArticleNavigation';
diff --git a/src/components/ArticleNavigations/ArticleNavigations.scss b/src/components/ArticleNavigations/ArticleNavigations.scss
deleted file mode 100644
index 01966bc8b849..000000000000
--- a/src/components/ArticleNavigations/ArticleNavigations.scss
+++ /dev/null
@@ -1,28 +0,0 @@
-@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;
- }
- }
-}
diff --git a/src/components/ArticleNavigations/ArticleNavigations.tsx b/src/components/ArticleNavigations/ArticleNavigations.tsx
deleted file mode 100644
index 0846113aae1c..000000000000
--- a/src/components/ArticleNavigations/ArticleNavigations.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-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 (
-
-
-
-
-
- Previous
-
-
- {previousTitle}
-
-
-
-
-
-
- Next
-
-
- {nextTitle}
-
-
-
-
-
- );
-};
diff --git a/src/components/ArticleNavigations/index.ts b/src/components/ArticleNavigations/index.ts
deleted file mode 100644
index f456dad0ba45..000000000000
--- a/src/components/ArticleNavigations/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export {ArticleNavigations} from './ArticleNavigations';
diff --git a/src/components/Component/Component.tsx b/src/components/Component/Component.tsx
index 226650637888..fb226ccddc5f 100644
--- a/src/components/Component/Component.tsx
+++ b/src/components/Component/Component.tsx
@@ -1,15 +1,14 @@
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, {useMemo} from 'react';
-import {CONTENT_WRAPPER_ID} from 'src/constants';
+import React from 'react';
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 {ArticleNavigation} from '../ArticleNavigation/ArticleNavigation';
import {Section} from '../NavigationLayout/types';
import {SandboxBlock} from '../SandboxBlock';
@@ -52,84 +51,71 @@ export const Component: React.FC = ({
const [activeTab, setActiveTab] = React.useState(
tabId === Tab.Design ? Tab.Design : Tab.Overview,
);
- React.useEffect(() => {
- setActiveTab(tabId === Tab.Design ? Tab.Design : Tab.Overview);
- }, [tabId]);
-
- const rewriteLinks = React.useCallback(
- (link: string) => {
- if (!component.content?.readmeUrl) {
- return link;
- }
-
- const readmeUrl = new URL(component.content.readmeUrl);
- const url = new URL(link, component.content.readmeUrl);
-
- if (url.origin !== readmeUrl.origin) {
- return link;
- }
- const newLink = getRouteFromReadmeUrl(url.toString());
- return newLink ?? link;
- },
- [component.content?.readmeUrl],
- );
-
- const currentSection = useMemo(
+ const currentSection = React.useMemo(
() => sections.find((item) => item.id === libId),
[libId, sections],
);
- const currentIndex = useMemo(() => {
- if (!currentSection || !currentSection.subSections || !currentIndex) {
+ const currentIndex = React.useMemo(() => {
+ if (!currentSection || !currentSection.subSections) {
return null;
}
return currentSection.subSections.findIndex((item) => item.id === component.id);
}, [currentSection, component.id]);
- const nextSection = useMemo(() => {
- if (!currentSection || !currentSection.subSections || !currentIndex) {
+ const nextSection = React.useMemo(() => {
+ if (
+ !currentSection ||
+ !currentSection.subSections ||
+ (!currentIndex && currentIndex !== 0)
+ ) {
+ return null;
+ }
+ const nextIndex = currentIndex + 1;
+ if (nextIndex >= currentSection.subSections.length) {
return null;
}
- const nextIndex = (currentIndex + 1) % currentSection.subSections.length;
return currentSection.subSections[nextIndex];
}, [currentIndex, currentSection]);
- const prevSection = useMemo(() => {
- if (!currentSection || !currentSection.subSections || !currentIndex) {
+ const prevSection = React.useMemo(() => {
+ if (
+ !currentSection ||
+ !currentSection.subSections ||
+ (!currentIndex && currentIndex !== 0)
+ ) {
+ return null;
+ }
+ const prevIndex = currentIndex - 1;
+ if (prevIndex < 0) {
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',
- });
- }
- }, []);
+ React.useEffect(() => {
+ setActiveTab(tabId === Tab.Design ? Tab.Design : Tab.Overview);
+ }, [tabId]);
- const onNextHandler = () => {
- if (!nextSection) {
- return;
- }
- router.push(nextSection.url);
- scrollTop();
- };
+ const rewriteLinks = React.useCallback(
+ (link: string) => {
+ if (!component.content?.readmeUrl) {
+ return link;
+ }
- const onPrevHandler = () => {
- if (!prevSection) {
- return;
- }
- router.push(prevSection.url);
- scrollTop();
- };
+ const readmeUrl = new URL(component.content.readmeUrl);
+ const url = new URL(link, component.content.readmeUrl);
+
+ if (url.origin !== readmeUrl.origin) {
+ return link;
+ }
+
+ const newLink = getRouteFromReadmeUrl(url.toString());
+ return newLink ?? link;
+ },
+ [component.content?.readmeUrl],
+ );
return (
@@ -205,16 +191,12 @@ export const Component: React.FC
= ({
rewriteLinks={rewriteLinks}
withComponents
/>
- {typeof window !== 'undefined' && prevSection && nextSection ? (
-
- ) : null}
+
>
)}
diff --git a/src/components/DesignArticle/DesignArticle.tsx b/src/components/DesignArticle/DesignArticle.tsx
index 554ce76b7003..75334670afa5 100644
--- a/src/components/DesignArticle/DesignArticle.tsx
+++ b/src/components/DesignArticle/DesignArticle.tsx
@@ -1,9 +1,10 @@
-import React from 'react';
+import React, {useMemo} from 'react';
import {Article} from '../../content/design/types';
import {block} from '../../utils';
-import {ArticleNavigations} from '../ArticleNavigations/index';
+import {ArticleNavigation} from '../ArticleNavigation/ArticleNavigation';
import {MDXRenderer} from '../MDXRenderer/MDXRenderer';
+import {Section} from '../NavigationLayout/types';
import './DesignArticle.scss';
@@ -12,31 +13,65 @@ const b = block('design-article');
export type DesignArticleProps = {
article: Article;
articleId?: string;
- previousTitle: string;
- nextTitle: string;
- nextHandler: () => void;
- prevHandler: () => void;
+ sectionId?: string;
+ sections: Section[];
};
export const DesignArticle: React.FC = ({
article,
- previousTitle,
- nextTitle,
- nextHandler,
- prevHandler,
+ articleId,
+ sectionId,
+ sections,
}) => {
+ const currentSection = useMemo(
+ () => sections.find((item) => item.id === sectionId),
+ [sectionId, sections],
+ );
+
+ const currentIndex = useMemo(() => {
+ if (!currentSection || !currentSection.subSections) {
+ return null;
+ }
+ return currentSection.subSections.findIndex((item) => item.id === articleId);
+ }, [currentSection, articleId]);
+
+ const nextSection = useMemo(() => {
+ if (
+ !currentSection ||
+ !currentSection.subSections ||
+ (!currentIndex && currentIndex !== 0)
+ ) {
+ return null;
+ }
+ const nextIndex = currentIndex + 1;
+ if (nextIndex >= currentSection.subSections.length) {
+ return null;
+ }
+ return currentSection.subSections[nextIndex];
+ }, [currentIndex, currentSection]);
+
+ const prevSection = useMemo(() => {
+ if (
+ !currentSection ||
+ !currentSection.subSections ||
+ (!currentIndex && currentIndex !== 0)
+ ) {
+ return null;
+ }
+ const prevIndex = currentIndex - 1;
+ if (prevIndex < 0) {
+ return null;
+ }
+ return currentSection.subSections[prevIndex];
+ }, [currentIndex, currentSection]);
+
return (
);
};
diff --git a/src/pages/components/[libId]/[componentId].tsx b/src/pages/components/[libId]/[componentId].tsx
index 012d8fcf5dad..8d04d92d83fa 100644
--- a/src/pages/components/[libId]/[componentId].tsx
+++ b/src/pages/components/[libId]/[componentId].tsx
@@ -65,16 +65,18 @@ export const ComponentPage = ({
}
const sections = useMemo(() => {
- return libs.map((lib) => ({
- id: lib.id,
- title: lib.title,
+ return libs.map(({id, title, components}) => ({
+ id: id,
+ title: title,
// url: `/components/${lib.id}`, // "Overview" link
- subSections: lib.components.map((component) => ({
- id: component.id,
- title: component.title,
+ subSections: components.map((componentItem) => ({
+ id: componentItem.id,
+ title: componentItem.title,
url:
- component.isComingSoon === true ? '#' : `/components/${lib.id}/${component.id}`,
- isComingSoon: component.isComingSoon,
+ componentItem.isComingSoon === true
+ ? '#'
+ : `/components/${lib.id}/${componentItem.id}`,
+ isComingSoon: componentItem.isComingSoon,
})),
}));
}, []);
diff --git a/src/pages/design/[sectionId]/[articleId].tsx b/src/pages/design/[sectionId]/[articleId].tsx
index 253815bb49f7..9d61337b5743 100644
--- a/src/pages/design/[sectionId]/[articleId].tsx
+++ b/src/pages/design/[sectionId]/[articleId].tsx
@@ -1,8 +1,6 @@
import {GetStaticPaths, GetStaticProps} from 'next';
-import {useRouter} from 'next/router';
-import {useCallback, useMemo} from 'react';
+import {useMemo} from 'react';
import {Section} from 'src/components/NavigationLayout/types';
-import {CONTENT_WRAPPER_ID} from 'src/constants';
import {DesignArticle} from '../../../components/DesignArticle/DesignArticle';
import {DesignLayout} from '../../../components/DesignLayout/DesignLayout';
@@ -31,7 +29,6 @@ export const getStaticProps: GetStaticProps = async (context) => {
};
export const ArticlePage = ({sectionId, articleId}: {sectionId: string; articleId: string}) => {
- const router = useRouter();
const section = designSections.find((item) => item.id === sectionId);
const article = section?.articles.find((item) => item.id === articleId);
@@ -40,86 +37,28 @@ export const ArticlePage = ({sectionId, articleId}: {sectionId: string; articleI
}
const sections = useMemo(() => {
- const result: Section[] = designSections.map((section) => ({
- id: section.id,
- title: section.title,
+ const result: Section[] = designSections.map(({id, title, articles}) => ({
+ id: id,
+ title: 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}`,
+ subSections: articles.map((articleItem) => ({
+ id: articleItem.id,
+ title: articleItem.title,
+ url: `/design/${id}/${articleItem.id}`,
})),
}));
return result;
}, []);
- const currentSection = useMemo(
- () => sections.find((item) => item.id === sectionId),
- [sectionId, sections],
- );
-
- const currentIndex = useMemo(() => {
- if (!currentSection || !currentSection.subSections) {
- return null;
- }
- return currentSection.subSections.findIndex((item) => item.id === articleId);
- }, [currentSection, articleId]);
-
- const nextSection = useMemo(() => {
- if (!currentSection || !currentSection.subSections || !currentIndex) {
- return null;
- }
- const nextIndex = (currentIndex + 1) % currentSection.subSections.length;
- return currentSection.subSections[nextIndex];
- }, [currentIndex, currentSection]);
-
- const prevSection = useMemo(() => {
- if (!currentSection || !currentSection.subSections || !currentIndex) {
- return null;
- }
- const prevIndex =
- (currentIndex - 1 + currentSection.subSections.length) %
- currentSection.subSections.length;
- return currentSection.subSections[prevIndex];
- }, [currentIndex, currentSection]);
-
- const scrollTop = useCallback(() => {
- const content = document.getElementById(CONTENT_WRAPPER_ID);
- if (content) {
- content.scrollTo({
- top: 0,
- behavior: 'smooth',
- });
- }
- }, []);
-
- const onNextHandler = () => {
- if (!nextSection) {
- return;
- }
- router.push(nextSection.url);
- scrollTop();
- };
-
- const onPrevHandler = () => {
- if (!prevSection) {
- return;
- }
- router.push(prevSection.url);
- scrollTop();
- };
-
return (