From 709f38ef92a79707df93cb73d1ee1e58fed929d4 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Tue, 16 Jan 2024 02:42:23 +0300 Subject: [PATCH 1/6] feat: Added ArticleNavigation component, which provides next/prev navigation between articles --- .../ArticleNavigations.scss | 28 ++++++ .../ArticleNavigations/ArticleNavigations.tsx | 52 +++++++++++ src/components/ArticleNavigations/index.ts | 1 + src/components/Component/Component.scss | 4 + src/components/Component/Component.tsx | 73 +++++++++++++++- .../ComponentsLayout/ComponentsLayout.tsx | 20 +---- .../DesignArticle/DesignArticle.scss | 3 + .../DesignArticle/DesignArticle.tsx | 20 ++++- src/components/DesignLayout/DesignLayout.tsx | 26 ++---- .../components/[libId]/[componentId].tsx | 26 +++++- src/pages/design/[sectionId]/[articleId].tsx | 87 +++++++++++++++++-- 11 files changed, 295 insertions(+), 45 deletions(-) create mode 100644 src/components/ArticleNavigations/ArticleNavigations.scss create mode 100644 src/components/ArticleNavigations/ArticleNavigations.tsx create mode 100644 src/components/ArticleNavigations/index.ts diff --git a/src/components/ArticleNavigations/ArticleNavigations.scss b/src/components/ArticleNavigations/ArticleNavigations.scss new file mode 100644 index 000000000000..01966bc8b849 --- /dev/null +++ b/src/components/ArticleNavigations/ArticleNavigations.scss @@ -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; + } + } +} diff --git a/src/components/ArticleNavigations/ArticleNavigations.tsx b/src/components/ArticleNavigations/ArticleNavigations.tsx new file mode 100644 index 000000000000..0846113aae1c --- /dev/null +++ b/src/components/ArticleNavigations/ArticleNavigations.tsx @@ -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 ( +
+
+ + + + Previous + + + {previousTitle} + + +
+
+ + + Next + + + {nextTitle} + + + +
+
+ ); +}; diff --git a/src/components/ArticleNavigations/index.ts b/src/components/ArticleNavigations/index.ts new file mode 100644 index 000000000000..f456dad0ba45 --- /dev/null +++ b/src/components/ArticleNavigations/index.ts @@ -0,0 +1 @@ +export {ArticleNavigations} from './ArticleNavigations'; diff --git a/src/components/Component/Component.scss b/src/components/Component/Component.scss index 4f2ff9ff39fb..ddd97ca397d7 100644 --- a/src/components/Component/Component.scss +++ b/src/components/Component/Component.scss @@ -19,6 +19,10 @@ $block: '.#{variables.$ns}component'; border-radius: 10px; } + &__navigation { + margin-top: 40px; + } + &__header { display: flex; align-items: center; diff --git a/src/components/Component/Component.tsx b/src/components/Component/Component.tsx index a64b04a9cbc3..5b66a0b4bd15 100644 --- a/src/components/Component/Component.tsx +++ b/src/components/Component/Component.tsx @@ -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'; @@ -34,9 +37,15 @@ export type ComponentProps = { libId: string; component: ComponentType; readmeContent: string; + sections: Section[]; }; -export const Component: React.FC = ({libId, component, readmeContent}) => { +export const Component: React.FC = ({ + libId, + component, + readmeContent, + sections, +}) => { const router = useRouter(); const {tabId} = router.query; @@ -66,6 +75,56 @@ export const Component: React.FC = ({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 (
@@ -140,6 +199,16 @@ export const Component: React.FC = ({libId, component, readmeCon rewriteLinks={rewriteLinks} withComponents /> + {typeof window !== 'undefined' ? ( +
+ +
+ ) : null} )}
diff --git a/src/components/ComponentsLayout/ComponentsLayout.tsx b/src/components/ComponentsLayout/ComponentsLayout.tsx index 28581de46fce..61803288dfae 100644 --- a/src/components/ComponentsLayout/ComponentsLayout.tsx +++ b/src/components/ComponentsLayout/ComponentsLayout.tsx @@ -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 = ({ libId, componentId, children, + sections, }) => { - const sections = useMemo(() => { - 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 ( void; + prevHandler: () => void; }; -export const DesignArticle: React.FC = ({article}) => { +export const DesignArticle: React.FC = ({ + article, + previousTitle, + nextTitle, + nextHandler, + prevHandler, +}) => { return (

{article.title}

+
); }; diff --git a/src/components/DesignLayout/DesignLayout.tsx b/src/components/DesignLayout/DesignLayout.tsx index 84b2344eab73..37f0e214ece6 100644 --- a/src/components/DesignLayout/DesignLayout.tsx +++ b/src/components/DesignLayout/DesignLayout.tsx @@ -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 = ({sectionId, articleId, children}) => { - const sections = useMemo(() => { - 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 = ({ + sectionId, + articleId, + children, + sections, +}) => { return ( (() => { + 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 ( - - + + ); diff --git a/src/pages/design/[sectionId]/[articleId].tsx b/src/pages/design/[sectionId]/[articleId].tsx index 2eaf2443d660..0bc26cbc4d7e 100644 --- a/src/pages/design/[sectionId]/[articleId].tsx +++ b/src/pages/design/[sectionId]/[articleId].tsx @@ -1,13 +1,17 @@ import {GetStaticPaths, GetStaticProps} from 'next'; +import {useRouter} from 'next/router'; +import {useCallback, 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'; import {Layout} from '../../../components/Layout/Layout'; -import {sections} from '../../../content/design'; +import {sections as designSections} from '../../../content/design'; export const getStaticPaths: GetStaticPaths = async () => { return { - paths: sections.reduce<{params: {sectionId: string; articleId: string}}[]>( + paths: designSections.reduce<{params: {sectionId: string; articleId: string}}[]>( (acc, section) => { section.articles.forEach((article) => { acc.push({params: {sectionId: section.id, articleId: article.id}}); @@ -27,17 +31,90 @@ export const getStaticProps: GetStaticProps = async (context) => { }; export const ArticlePage = ({sectionId, articleId}: {sectionId: string; articleId: string}) => { - const section = sections.find((item) => item.id === sectionId); + const router = useRouter(); + const section = designSections.find((item) => item.id === sectionId); const article = section?.articles.find((item) => item.id === articleId); if (!section || !article) { return null; } + const sections = useMemo(() => { + 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; + }, []); + + const currentSection = useMemo( + () => sections.find((item) => item.id === sectionId), + [sectionId, sections], + ); + + const currentIndex = useMemo(() => { + if (!currentSection) { + return null; + } + return currentSection.subSections.findIndex((item) => item.id === articleId); + }, [currentSection, articleId]); + + 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 = 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 ( - - + + ); From f5a80e5a0e2c54069c922b8dea61b716892c0b02 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Tue, 16 Jan 2024 16:55:31 +0300 Subject: [PATCH 2/6] fix: Fixed typecheeck errors --- src/components/Component/Component.tsx | 14 ++++++++++---- src/pages/design/[sectionId]/[articleId].tsx | 16 +++++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/components/Component/Component.tsx b/src/components/Component/Component.tsx index 5b66a0b4bd15..226650637888 100644 --- a/src/components/Component/Component.tsx +++ b/src/components/Component/Component.tsx @@ -81,14 +81,14 @@ export const Component: React.FC = ({ ); const currentIndex = useMemo(() => { - if (!currentSection) { + if (!currentSection || !currentSection.subSections || !currentIndex) { return null; } return currentSection.subSections.findIndex((item) => item.id === component.id); }, [currentSection, component.id]); const nextSection = useMemo(() => { - if (!currentSection) { + if (!currentSection || !currentSection.subSections || !currentIndex) { return null; } const nextIndex = (currentIndex + 1) % currentSection.subSections.length; @@ -96,7 +96,7 @@ export const Component: React.FC = ({ }, [currentIndex, currentSection]); const prevSection = useMemo(() => { - if (!currentSection) { + if (!currentSection || !currentSection.subSections || !currentIndex) { return null; } const prevIndex = @@ -116,11 +116,17 @@ export const Component: React.FC = ({ }, []); const onNextHandler = () => { + if (!nextSection) { + return; + } router.push(nextSection.url); scrollTop(); }; const onPrevHandler = () => { + if (!prevSection) { + return; + } router.push(prevSection.url); scrollTop(); }; @@ -199,7 +205,7 @@ export const Component: React.FC = ({ rewriteLinks={rewriteLinks} withComponents /> - {typeof window !== 'undefined' ? ( + {typeof window !== 'undefined' && prevSection && nextSection ? (
{ - if (!currentSection) { + if (!currentSection || !currentSection.subSections) { return null; } return currentSection.subSections.findIndex((item) => item.id === articleId); }, [currentSection, articleId]); const nextSection = useMemo(() => { - if (!currentSection) { + if (!currentSection || !currentSection.subSections || !currentIndex) { return null; } const nextIndex = (currentIndex + 1) % currentSection.subSections.length; @@ -75,7 +75,7 @@ export const ArticlePage = ({sectionId, articleId}: {sectionId: string; articleI }, [currentIndex, currentSection]); const prevSection = useMemo(() => { - if (!currentSection) { + if (!currentSection || !currentSection.subSections || !currentIndex) { return null; } const prevIndex = @@ -95,11 +95,17 @@ export const ArticlePage = ({sectionId, articleId}: {sectionId: string; articleI }, []); const onNextHandler = () => { + if (!nextSection) { + return; + } router.push(nextSection.url); scrollTop(); }; const onPrevHandler = () => { + if (!prevSection) { + return; + } router.push(prevSection.url); scrollTop(); }; @@ -112,8 +118,8 @@ export const ArticlePage = ({sectionId, articleId}: {sectionId: string; articleI articleId={articleId} prevHandler={onPrevHandler} nextHandler={onNextHandler} - previousTitle={prevSection.title} - nextTitle={nextSection.title} + previousTitle={prevSection ? prevSection.title : ''} + nextTitle={nextSection ? nextSection.title : ''} /> From ca0888021eaa3de3dd52364b8af8d9bbfc50c84f Mon Sep 17 00:00:00 2001 From: Aleksei Date: Tue, 23 Jan 2024 21:42:08 +0300 Subject: [PATCH 3/6] fix: fixed review issues --- .../ArticleNavigation/ArticleNavigation.scss | 60 +++++++++ .../ArticleNavigation/ArticleNavigation.tsx | 95 ++++++++++++++ src/components/ArticleNavigation/index.ts | 1 + .../ArticleNavigations.scss | 28 ---- .../ArticleNavigations/ArticleNavigations.tsx | 52 -------- src/components/ArticleNavigations/index.ts | 1 - src/components/Component/Component.tsx | 120 ++++++++---------- .../DesignArticle/DesignArticle.tsx | 67 +++++++--- .../components/[libId]/[componentId].tsx | 18 +-- src/pages/design/[sectionId]/[articleId].tsx | 81 ++---------- 10 files changed, 278 insertions(+), 245 deletions(-) create mode 100644 src/components/ArticleNavigation/ArticleNavigation.scss create mode 100644 src/components/ArticleNavigation/ArticleNavigation.tsx create mode 100644 src/components/ArticleNavigation/index.ts delete mode 100644 src/components/ArticleNavigations/ArticleNavigations.scss delete mode 100644 src/components/ArticleNavigations/ArticleNavigations.tsx delete mode 100644 src/components/ArticleNavigations/index.ts 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 (

{article.title}

- +
); }; 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 ( From 838eedff193f74ca3f2bc0106448c8ceecfd99be Mon Sep 17 00:00:00 2001 From: Aleksei Date: Fri, 26 Jan 2024 18:17:08 +0300 Subject: [PATCH 4/6] fix: review fixes --- .../ArticleNavigation/ArticleNavigation.scss | 11 ++--------- .../ArticleNavigation/ArticleNavigation.tsx | 8 ++------ src/components/Component/Component.scss | 2 +- src/components/DesignArticle/DesignArticle.scss | 4 ++-- src/components/DesignArticle/DesignArticle.tsx | 6 +++--- 5 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/components/ArticleNavigation/ArticleNavigation.scss b/src/components/ArticleNavigation/ArticleNavigation.scss index c3be46574b28..99e602b8b980 100644 --- a/src/components/ArticleNavigation/ArticleNavigation.scss +++ b/src/components/ArticleNavigation/ArticleNavigation.scss @@ -33,27 +33,20 @@ $block: '.#{variables.$ns}article-navigation'; 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); + padding: 0 var(--g-spacing-5); @media (max-width: map-get(pcVariables.$gridBreakpoints, 'md') - 1) { - width: 40px; + padding: 0 var(--g-spacing-3); } } } diff --git a/src/components/ArticleNavigation/ArticleNavigation.tsx b/src/components/ArticleNavigation/ArticleNavigation.tsx index 45453ae89d03..fcb47952ee64 100644 --- a/src/components/ArticleNavigation/ArticleNavigation.tsx +++ b/src/components/ArticleNavigation/ArticleNavigation.tsx @@ -43,11 +43,7 @@ export const ArticleNavigation = ({prevSection, nextSection}: ArticleNavigationP return (
{prevSection && ( - +
@@ -70,7 +66,7 @@ export const ArticleNavigation = ({prevSection, nextSection}: ArticleNavigationP
diff --git a/src/components/Component/Component.scss b/src/components/Component/Component.scss index ddd97ca397d7..ac97c8dad0a6 100644 --- a/src/components/Component/Component.scss +++ b/src/components/Component/Component.scss @@ -20,7 +20,7 @@ $block: '.#{variables.$ns}component'; } &__navigation { - margin-top: 40px; + margin-top: var(--g-spacing-10); } &__header { diff --git a/src/components/DesignArticle/DesignArticle.scss b/src/components/DesignArticle/DesignArticle.scss index 7a3dabd807c1..d692f2143260 100644 --- a/src/components/DesignArticle/DesignArticle.scss +++ b/src/components/DesignArticle/DesignArticle.scss @@ -19,7 +19,7 @@ $block: '.#{variables.$ns}design-article'; line-height: 48px; font-weight: 600; } - &__content { - margin-bottom: 40px; + &__navigation { + margin-top: var(--g-spacing-10); } } diff --git a/src/components/DesignArticle/DesignArticle.tsx b/src/components/DesignArticle/DesignArticle.tsx index 75334670afa5..b9c68fa6c116 100644 --- a/src/components/DesignArticle/DesignArticle.tsx +++ b/src/components/DesignArticle/DesignArticle.tsx @@ -68,10 +68,10 @@ export const DesignArticle: React.FC = ({ return (

{article.title}

-
- + +
+
-
); }; From 502579debbe5258eae586b1b1fd547f6aa60ac85 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Thu, 1 Feb 2024 19:50:52 +0300 Subject: [PATCH 5/6] fix: review fixes --- .../ArticleNavigation/ArticleNavigation.scss | 1 + .../ArticleNavigation/ArticleNavigation.tsx | 91 +++++++++---------- .../DesignArticle/DesignArticle.tsx | 12 +-- src/pages/design/[sectionId]/[articleId].tsx | 7 +- 4 files changed, 46 insertions(+), 65 deletions(-) diff --git a/src/components/ArticleNavigation/ArticleNavigation.scss b/src/components/ArticleNavigation/ArticleNavigation.scss index 99e602b8b980..be7fc02750f7 100644 --- a/src/components/ArticleNavigation/ArticleNavigation.scss +++ b/src/components/ArticleNavigation/ArticleNavigation.scss @@ -24,6 +24,7 @@ $block: '.#{variables.$ns}article-navigation'; } &__button { cursor: pointer; + text-decoration: none; display: flex; gap: var(--g-spacing-3); width: 50%; diff --git a/src/components/ArticleNavigation/ArticleNavigation.tsx b/src/components/ArticleNavigation/ArticleNavigation.tsx index fcb47952ee64..cf05cf033c78 100644 --- a/src/components/ArticleNavigation/ArticleNavigation.tsx +++ b/src/components/ArticleNavigation/ArticleNavigation.tsx @@ -1,5 +1,6 @@ import {ArrowLeft, ArrowRight} from '@gravity-ui/icons'; -import {Flex, Icon, Link, Text} from '@gravity-ui/uikit'; +import {Flex, Icon, Text} from '@gravity-ui/uikit'; +import Link from 'next/link'; import {useCallback} from 'react'; import {CONTENT_WRAPPER_ID} from 'src/constants'; import {block} from 'src/utils'; @@ -26,64 +27,54 @@ export const ArticleNavigation = ({prevSection, nextSection}: ArticleNavigationP } }, []); - const nextHandler = () => { - if (!nextSection) { - return; - } - scrollTop(); - }; - - const prevHandler = () => { - if (!prevSection) { - return; - } + const linkClickHandler = () => { scrollTop(); }; return (
{prevSection && ( - -
- -
- - - Previous - - - {prevSection.title} - - + + +
+ +
+ + + Previous + + + {prevSection.title} + + +
)} {nextSection && ( - -
- -
- - - Next - - - {nextSection.title} - - + + +
+ +
+ + + Next + + + {nextSection.title} + + +
)}
diff --git a/src/components/DesignArticle/DesignArticle.tsx b/src/components/DesignArticle/DesignArticle.tsx index b9c68fa6c116..0585060749c7 100644 --- a/src/components/DesignArticle/DesignArticle.tsx +++ b/src/components/DesignArticle/DesignArticle.tsx @@ -12,17 +12,11 @@ const b = block('design-article'); export type DesignArticleProps = { article: Article; - articleId?: string; sectionId?: string; sections: Section[]; }; -export const DesignArticle: React.FC = ({ - article, - articleId, - sectionId, - sections, -}) => { +export const DesignArticle: React.FC = ({article, sectionId, sections}) => { const currentSection = useMemo( () => sections.find((item) => item.id === sectionId), [sectionId, sections], @@ -32,8 +26,8 @@ export const DesignArticle: React.FC = ({ if (!currentSection || !currentSection.subSections) { return null; } - return currentSection.subSections.findIndex((item) => item.id === articleId); - }, [currentSection, articleId]); + return currentSection.subSections.findIndex((item) => item.id === article.id); + }, [currentSection, article.id]); const nextSection = useMemo(() => { if ( diff --git a/src/pages/design/[sectionId]/[articleId].tsx b/src/pages/design/[sectionId]/[articleId].tsx index 9d61337b5743..d1843377f84a 100644 --- a/src/pages/design/[sectionId]/[articleId].tsx +++ b/src/pages/design/[sectionId]/[articleId].tsx @@ -54,12 +54,7 @@ export const ArticlePage = ({sectionId, articleId}: {sectionId: string; articleI return ( - + ); From 5af7b8dcb606c30b2fb1b1e4d45def23a7781a70 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Thu, 1 Feb 2024 20:03:35 +0300 Subject: [PATCH 6/6] fix: added hiding texts into buttons on mobile --- .../ArticleNavigation/ArticleNavigation.scss | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/ArticleNavigation/ArticleNavigation.scss b/src/components/ArticleNavigation/ArticleNavigation.scss index be7fc02750f7..3b885e3b9178 100644 --- a/src/components/ArticleNavigation/ArticleNavigation.scss +++ b/src/components/ArticleNavigation/ArticleNavigation.scss @@ -16,6 +16,9 @@ $block: '.#{variables.$ns}article-navigation'; @media (max-width: map-get(pcVariables.$gridBreakpoints, 'md') - 1) { padding: var(--g-spacing-3) 0; } + @media (max-width: map-get(pcVariables.$gridBreakpoints, 'sm') - 1) { + display: none; + } &-title { @media (max-width: map-get(pcVariables.$gridBreakpoints, 'md') - 1) { display: none; @@ -32,10 +35,16 @@ $block: '.#{variables.$ns}article-navigation'; border: 1px solid var(--g-color-sfx-fade); padding: var(--g-spacing-1); justify-content: space-between; + @media (max-width: map-get(pcVariables.$gridBreakpoints, 'sm') - 1) { + width: unset; + } &_reverse { flex-direction: row-reverse; margin-left: auto; padding-left: var(--g-spacing-5); + @media (max-width: map-get(pcVariables.$gridBreakpoints, 'sm') - 1) { + padding-left: var(--g-spacing-1); + } } &-icon { flex: none; @@ -49,6 +58,9 @@ $block: '.#{variables.$ns}article-navigation'; @media (max-width: map-get(pcVariables.$gridBreakpoints, 'md') - 1) { padding: 0 var(--g-spacing-3); } + @media (max-width: map-get(pcVariables.$gridBreakpoints, 'sm') - 1) { + padding: var(--g-spacing-3); + } } } }