From e528e52cfc09ec3008937bf56e0b5b8cb2a5c7a8 Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Sun, 10 Mar 2024 17:19:47 +0530 Subject: [PATCH 01/63] added link.tsx --- components/link.tsx | 81 +++++++++++++++++++++++++++++++++++++++++++++ utils/i18nPaths.ts | 21 +++++++----- 2 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 components/link.tsx diff --git a/components/link.tsx b/components/link.tsx new file mode 100644 index 00000000000..3558c18ed99 --- /dev/null +++ b/components/link.tsx @@ -0,0 +1,81 @@ +import Link from 'next/link'; +import { useRouter } from 'next/router'; +import { defaultLanguage, languages } from '../utils/i18n'; +import i18nPaths from '../utils/i18nPaths'; + +interface LinkComponentProps { + children: React.ReactNode; + locale?: string; + href?: string; +} + +export default function LinkComponent ({ children, locale, ...props }: LinkComponentProps){ + const router = useRouter(); + + // If there is no router available (e.g., during server-side rendering & cypress tests), render a standard Link + if (!router) { + return ( + + {children} + + ); + } + + const { pathname, query, asPath } = router; + + // Detect current language based on the path or query parameter + const slug = asPath.split('/')[1]; + const langSlug = languages.includes(slug) && slug; + const language: string = query.lang && typeof query.lang === 'string' ? query.lang : langSlug || defaultLanguage; // Ensure language is always a string + + let href = props.href || pathname; + + /* + If explicit href is provided, and the language-specific paths for the current language do not include the href, or if the href starts with "http", render a standard Link + */ + if ( + (props.href && i18nPaths[language] && !i18nPaths[language].includes(href)) || + href.includes('http', 0) + ) { + return ( + + {children} + + ); + } + + // If a locale is provided, update the href with the locale + if (locale) { + if (props.href) { + href = `/${locale}${href}`; + } else { + // If the current path starts with "/404", update href to be the root path with the locale + // Otherwise, replace "[lang]" placeholder with the locale + if (pathname.startsWith('/404')) { + href = `/${locale}`; + } else { + href = pathname.replace('[lang]', locale); + } + } + } else { + // If no locale is provided, update the href with the current language or keep it as is + if (language) { + href = `/${language}${href}`; + } else { + href = `/${href}`; + } + } + + // Fix double slashes + href = href.replace(/([^:\/]|^)\/{2,}/g, '$1/'); + + return ( + + {children} + + ); +}; + +export const LinkText = ({ href, children, ...props }: LinkComponentProps) => { + return {children}; +}; diff --git a/utils/i18nPaths.ts b/utils/i18nPaths.ts index 138b598cea6..3f26d2e9684 100644 --- a/utils/i18nPaths.ts +++ b/utils/i18nPaths.ts @@ -1,12 +1,17 @@ -const i18nPaths = { +interface I18nPaths { + [key: string]: string[]; + } + + const i18nPaths: I18nPaths = { en: [ - "", //Homepage Route - "/tools/cli" + "", // Homepage Route + "/tools/cli" ], de: [ - "", //Homepage Route - "/tools/cli" + "", // Homepage Route + "/tools/cli" ] -}; - -export default i18nPaths; \ No newline at end of file + }; + + export default i18nPaths; + \ No newline at end of file From 3c824af050773e6aff80914e909016d96d30bab4 Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Sun, 10 Mar 2024 23:26:46 +0530 Subject: [PATCH 02/63] added few component --- components/AsyncAPILogo.tsx | 63 +++++++++++++++++ components/ClickableLogo.tsx | 19 +++++ components/data/bucket.tsx | 89 ++++++++++++++++++++++++ components/link.tsx | 2 +- components/navigation/CommunityPanel.tsx | 6 ++ components/navigation/FlyoutMenu.tsx | 20 ++++++ components/navigation/Label.tsx | 25 +++++++ components/navigation/MenuBlocks.tsx | 49 +++++++++++++ components/navigation/communityItems.tsx | 19 +++++ types/navigation/MenuBlocks.ts | 13 ++++ 10 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 components/AsyncAPILogo.tsx create mode 100644 components/ClickableLogo.tsx create mode 100644 components/data/bucket.tsx create mode 100644 components/navigation/CommunityPanel.tsx create mode 100644 components/navigation/FlyoutMenu.tsx create mode 100644 components/navigation/Label.tsx create mode 100644 components/navigation/MenuBlocks.tsx create mode 100644 components/navigation/communityItems.tsx create mode 100644 types/navigation/MenuBlocks.ts diff --git a/components/AsyncAPILogo.tsx b/components/AsyncAPILogo.tsx new file mode 100644 index 00000000000..ea4149bfbf5 --- /dev/null +++ b/components/AsyncAPILogo.tsx @@ -0,0 +1,63 @@ +interface classType{ + className : string; +} + +export default function AsyncAPILogo({className}:classType) { + return ( + + AsyncAPI Logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) + } \ No newline at end of file diff --git a/components/ClickableLogo.tsx b/components/ClickableLogo.tsx new file mode 100644 index 00000000000..e3147588df3 --- /dev/null +++ b/components/ClickableLogo.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import Link from 'next/link'; +import AsyncAPILogo from './AsyncAPILogo'; + +interface ClickableLogoProps { + href?: string; + className?: string; + logoClassName?: string; +} + +export default function ClickableLogo({ href = '/', className = 'flex', logoClassName }: ClickableLogoProps){ + return ( + + + + + + ); +} diff --git a/components/data/bucket.tsx b/components/data/bucket.tsx new file mode 100644 index 00000000000..a79cec05804 --- /dev/null +++ b/components/data/bucket.tsx @@ -0,0 +1,89 @@ +import IconGettingStarted from '../icons/GettingStarted' +import IconTutorials from '../icons/Tutorials' +import IconUseCases from '../icons/UseCases' +import IconGuide from '../icons/Guide' +import IconSpec from '../icons/Spec' +import IconUsers from '../icons/Users' +import IconMigration from '../icons/Migration' + +interface Bucket { + name: string; + title: string; + description: string; + link: string; + className: string; + borderClassName: string; + icon: React.ComponentType>; +} + +const buckets: Bucket[] = [ + { + name: 'concepts', + title: 'Concepts', + description: 'Our Concepts section defines the concepts of AsyncAPI features and capabilities.', + link: '/docs/concepts', + className: 'bg-secondary-200', + borderClassName: 'border-secondary-200', + icon: IconGettingStarted, + }, + { + name: 'tutorials', + title: 'Tutorials', + description: 'Our Tutorials section teaches beginner processes with AsyncAPI, guiding you from Point A to Point B.', + link: '/docs/tutorials', + className: 'bg-pink-100', + borderClassName: 'border-pink-100', + icon: IconTutorials, + }, + { + name: 'guides', + title: 'Guides', + description: "Our Guides section teaches AsyncAPI's capabilities at a high level.", + link: '/docs/guides', + className: 'bg-primary-200', + borderClassName: 'border-primary-200', + icon: IconGuide, + }, + { + name: 'tools', + title: 'Tools', + description: 'Our Tools section documents the AsyncAPI tools ecosystem.', + link: '/docs/tools', + className: 'bg-green-200', + borderClassName: 'border-green-200', + icon: IconUseCases, + }, + { + name: 'reference', + title: 'Reference', + description: 'Our Reference section documents the AsyncAPI specification.', + link: '/docs/reference', + className: 'bg-yellow-200', + borderClassName: 'border-yellow-200', + icon: IconSpec, + }, + { + name: 'migration', + title: 'Migration', + description: 'Our migration guides on how to upgrade to newer AsyncAPI versions.', + link: '/docs/migration', + className: 'bg-blue-400', + borderClassName: 'border-blue-400', + icon: IconMigration, + }, + { + name: 'community', + title: 'Community', + description: 'Our Community section documents the community guidelines and resources.', + link: '/docs/community', + className: 'bg-orange-200', + borderClassName: 'border-orange-200', + icon: IconUsers, + }, +].map(bucket => ({ + ...bucket, + href: bucket.link, + icon: bucket.icon, +})); + +export { buckets }; diff --git a/components/link.tsx b/components/link.tsx index 3558c18ed99..584fd55d373 100644 --- a/components/link.tsx +++ b/components/link.tsx @@ -9,7 +9,7 @@ interface LinkComponentProps { href?: string; } -export default function LinkComponent ({ children, locale, ...props }: LinkComponentProps){ +export default function LinkComponent({ children, locale, ...props }: LinkComponentProps) { const router = useRouter(); // If there is no router available (e.g., during server-side rendering & cypress tests), render a standard Link diff --git a/components/navigation/CommunityPanel.tsx b/components/navigation/CommunityPanel.tsx new file mode 100644 index 00000000000..c8303b3a2f0 --- /dev/null +++ b/components/navigation/CommunityPanel.tsx @@ -0,0 +1,6 @@ +import FlyoutMenu from './FlyoutMenu'; +import communityItems from './communityItems'; + +export default function CommunityPanel() { + return ; +} diff --git a/components/navigation/FlyoutMenu.tsx b/components/navigation/FlyoutMenu.tsx new file mode 100644 index 00000000000..e6056c96027 --- /dev/null +++ b/components/navigation/FlyoutMenu.tsx @@ -0,0 +1,20 @@ +import MenuBlocks from './MenuBlocks'; +import { MenuItem } from '@/types/navigation/MenuBlocks'; + +interface FlyoutProps { + items?: MenuItem[]; +} + +export default function Flyout({ items = [] }: FlyoutProps){ + return ( +
+
+
+
+ +
+
+
+
+ ); +} diff --git a/components/navigation/Label.tsx b/components/navigation/Label.tsx new file mode 100644 index 00000000000..be4d7836f21 --- /dev/null +++ b/components/navigation/Label.tsx @@ -0,0 +1,25 @@ +interface LabelProps { + text: string; + color?: 'gray' | 'green'; // Define the color prop as optional with specific values +} + +export default function Label({ text, color = 'gray' }: LabelProps) { + let colorClasses: string; + + switch (color) { + case 'gray': + colorClasses = 'bg-gray-300 text-gray-700'; + break; + case 'green': + colorClasses = 'bg-green-300 text-green-700'; + break; + default: + colorClasses = 'bg-gray-300 text-gray-700'; // Default case + } + + return ( + + {text} + + ); +} diff --git a/components/navigation/MenuBlocks.tsx b/components/navigation/MenuBlocks.tsx new file mode 100644 index 00000000000..d5c131e5df2 --- /dev/null +++ b/components/navigation/MenuBlocks.tsx @@ -0,0 +1,49 @@ +import LinkComponent from '../link'; +import Paragraph from '../typography/Paragraph'; +import Label from './Label'; +import { useRouter } from 'next/router'; +import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; +import { MenuBlocksProps } from '@/types/navigation/MenuBlocks'; + +export default function MenuBlocks({ items = [] }: MenuBlocksProps) { + const router = useRouter(); + + return ( + <> + {items.map((item, index) => { + const isExternalHref = item.href && item.href.startsWith('http'); + + return ( + + +
+ +
+
+ + {item.title}{' '} + {item.comingSoon && + + {item.description} + +
+
+
+ ); + })} + + ); +} diff --git a/components/navigation/communityItems.tsx b/components/navigation/communityItems.tsx new file mode 100644 index 00000000000..be3bcc26278 --- /dev/null +++ b/components/navigation/communityItems.tsx @@ -0,0 +1,19 @@ +import IconGithubOrganization from '../icons/GithubOrganization' +import IconSlack from '../icons/Slack' +import IconContributing from '../icons/Contributing' +import IconDashboard from '../icons/Dashboard' +import IconTSC from '../icons/TSC' +import IconMeetings from '../icons/Meetings' +import IconNewsroom from '../icons/Newsroom' +import Ambassador from '../icons/Ambassador' + +export default [ + { icon: IconGithubOrganization, title: 'GitHub Organization', href: 'https://github.com/asyncapi', target: '_blank', description: 'Want to sneak in the code? Everything we do is open-sourced in our GitHub organization.' }, + { icon: IconSlack, title: 'Slack Workspace', href: 'https://asyncapi.com/slack-invite', target: '_blank', description: `Need help? Want to share something? Join our Slack workspace. We're nice people :)` }, + { icon: IconContributing, title: 'Contributing', href: 'https://github.com/asyncapi?type=source#-contribute-to-asyncapi', target: '_blank', description: `We are always welcoming and looking for contributions. If you are interested check out our contribution guide.` }, + { icon: IconTSC, title: 'Technical Steering Committee', href: '/community/tsc', description: 'Get to know what is a TSC member, how you can become one, and meet our current TSC members.' }, + { icon: Ambassador, title: 'Ambassadors', href: '/community/ambassadors', description: 'Passionate about APIs? Become an AsyncAPI Ambassador and help shape the future of APIs.' }, + { icon: IconDashboard, title: 'Dashboard',href: '/community/dashboard', description: `Just need a good first issue to start your contribution journey? or want to see what topics are hot in discussion?`}, + { icon: IconMeetings, title: 'Events', href: '/community/events', description: 'See what events and meetings are organized under AsyncAPI umbrella and join one of them.' }, + { icon: IconNewsroom, title: 'Newsroom', href: '/community/newsroom', description: 'Get upto date with the recent activity in the initiative.' }, + ] \ No newline at end of file diff --git a/types/navigation/MenuBlocks.ts b/types/navigation/MenuBlocks.ts new file mode 100644 index 00000000000..ebe2e62efe7 --- /dev/null +++ b/types/navigation/MenuBlocks.ts @@ -0,0 +1,13 @@ +export interface MenuItem { + title: string; + href: string; + description: string; + icon: React.ElementType; + className?: string; + comingSoon?: boolean; + beta?: boolean; +} + +export interface MenuBlocksProps { + items?: MenuItem[]; +} \ No newline at end of file From 83444af2c2e23ea204741191387ecf5029733f2c Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Mon, 11 Mar 2024 00:00:14 +0530 Subject: [PATCH 03/63] fixed errors --- components/data/bucket.tsx | 89 -------------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 components/data/bucket.tsx diff --git a/components/data/bucket.tsx b/components/data/bucket.tsx deleted file mode 100644 index a79cec05804..00000000000 --- a/components/data/bucket.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import IconGettingStarted from '../icons/GettingStarted' -import IconTutorials from '../icons/Tutorials' -import IconUseCases from '../icons/UseCases' -import IconGuide from '../icons/Guide' -import IconSpec from '../icons/Spec' -import IconUsers from '../icons/Users' -import IconMigration from '../icons/Migration' - -interface Bucket { - name: string; - title: string; - description: string; - link: string; - className: string; - borderClassName: string; - icon: React.ComponentType>; -} - -const buckets: Bucket[] = [ - { - name: 'concepts', - title: 'Concepts', - description: 'Our Concepts section defines the concepts of AsyncAPI features and capabilities.', - link: '/docs/concepts', - className: 'bg-secondary-200', - borderClassName: 'border-secondary-200', - icon: IconGettingStarted, - }, - { - name: 'tutorials', - title: 'Tutorials', - description: 'Our Tutorials section teaches beginner processes with AsyncAPI, guiding you from Point A to Point B.', - link: '/docs/tutorials', - className: 'bg-pink-100', - borderClassName: 'border-pink-100', - icon: IconTutorials, - }, - { - name: 'guides', - title: 'Guides', - description: "Our Guides section teaches AsyncAPI's capabilities at a high level.", - link: '/docs/guides', - className: 'bg-primary-200', - borderClassName: 'border-primary-200', - icon: IconGuide, - }, - { - name: 'tools', - title: 'Tools', - description: 'Our Tools section documents the AsyncAPI tools ecosystem.', - link: '/docs/tools', - className: 'bg-green-200', - borderClassName: 'border-green-200', - icon: IconUseCases, - }, - { - name: 'reference', - title: 'Reference', - description: 'Our Reference section documents the AsyncAPI specification.', - link: '/docs/reference', - className: 'bg-yellow-200', - borderClassName: 'border-yellow-200', - icon: IconSpec, - }, - { - name: 'migration', - title: 'Migration', - description: 'Our migration guides on how to upgrade to newer AsyncAPI versions.', - link: '/docs/migration', - className: 'bg-blue-400', - borderClassName: 'border-blue-400', - icon: IconMigration, - }, - { - name: 'community', - title: 'Community', - description: 'Our Community section documents the community guidelines and resources.', - link: '/docs/community', - className: 'bg-orange-200', - borderClassName: 'border-orange-200', - icon: IconUsers, - }, -].map(bucket => ({ - ...bucket, - href: bucket.link, - icon: bucket.icon, -})); - -export { buckets }; From a64976177da269d5f115b9a5c650d063919d9832 Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Mon, 11 Mar 2024 18:22:26 +0530 Subject: [PATCH 04/63] docnavitem , subcategory, docsbav added --- components/data/buckets.tsx | 105 +++++++++++++++++++ components/navigation/DocsNav.tsx | 90 ++++++++++++++++ components/navigation/DocsNavItem.tsx | 63 +++++++++++ components/navigation/SubCategoryDocsNav.tsx | 82 +++++++++++++++ 4 files changed, 340 insertions(+) create mode 100644 components/data/buckets.tsx create mode 100644 components/navigation/DocsNav.tsx create mode 100644 components/navigation/DocsNavItem.tsx create mode 100644 components/navigation/SubCategoryDocsNav.tsx diff --git a/components/data/buckets.tsx b/components/data/buckets.tsx new file mode 100644 index 00000000000..2a33296db0a --- /dev/null +++ b/components/data/buckets.tsx @@ -0,0 +1,105 @@ +import IconGettingStarted from '../icons/GettingStarted'; +import IconTutorials from '../icons/Tutorials'; +import IconUseCases from '../icons/UseCases'; +import IconGuide from '../icons/Guide'; +import IconSpec from '../icons/Spec'; +import IconUsers from '../icons/Users'; +import IconMigration from '../icons/Migration'; + +interface Bucket { + name: string; + title: string; + description: string; + link: string; + className: string; + borderClassName: string; + Icon: React.ComponentType; + href: string; + icon: React.ComponentType; +} + +export const buckets: Bucket[] = [ + { + name: 'concepts', + title: 'Concepts', + description: 'Our Concepts section defines the concepts of AsyncAPI features and capabilities.', + link: '/docs/concepts', + className: 'bg-secondary-200', + borderClassName: 'border-secondary-200', + Icon: IconGettingStarted, + href: '/docs/concepts', + icon: IconGettingStarted, + }, + { + name: 'tutorials', + title: 'Tutorials', + description: 'Our Tutorials section teaches beginner processes with AsyncAPI, guiding you from Point A to Point B.', + link: '/docs/tutorials', + className: 'bg-pink-100', + borderClassName: 'border-pink-100', + Icon: IconTutorials, + href: '/docs/tutorials', + icon: IconTutorials, + }, + { + name: 'guides', + title: 'Guides', + description: "Our Guides section teaches AsyncAPI's capabilities at a high level.", + link: '/docs/guides', + className: 'bg-primary-200', + borderClassName: 'border-primary-200', + Icon: IconGuide, + href: '/docs/guides', + icon: IconGuide, + }, + { + name: 'tools', + title: 'Tools', + description: 'Our Tools section documents the AsyncAPI tools ecosystem.', + link: '/docs/tools', + className: 'bg-green-200', + borderClassName: 'border-green-200', + Icon: IconUseCases, + href: '/docs/tools', + icon: IconUseCases, + }, + { + name: 'reference', + title: 'Reference', + description: 'Our Reference section documents the AsyncAPI specification.', + link: '/docs/reference', + className: 'bg-yellow-200', + borderClassName: 'border-yellow-200', + Icon: IconSpec, + href: '/docs/reference', + icon: IconSpec, + }, + { + name: 'migration', + title: 'Migration', + description: 'Our migration guides on how to upgrade to newer AsyncAPI versions.', + link: '/docs/migration', + className: 'bg-blue-400', + borderClassName: 'border-blue-400', + Icon: IconMigration, + href: '/docs/migration', + icon: IconMigration, + }, + { + name: 'community', + title: 'Community', + description: 'Our Community section documents the community guidelines and resources.', + link: '/docs/community', + className: 'bg-orange-200', + borderClassName: 'border-orange-200', + Icon: IconUsers, + href: '/docs/community', + icon: IconUsers, + }, +].map(bucket => { + return { + ...bucket, + href: bucket.link, + icon: bucket.Icon, + }; +}); diff --git a/components/navigation/DocsNav.tsx b/components/navigation/DocsNav.tsx new file mode 100644 index 00000000000..3425f58f758 --- /dev/null +++ b/components/navigation/DocsNav.tsx @@ -0,0 +1,90 @@ +import React, { useState, useEffect } from 'react'; +import DocsNavItem from './DocsNavItem'; +import IconHome from '../icons/Home'; +import SubCategoryDocsNav from './SubCategoryDocsNav'; +import DocsArrow from '../icons/DocsArrow'; +import { buckets } from '../data/buckets'; + +interface Bucket { + name?: string; + title?: string; + description?: string; + link?: string; + className?: string; + borderClassName?: string; + Icon?: React.ComponentType; + href?: string; + icon?: React.ComponentType | null; +} + +interface SerializedBuckets { + [key: string]: Bucket; +} + +const serializedBuckets: SerializedBuckets = buckets.reduce((acc, bucket) => { + acc[bucket.name || ''] = { + ...bucket, + className: `${bucket.className || ''} ${bucket.borderClassName || ''}`, + }; + return acc; +}, { + welcome: { + icon: IconHome, + className: 'bg-gray-300 border-gray-300', + }, +} as SerializedBuckets); + +interface DocsNavProps { + item: { + children: { + [key: string]: any; + }; + item: { + rootSectionId: string; + slug: string; + title: string; + }; + }; + active: string; + onClick?: () => void; +} + +const DocsNav: React.FC = ({ + item, + active, + onClick = () => {}, +}) => { + const subCategories = item.children; + const bucket = serializedBuckets[item.item.rootSectionId]; + const [openSubCategory, setOpenSubCategory] = useState(active.startsWith(item.item.slug)); + + const onClickHandler = () => { + setOpenSubCategory(!openSubCategory); + onClick(); + } + + useEffect(() => { + setOpenSubCategory(active.startsWith(item.item.slug)); + }, [active]) + + return ( +
  • +
    + 0} activeDropDownItem={openSubCategory} onClick={() => setOpenSubCategory(!openSubCategory)} /> + +
    + {openSubCategory && ( +
      + {Object.values(subCategories).map((subCategory: any) => ( + + ))} +
    + )} +
  • + ); +} + +export default DocsNav; diff --git a/components/navigation/DocsNavItem.tsx b/components/navigation/DocsNavItem.tsx new file mode 100644 index 00000000000..4e0f7151c7e --- /dev/null +++ b/components/navigation/DocsNavItem.tsx @@ -0,0 +1,63 @@ +import Link from 'next/link'; + +function isActiveSlug(slug: string, activeSlug: string, sectionSlug?: string): boolean { + if (slug === '/docs' || (sectionSlug !== undefined && slug === sectionSlug)) { + return slug === activeSlug; + } + + const partialSlug = slug.split('/'); + const partialActiveSlug = activeSlug.split('/'); + const activeParts = partialActiveSlug.filter((a, idx) => a === partialSlug[idx]); + return activeParts.length === partialSlug.length; +} + +interface DocsNavItemProps { + title: string; + slug: string; + href?: string; + activeSlug: string; + sectionSlug?: string; + onClick?: () => void; + defaultClassName?: string; + inactiveClassName?: string; + activeClassName?: string; + bucket?: { + className: string; + icon: React.ComponentType; + }; +} + +const DocsNavItem: React.FC = ({ + title, + slug, + href, + activeSlug, + sectionSlug, + onClick = () => {}, + defaultClassName = '', + inactiveClassName = '', + activeClassName = '', + bucket, +}) => { + const isActive = isActiveSlug(slug, activeSlug, sectionSlug); + const classes = `${isActive ? activeClassName : inactiveClassName} ${defaultClassName} inline-block w-full`; + + return ( + + ); +}; + +export default DocsNavItem; diff --git a/components/navigation/SubCategoryDocsNav.tsx b/components/navigation/SubCategoryDocsNav.tsx new file mode 100644 index 00000000000..147fdbefb06 --- /dev/null +++ b/components/navigation/SubCategoryDocsNav.tsx @@ -0,0 +1,82 @@ +import { useState, useEffect } from "react"; +import DocsNavItem from "./DocsNavItem"; +import DocsArrow from "../icons/DocsArrow"; + +interface SubCategoryDocsNavProps { + subCategory: { + item: { + title: string; + slug: string; + href?: string; + }; + children?: { + title: string; + slug: string; + href?: string; + }[]; + }; + activeItem: string; + onClick: () => void; +} + +export default function SubCategoryDocsNav({ + subCategory, + activeItem, + onClick, +}: SubCategoryDocsNavProps) { + const [openSubCategoryChildren, setOpenSubCategoryChildren] = useState( + activeItem.startsWith(subCategory.item.slug) + ); + + const onClickHandler = () => { + setOpenSubCategoryChildren(!openSubCategoryChildren); + onClick(); + }; + + useEffect(() => { + setOpenSubCategoryChildren(activeItem.startsWith(subCategory.item.slug)); + }, [activeItem, subCategory.item.slug]); + + return ( +
  • +
    + setOpenSubCategoryChildren(!openSubCategoryChildren)} + /> + +
    + {openSubCategoryChildren && ( +
      + {subCategory.children && + subCategory.children.map((subItem) => ( +
    • + +
    • + ))} +
    + )} +
  • + ); +} From f1be895a02fac8812ba0c4deae22c65f82ae1461 Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Mon, 11 Mar 2024 18:26:01 +0530 Subject: [PATCH 05/63] separated types --- components/navigation/DocsNav.tsx | 32 +------------------- components/navigation/DocsNavItem.tsx | 17 +---------- components/navigation/SubCategoryDocsNav.tsx | 18 +---------- types/navigation/DocsNav.ts | 30 ++++++++++++++++++ types/navigation/DocsNavItem.ts | 15 +++++++++ types/navigation/SubCategoryDocsNav.ts | 16 ++++++++++ 6 files changed, 64 insertions(+), 64 deletions(-) create mode 100644 types/navigation/DocsNav.ts create mode 100644 types/navigation/DocsNavItem.ts create mode 100644 types/navigation/SubCategoryDocsNav.ts diff --git a/components/navigation/DocsNav.tsx b/components/navigation/DocsNav.tsx index 3425f58f758..51d3a7c23d7 100644 --- a/components/navigation/DocsNav.tsx +++ b/components/navigation/DocsNav.tsx @@ -4,22 +4,7 @@ import IconHome from '../icons/Home'; import SubCategoryDocsNav from './SubCategoryDocsNav'; import DocsArrow from '../icons/DocsArrow'; import { buckets } from '../data/buckets'; - -interface Bucket { - name?: string; - title?: string; - description?: string; - link?: string; - className?: string; - borderClassName?: string; - Icon?: React.ComponentType; - href?: string; - icon?: React.ComponentType | null; -} - -interface SerializedBuckets { - [key: string]: Bucket; -} +import { SerializedBuckets , DocsNavProps } from '@/types/navigation/DocsNav'; const serializedBuckets: SerializedBuckets = buckets.reduce((acc, bucket) => { acc[bucket.name || ''] = { @@ -34,21 +19,6 @@ const serializedBuckets: SerializedBuckets = buckets.reduce((acc, bucket) => { }, } as SerializedBuckets); -interface DocsNavProps { - item: { - children: { - [key: string]: any; - }; - item: { - rootSectionId: string; - slug: string; - title: string; - }; - }; - active: string; - onClick?: () => void; -} - const DocsNav: React.FC = ({ item, active, diff --git a/components/navigation/DocsNavItem.tsx b/components/navigation/DocsNavItem.tsx index 4e0f7151c7e..66136aaad37 100644 --- a/components/navigation/DocsNavItem.tsx +++ b/components/navigation/DocsNavItem.tsx @@ -1,4 +1,5 @@ import Link from 'next/link'; +import { DocsNavItemProps } from '@/types/navigation/DocsNavItem'; function isActiveSlug(slug: string, activeSlug: string, sectionSlug?: string): boolean { if (slug === '/docs' || (sectionSlug !== undefined && slug === sectionSlug)) { @@ -11,22 +12,6 @@ function isActiveSlug(slug: string, activeSlug: string, sectionSlug?: string): b return activeParts.length === partialSlug.length; } -interface DocsNavItemProps { - title: string; - slug: string; - href?: string; - activeSlug: string; - sectionSlug?: string; - onClick?: () => void; - defaultClassName?: string; - inactiveClassName?: string; - activeClassName?: string; - bucket?: { - className: string; - icon: React.ComponentType; - }; -} - const DocsNavItem: React.FC = ({ title, slug, diff --git a/components/navigation/SubCategoryDocsNav.tsx b/components/navigation/SubCategoryDocsNav.tsx index 147fdbefb06..aca710ae1da 100644 --- a/components/navigation/SubCategoryDocsNav.tsx +++ b/components/navigation/SubCategoryDocsNav.tsx @@ -1,23 +1,7 @@ import { useState, useEffect } from "react"; import DocsNavItem from "./DocsNavItem"; import DocsArrow from "../icons/DocsArrow"; - -interface SubCategoryDocsNavProps { - subCategory: { - item: { - title: string; - slug: string; - href?: string; - }; - children?: { - title: string; - slug: string; - href?: string; - }[]; - }; - activeItem: string; - onClick: () => void; -} +import { SubCategoryDocsNavProps } from "@/types/navigation/SubCategoryDocsNav"; export default function SubCategoryDocsNav({ subCategory, diff --git a/types/navigation/DocsNav.ts b/types/navigation/DocsNav.ts new file mode 100644 index 00000000000..31e4a03291c --- /dev/null +++ b/types/navigation/DocsNav.ts @@ -0,0 +1,30 @@ +export interface Bucket { + name?: string; + title?: string; + description?: string; + link?: string; + className?: string; + borderClassName?: string; + Icon?: React.ComponentType; + href?: string; + icon?: React.ComponentType | null; +} + +export interface SerializedBuckets { + [key: string]: Bucket; +} + +export interface DocsNavProps { + item: { + children: { + [key: string]: any; + }; + item: { + rootSectionId: string; + slug: string; + title: string; + }; + }; + active: string; + onClick?: () => void; +} \ No newline at end of file diff --git a/types/navigation/DocsNavItem.ts b/types/navigation/DocsNavItem.ts new file mode 100644 index 00000000000..b736c01d3a0 --- /dev/null +++ b/types/navigation/DocsNavItem.ts @@ -0,0 +1,15 @@ +export interface DocsNavItemProps { + title: string; + slug: string; + href?: string; + activeSlug: string; + sectionSlug?: string; + onClick?: () => void; + defaultClassName?: string; + inactiveClassName?: string; + activeClassName?: string; + bucket?: { + className: string; + icon: React.ComponentType; + }; +} \ No newline at end of file diff --git a/types/navigation/SubCategoryDocsNav.ts b/types/navigation/SubCategoryDocsNav.ts new file mode 100644 index 00000000000..660cbacb546 --- /dev/null +++ b/types/navigation/SubCategoryDocsNav.ts @@ -0,0 +1,16 @@ +export interface SubCategoryDocsNavProps { + subCategory: { + item: { + title: string; + slug: string; + href?: string; + }; + children?: { + title: string; + slug: string; + href?: string; + }[]; + }; + activeItem: string; + onClick: () => void; + } \ No newline at end of file From 84a11bc6e780598052b2b39d19e3c7141f12269f Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Mon, 11 Mar 2024 18:31:20 +0530 Subject: [PATCH 06/63] converted functions --- components/navigation/DocsNav.tsx | 6 ++---- components/navigation/DocsNavItem.tsx | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/components/navigation/DocsNav.tsx b/components/navigation/DocsNav.tsx index 51d3a7c23d7..3d5d270f7d3 100644 --- a/components/navigation/DocsNav.tsx +++ b/components/navigation/DocsNav.tsx @@ -19,11 +19,11 @@ const serializedBuckets: SerializedBuckets = buckets.reduce((acc, bucket) => { }, } as SerializedBuckets); -const DocsNav: React.FC = ({ +export default function DocsNav ({ item, active, onClick = () => {}, -}) => { +}:DocsNavProps ) { const subCategories = item.children; const bucket = serializedBuckets[item.item.rootSectionId]; const [openSubCategory, setOpenSubCategory] = useState(active.startsWith(item.item.slug)); @@ -56,5 +56,3 @@ const DocsNav: React.FC = ({ ); } - -export default DocsNav; diff --git a/components/navigation/DocsNavItem.tsx b/components/navigation/DocsNavItem.tsx index 66136aaad37..e16efbbfcbc 100644 --- a/components/navigation/DocsNavItem.tsx +++ b/components/navigation/DocsNavItem.tsx @@ -12,7 +12,7 @@ function isActiveSlug(slug: string, activeSlug: string, sectionSlug?: string): b return activeParts.length === partialSlug.length; } -const DocsNavItem: React.FC = ({ +export default function DocsNavItem ({ title, slug, href, @@ -23,7 +23,7 @@ const DocsNavItem: React.FC = ({ inactiveClassName = '', activeClassName = '', bucket, -}) => { +}: DocsNavItemProps) { const isActive = isActiveSlug(slug, activeSlug, sectionSlug); const classes = `${isActive ? activeClassName : inactiveClassName} ${defaultClassName} inline-block w-full`; @@ -44,5 +44,3 @@ const DocsNavItem: React.FC = ({ ); }; - -export default DocsNavItem; From 4e764b614460bf0c882d3cea06df0ee3d18e8ecb Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Mon, 11 Mar 2024 18:39:17 +0530 Subject: [PATCH 07/63] docs mobile menu added --- components/navigation/DocsMobileMenu.tsx | 77 ++++++++++++++++++++++++ components/navigation/DocsNav.tsx | 2 +- types/navigation/DocsMobileMenu.ts | 9 +++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 components/navigation/DocsMobileMenu.tsx create mode 100644 types/navigation/DocsMobileMenu.ts diff --git a/components/navigation/DocsMobileMenu.tsx b/components/navigation/DocsMobileMenu.tsx new file mode 100644 index 00000000000..fa64b78d5fc --- /dev/null +++ b/components/navigation/DocsMobileMenu.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import ClickableLogo from '../ClickableLogo'; +import DocsNav from './DocsNav'; +import { SearchButton, DOCS_INDEX_NAME } from '../AlgoliaSearch'; +import IconLoupe from '../icons/Loupe'; +import { DocsMobileMenuProps } from '@/types/navigation/DocsMobileMenu'; + +export default function DocsMobileMenu ({ + post, + navigation, + onClickClose = () => {}, +}:DocsMobileMenuProps) { + return ( +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    +
    + + + Search docs... + +
    + +
    +
    +
    + {/* Force sidebar to shrink to fit close icon */} +
    +
    +
    + ); +}; diff --git a/components/navigation/DocsNav.tsx b/components/navigation/DocsNav.tsx index 3d5d270f7d3..6e7768d8f2e 100644 --- a/components/navigation/DocsNav.tsx +++ b/components/navigation/DocsNav.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import DocsNavItem from './DocsNavItem'; import IconHome from '../icons/Home'; import SubCategoryDocsNav from './SubCategoryDocsNav'; diff --git a/types/navigation/DocsMobileMenu.ts b/types/navigation/DocsMobileMenu.ts new file mode 100644 index 00000000000..407eab715cd --- /dev/null +++ b/types/navigation/DocsMobileMenu.ts @@ -0,0 +1,9 @@ +export interface DocsMobileMenuProps { + post: { + slug: string; + }; + navigation: { + [key: string]: any; + }; + onClickClose?: () => void; +} \ No newline at end of file From eef5c569be62618e08e36ccc2a54ed70ec20c95d Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Mon, 11 Mar 2024 19:15:18 +0530 Subject: [PATCH 08/63] event post added --- components/navigation/EventFilter.tsx | 62 +++++++++++++++++ components/navigation/EventPostItem.tsx | 92 +++++++++++++++++++++++++ config/adopters.json | 2 +- types/@heroicons/react/outline.d.ts | 1 + 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 components/navigation/EventFilter.tsx create mode 100644 components/navigation/EventPostItem.tsx create mode 100644 types/@heroicons/react/outline.d.ts diff --git a/components/navigation/EventFilter.tsx b/components/navigation/EventFilter.tsx new file mode 100644 index 00000000000..a2465a90535 --- /dev/null +++ b/components/navigation/EventFilter.tsx @@ -0,0 +1,62 @@ +import React, { useEffect, useState } from 'react'; +import moment from 'moment'; +import { getEvents } from '../../utils/staticHelpers'; + +interface Event { + date: moment.Moment; +} + +interface EventFilterProps { + data: Event[]; + setData: React.Dispatch>; +} + +function EventFilter({ data, setData }: EventFilterProps) { + const localTime = moment().format('YYYY-MM-DD'); + const currentDate = localTime + 'T00:00:00.000Z'; + const filterList: string[] = ['All', 'Upcoming', 'Recorded']; + const [active, setActive] = useState('All'); + + useEffect(() => { + switch (active) { + case 'All': + setData(getEvents(data)); + break; + case 'Upcoming': + setData( + getEvents(data).filter((a: Event) => { + return a.date.format() > currentDate; + }) + ); + break; + case 'Recorded': + setData( + getEvents(data).filter((a: Event) => { + return a.date.format() < currentDate; + }) + ); + break; + default: + break; + } + }, [active, data, setData, currentDate]); + + return ( +
    + {filterList.map((list) => ( +
    setActive(list)} + > + {list} +
    + ))} +
    + ); +} + +export default EventFilter; diff --git a/components/navigation/EventPostItem.tsx b/components/navigation/EventPostItem.tsx new file mode 100644 index 00000000000..bdefb2c1734 --- /dev/null +++ b/components/navigation/EventPostItem.tsx @@ -0,0 +1,92 @@ +import { ArrowRightIcon } from '@heroicons/react/outline'; +import React from 'react'; +import moment from 'moment'; +import IconCalendar from '../icons/Calendar'; +import Community from '../icons/Community'; +import Conference from '../icons/Conference'; +import Webinar from '../icons/Webinar'; +import Heading from '../typography/Heading'; +import { HeadingLevel , HeadingTypeStyle} from '@/types/typography/Heading'; +interface Event { + title: string; + url: string; + banner?: string; + date: string; +} + +interface EventPostItemProps { + post: Event; + className?: string; + id: string; +} + +function EventPostItem({ post, className = '', id }: EventPostItemProps): JSX.Element { + const localTime = moment().format('YYYY-MM-DD'); // store localTime + const currentDate = localTime + 'T00:00:00.000Z'; + const title = post.title || ''; + let color = ''; + let icon = <>; + let type = ''; + + if (title.includes('community')) { + icon = ; + color = 'text-green-800'; + type = 'COMMUNITY'; + } else if (title.includes('conference')) { + icon = ; + color = 'text-orange-800'; + type = 'CONFERENCE'; + } else if (title.includes('workshop')) { + icon = ; + color = 'text-blue-400'; + type = 'WORKSHOP'; + } + + const defaultCover = '/img/homepage/confBlurBg.webp'; + let active = true; + let postDate = moment(post.date); // Convert post.date to a moment object if necessary + + if (!postDate.isValid()) { + // Handle invalid date if necessary + active = false; + } else if (currentDate > postDate.format()) { + active = false; + } + + return ( +
  • + +
  • + ); +} + +export default EventPostItem; diff --git a/config/adopters.json b/config/adopters.json index e92e3ed5865..18985428bb4 100644 --- a/config/adopters.json +++ b/config/adopters.json @@ -1 +1 @@ -[{"companyName":"Reiffeisen Bank","useCase":"Continuous Integration and Continuous Delivery (CI/CD) pipeline based on GitOps to deploy a topology built on Async API definitions using a Kubernetes operator to an Apache Pulsar cluster.","resources":[{"title":"Video - From an AsyncAPI Definition to a Deployed Pulsar Topology Via GitOps","link":"https://www.youtube.com/watch?v=_MwzLZMwFN8"}]},{"companyName":"LEGO Group","useCase":"Broker management, where developers do not access the management console themselves, but rely on uploading AsyncAPI documents to a self service API that provisions access and topics specified in documents.","resources":[{"title":"Video - Documentation as Configuration for Management of Apache Pulsar","link":"https://www.youtube.com/watch?v=m8I0fYjx6Cc"}]},{"companyName":"LEGO Group","useCase":"Define, document and distribute event-driven APIs. Ensuring consistency and governance","resources":[{"title":"Video - Cross-Domain Events with AsyncAPI and AWS","link":"https://www.youtube.com/watch?v=qjarcJQVLOg"}]},{"companyName":"Bank of New Zealand","useCase":"Decentralized company-wide governance strategy for API. A self service for publishing APIs and docs.","resources":[{"title":"Video - Self-service Events & Decentralised Governance with AsyncAPI: A Real World Example","link":"https://www.confluent.io/events/kafka-summit-apac-2021/self-service-events-and-decentralised-governance-with-asyncapi-a-real-world/"}]},{"companyName":"Zora Robotics","useCase":"Documenting lot products public MQTT API and building a developers portal.","resources":[{"title":"Video - Buliding and managing an extensive API for Robotics and loT","link":"https://www.youtube.com/watch?v=yjHgT0n2BtA"},{"title":"Docs - Buliding and managing an extensive API for Robotics and loT","link":"https://docs.zorabots.be/dev-mqtt-docs/latest/index.html"}]},{"companyName":"Walmart","useCase":"Managing a central API Hub for internal teams. Using AsyncAPI for events discoverability an visibility in a single place. AsyncAPI also enabled company-wide governance on asynchronous APIs.","resources":[{"title":"Video - Time For AsyncAPI Specification","link":"https://www.youtube.com/watch?v=SxTpGRaNIPo"}]},{"companyName":"eBay","useCase":"Enabling partners to build with eBay through asynchronous communication. Public AsyncAPI documents enable code generation and faster integration. It also enables governance and standardisation.","resources":[{"title":"Video - AsyncAPI 2.0: Enabling the Event-Driven World","link":"https://www.youtube.com/watch?v=SxTpGRaNIPo"},{"title":"Article - AsyncAPI 2.0: Enabling the Event-Driven World","link":"https://innovation.ebayinc.com/tech/engineering/asyncapi-2-0-enabling-the-event-driven-world/"},{"title":"Docs - Overview of Notification API with public AsyncAPI documents","link":"https://developer.ebay.com/api-docs/commerce/notification/overview.html"}]}] \ No newline at end of file +[{"companyName":"Reiffeisen Bank","useCase":"Implementing a Continuous Integration and Continuous Delivery (CI/CD) pipeline utilizing GitOps principles to deploy a topology constructed on AsyncAPI definitions using a Kubernetes operator to an Apache Pulsar cluster.","resources":[{"title":"Video: From an AsyncAPI Definition to a Deployed Pulsar Topology Via GitOps","link":"https://www.youtube.com/watch?v=_MwzLZMwFN8"}]},{"companyName":"LEGO Group","useCase":"Managing brokers, where developers abstain from direct access to the management console and instead upload AsyncAPI documents to a self-service API, which provisions access and topics specified in the documents.","resources":[{"title":"Video: Documentation as Configuration for Management of Apache Pulsar","link":"https://www.youtube.com/watch?v=m8I0fYjx6Cc"}]},{"companyName":"LEGO Group","useCase":"Defining, documenting, and distributing event-driven APIs while ensuring consistency and governance.","resources":[{"title":"Video: Cross-Domain Events with AsyncAPI and AWS","link":"https://www.youtube.com/watch?v=qjarcJQVLOg"}]},{"companyName":"Bank of New Zealand","useCase":"Establishing a decentralized company-wide governance strategy for APIs, providing a self-service platform for publishing APIs and documentation.","resources":[{"title":"Video: Self-service Events & Decentralised Governance with AsyncAPI: A Real World Example","link":"https://www.confluent.io/events/kafka-summit-apac-2021/self-service-events-and-decentralised-governance-with-asyncapi-a-real-world/"}]},{"companyName":"Zora Robotics","useCase":"Documenting public MQTT APIs for IoT products and constructing a developer portal.","resources":[{"title":"Video: Building and managing an extensive API for Robotics and IoT","link":"https://www.youtube.com/watch?v=yjHgT0n2BtA"},{"title":"Docs: Building and managing an extensive API for Robotics and IoT","link":"https://docs.zorabots.be/dev-mqtt-docs/latest/index.html"}]},{"companyName":"Walmart","useCase":"Managing a centralized API Hub for internal teams, enhancing event discoverability and visibility using AsyncAPI. AsyncAPI facilitates company-wide governance on asynchronous APIs.","resources":[{"title":"Video: Time For AsyncAPI Specification","link":"https://www.youtube.com/watch?v=SxTpGRaNIPo"}]},{"companyName":"eBay","useCase":"Facilitating partner integration with eBay through asynchronous communication, leveraging public AsyncAPI documents for code generation and rapid integration, while ensuring governance and standardization.","resources":[{"title":"Video: AsyncAPI 2.0: Enabling the Event-Driven World","link":"https://www.youtube.com/watch?v=SxTpGRaNIPo"},{"title":"Article: AsyncAPI 2.0: Enabling the Event-Driven World","link":"https://innovation.ebayinc.com/tech/engineering/asyncapi-2-0-enabling-the-event-driven-world/"},{"title":"Docs: Overview of Notification API with public AsyncAPI documents","link":"https://developer.ebay.com/api-docs/commerce/notification/overview.html"}]},{"companyName":"Postman","useCase":"Enhancing discoverability of information about system events by building a tool called Synapse for provisioning entire event-based infrastructure, with AsyncAPI documents as the source of truth.","resources":[{"title":"Video: Turbocharging your Developer Ecosystem with Events Powered by SNS/SQS, Serverless, and AsyncAPI","link":"https://www.youtube.com/watch?v=0_7QZyKLPoE"}]},{"companyName":"Adobe","useCase":"Providing event documentation to expedite development by generating classes based on message payload information from AsyncAPI documents.","resources":[{"title":"Slides: AsyncAPI and Modelina","link":"https://drive.google.com/file/d/1AVCG9_fFtuOtrvZVZWENmU2aDT7C51Jr/view?usp=sharing"}]},{"companyName":"Open University of Catalonia and Prodevelop","useCase":"Enabling monitoring of ports through a design-first approach, utilizing UML class diagrams to design the entire infrastructure. UML diagrams are source of truth for generated AsyncAPI documents that later are used for models and clients generation. These documents are extended with additional SLA properties to specify runtime quality of service requirements, facilitating real-time monitoring.","resources":[{"title":"Video: Leveraging AsyncAPI To Detect Anomalies in Smart Ports Platforms","link":"https://www.youtube.com/watch?v=m4KS6FSeTT4"}]},{"companyName":"Oracle","useCase":"Documenting data streaming APIs with AsyncAPI documents for client library generation in various programming languages, reducing development time for applications consuming data.","resources":[{"title":"Video: Productizing AsyncAPI for Data Replication","link":"https://www.youtube.com/watch?v=CGLlxYy66LY"}]},{"companyName":"TransferGo","useCase":"Enhancing discoverability of information about event-driven systems by documenting services with AsyncAPI, rendering documentation, and creating catalogs of events. Legacy services utilize a special library to generate AsyncAPI documents, while new services require AsyncAPI documents for generating PHP classes.","resources":[{"title":"Slides: How TransferGo Uses AsyncAPI To Improve Developer Experience","link":"https://drive.google.com/file/d/1t7tYMr8FMRInaZV0lWod1QfDuhhSjwBJ/view?usp=sharing"}]},{"companyName":"Kuehne+Nagel","useCase":"Implementing a GitOps-based pipeline to enable self-service management of Kafka infrastructure, including access control management. Automation of AsyncAPI document governance ensures consistency in the infrastructure at the pull request level.","resources":[{"title":"Slides: AsyncAPI For Platform Self-Service: A GitOps Tale","link":"https://drive.google.com/file/d/1y67PI8NaITPPwZAiDF2Zs7ISfcIpqMV8/view?usp=sharing"}]}] \ No newline at end of file diff --git a/types/@heroicons/react/outline.d.ts b/types/@heroicons/react/outline.d.ts new file mode 100644 index 00000000000..e2ac05a8d50 --- /dev/null +++ b/types/@heroicons/react/outline.d.ts @@ -0,0 +1 @@ +declare module '@heroicons/react/outline'; From cb317aee71ec55bc4cc4b867d5ada49e43d8dd7f Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Tue, 12 Mar 2024 19:00:44 +0530 Subject: [PATCH 09/63] nav added --- components/form/Select.tsx | 29 ++++++ components/helpers/applyFilter.tsx | 127 ++++++++++++++++++++++++ components/navigation/Filter.tsx | 70 +++++++++++++ components/navigation/JobPostItem.tsx | 90 +++++++++++++++++ components/navigation/LearningPanel.tsx | 8 ++ components/navigation/NavItem.tsx | 77 ++++++++++++++ components/navigation/NavMenu.tsx | 42 ++++++++ components/navigation/StickyNavbar.tsx | 14 +++ components/navigation/ToolsPanel.tsx | 8 ++ components/navigation/TutorialList.tsx | 36 +++++++ components/navigation/learningItems.tsx | 17 ++++ components/navigation/otherItems.tsx | 6 ++ components/navigation/toolingItems.tsx | 19 ++++ types/context/DocsContext.ts | 4 +- types/form/Select.ts | 11 ++ types/navigation/Filter.ts | 11 ++ types/navigation/JobPostItem.ts | 17 ++++ 17 files changed, 584 insertions(+), 2 deletions(-) create mode 100644 components/form/Select.tsx create mode 100644 components/helpers/applyFilter.tsx create mode 100644 components/navigation/Filter.tsx create mode 100644 components/navigation/JobPostItem.tsx create mode 100644 components/navigation/LearningPanel.tsx create mode 100644 components/navigation/NavItem.tsx create mode 100644 components/navigation/NavMenu.tsx create mode 100644 components/navigation/StickyNavbar.tsx create mode 100644 components/navigation/ToolsPanel.tsx create mode 100644 components/navigation/TutorialList.tsx create mode 100644 components/navigation/learningItems.tsx create mode 100644 components/navigation/otherItems.tsx create mode 100644 components/navigation/toolingItems.tsx create mode 100644 types/form/Select.ts create mode 100644 types/navigation/Filter.ts create mode 100644 types/navigation/JobPostItem.ts diff --git a/components/form/Select.tsx b/components/form/Select.tsx new file mode 100644 index 00000000000..8ac3996d881 --- /dev/null +++ b/components/form/Select.tsx @@ -0,0 +1,29 @@ +import React, { ChangeEvent } from 'react'; +import { twMerge } from 'tailwind-merge'; +import { SelectProps } from '@/types/form/Select'; + +export default function Select({ + className = '', + onChange = () => {}, + options, + selected, +}: SelectProps) { + const handleOnChange = (ev: ChangeEvent) => { + onChange(ev.target.value); + }; + + return ( + + ); +}; diff --git a/components/helpers/applyFilter.tsx b/components/helpers/applyFilter.tsx new file mode 100644 index 00000000000..1b0886a38ac --- /dev/null +++ b/components/helpers/applyFilter.tsx @@ -0,0 +1,127 @@ +export function sortFilter(arr: { value: string }[]): { value: string }[] { + return arr.sort((a, b) => { + if (a.value < b.value) { + return -1; + } + if (a.value > b.value) { + return 1; + } + return 0; + }); +} + +export const applyFilterList = ( + checks: { name: string }[], + data: { [key: string]: any }[], + setFilters: (lists: { [key: string]: { value: string; text: string }[] }) => void +): void => { + if (Object.keys(checks).length) { + let lists: { [key: string]: { value: string; text: string }[] } = {}; + checks.map((check) => { + lists[check.name] = []; + }); + for (let i = 0; i < data.length; i++) { + const res = data[i]; + for (const key in lists) { + const result = data[i][key]; + if (res) { + if (lists[key].length) { + if (Array.isArray(result)) { + result.map((a: any) => { + if (a.name) { + if (lists[key].some((e) => e.value === a.name)) { + return; + } else { + const newData = { + value: a.name, + text: a.name, + }; + lists[key].push(newData); + sortFilter(lists[key]); + } + } else { + if (lists[key].some((e) => e.value === a)) { + return; + } else { + const newData = { + value: a, + text: a, + }; + lists[key].push(newData); + sortFilter(lists[key]); + } + } + }); + } else { + if (lists[key].some((e) => e.value === result)) { + continue; + } else { + const newData = { + value: result, + text: result, + }; + lists[key].push(newData); + sortFilter(lists[key]); + } + } + } else { + if (Array.isArray(result)) { + result.map((e: any) => { + if (e.name) { + const newData = { + value: e.name, + text: e.name, + }; + lists[key].push(newData); + } else { + const newData = { + value: e, + text: e, + }; + lists[key].push(newData); + } + }); + } else { + const newData = { + value: result, + text: result, + }; + lists[key].push(newData); + } + } + } + } + } + setFilters(lists); + } +}; + +export const onFilterApply = ( + data: { [key: string]: any }[], + onFilter: (result: { [key: string]: any }[], query: { [key: string]: string }) => void, + query: { [key: string]: string } +): void => { + let result = data; + if (query && Object.keys(query).length >= 1) { + for (const property in query) { + const res = result.filter((e) => { + if (!query[property] || e[property] === query[property]) { + return e[property]; + } + if (Array.isArray(e[property])) { + if (e[property].some((data: any) => data.name === query[property])) { + return e[property].some((data: any) => data.name === query[property]); + } + return ( + e[property].some((data: any) => data.name === query[property]) || + e[property].includes(query[property]) || + false + ); + } + return; + }); + result = res; + } + } + onFilter(result, query); +}; diff --git a/components/navigation/Filter.tsx b/components/navigation/Filter.tsx new file mode 100644 index 00000000000..ced649e7eaf --- /dev/null +++ b/components/navigation/Filter.tsx @@ -0,0 +1,70 @@ +import { useState, useEffect } from "react"; +import { useRouter, NextRouter } from "next/router"; +import Select from "../form/Select"; +import { applyFilterList, onFilterApply } from "../helpers/applyFilter"; +import { FilterProps, Option } from "@/types/navigation/Filter"; + +export default function Filter ({ data, onFilter, checks, className }: FilterProps) { + const router: NextRouter = useRouter(); + const [filters, setFilters] = useState<{ [key: string]: Option[] }>({}); + const [query, setQuery] = useState<{ [key: string]: string }>({}); + + useEffect(() => { + setQuery(router.query as { [key: string]: string }); + applyFilterList(checks, data, setFilters); + }, [router]); + + useEffect(() => { + onFilterApply(data, onFilter, query); + }, [query, data, onFilter]); + + return ( + <> + {checks.map((check) => { + let selected = ""; + if (Object.keys(query).length) { + if (query[check.name]) { + selected = `${query[check.name]}`; + } + } + + const selectOptions: Option[] = [ + { + value: "", + text: `Filter by ${check.name}...`, + }, + ...(filters[check.name] || []), + ]; + + return ( + onChange(ev.target.value)} + className={twMerge( + `form-select h-full py-0 px-3 pr-7 inline-flex justify-center rounded-md border border-gray-300 shadow-sm py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:border-gray-500 focus:outline-none focus:ring-0 focus:ring-black ${className}` + )} + value={selected} + > + {options.map((option, index) => ( + + ))} + + ); +} diff --git a/components/navigation/MobileNavMenu.tsx b/components/navigation/MobileNavMenu.tsx new file mode 100644 index 00000000000..33be7661e0a --- /dev/null +++ b/components/navigation/MobileNavMenu.tsx @@ -0,0 +1,122 @@ +import AsyncAPILogo from '../AsyncAPILogo'; +import MenuBlocks from './MenuBlocks'; +import { SearchButton } from '../AlgoliaSearch'; +import learningItems from './learningItems'; +import toolingItems from './toolingItems'; +import communityItems from './communityItems'; +import otherItems from './otherItems'; +import Link from 'next/link'; +import NavItemDropdown from '../icons/NavItemDropdown'; +import { useState } from 'react'; + +interface MenuItem { + href: string; + target?: string; + text: string; +} + +interface MobileNavMenuProps { + onClickClose?: () => void; +} + +export default function MobileNavMenu({ onClickClose = () => {} }: MobileNavMenuProps) { + const [open, setOpen] = useState(null); + + function showMenu(menu: string) { + if (open === menu) { + setOpen(null); + return; + } + setOpen(menu); + } + + return ( +
    +
    +
    +
    +
    + + + + + +
    + + + + +
    +
    +
    +
    showMenu('learning')} data-testid="MobileNav-docs"> +

    Docs

    + {open === 'learning' && } +
    +
    showMenu('tooling')} data-testid="MobileNav-tools"> +

    Tools

    + {open === 'tooling' && } +
    +
    showMenu('community')} data-testid="MobileNav-community"> +

    Community

    + {open === 'community' && } +
    +
    showMenu('others')} data-testid="MobileNav-others"> +
    +
    +

    Others

    + {open === 'others' && otherItems.map((item: MenuItem, index: number) => ( + + + {item.text} + + + ))} +
    +
    +
    +
    +
    +
    + ); +} diff --git a/components/navigation/NavBar.tsx b/components/navigation/NavBar.tsx new file mode 100644 index 00000000000..30dec6e2569 --- /dev/null +++ b/components/navigation/NavBar.tsx @@ -0,0 +1,232 @@ +import { useState, useEffect } from "react"; +import { useRouter, NextRouter } from "next/router"; +import { isMobileDevice } from '../helpers/is-mobile'; +import { useOutsideClick } from '../helpers/use-outside-click'; +import AsyncAPILogo from '../AsyncAPILogo'; +import NavItem from './NavItem'; +import ToolsPanel from './ToolsPanel'; +import LearningPanel from './LearningPanel'; +import CommunityPanel from "./CommunityPanel"; +import MobileNavMenu from './MobileNavMenu'; +import otherItems from './otherItems'; +import GithubButton from "../buttons/GithubButton"; +import { SearchButton } from '../AlgoliaSearch'; +import IconLoupe from '../icons/Loupe'; +import Link from 'next/link'; +import LanguageSelect from '../languageSelector/LanguageSelect'; +import { + defaultLanguage, + languages, + useTranslation, + } from "../../utils/i18n"; +import i18nPaths from "../../utils/i18nPaths"; + +interface NavBarProps { + className?: string; + hideLogo?: boolean; +} + +const isMobile = isMobileDevice(); + +export default function NavBar ({ + className = '', + hideLogo = false, +}: NavBarProps) { + const router: NextRouter = useRouter(); + const { pathname, query, asPath } = router; + const [open, setOpen] = useState<'learning' | 'tooling' | 'community' | null>(null); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + const { i18n } = useTranslation(); + + /** + * Retrieves unique language options based on the current path and i18nPaths configuration. + * + * @returns {string[]} - An array of unique language options in uppercase. + */ + const getUniqueLangs = (): string[] => { + let pathnameWithoutLocale = pathname; + + // Check if the pathname includes "/[lang]", if so, replace it with an empty string + if (pathname && pathname.includes("/[lang]")) { + pathnameWithoutLocale = pathname.replace("/[lang]", ""); + } + + // Filter unique languages based on i18nPaths that include the modified pathnameWithoutLocale + let uniqueLangs = Object.keys(i18nPaths).filter(lang => i18nPaths[lang].includes(pathnameWithoutLocale)).map(lang => lang.toUpperCase()); + + // If no unique languages are found, default to ["EN"] + return uniqueLangs.length === 0 ? ["EN"] : uniqueLangs; + } + + const uniqueLangs = getUniqueLangs().map((lang) => ({ + key: lang, + text: lang, + value: lang + })); + + /** + * Changes the language and updates the URL accordingly. + * + * @async + * @param {string} locale - The new locale/language to set. + * @param {boolean} langPicker - Indicates whether the change is from the language picker. + * If true, stores the language in local storage. + * @returns {Promise} - A promise representing the completion of the language change. + * @throws {Error} - If an error occurs during the language change process. + */ + const changeLanguage = async (locale: string, langPicker: boolean): Promise => { + // Verifies if the language change is from langPicker or the browser-api + if (langPicker) { + localStorage.setItem('i18nLang', locale); + } + + // Detect current language + const slug = asPath.split("/")[1]; + const langSlug = languages.includes(slug) && slug; + const language = query.lang || langSlug || defaultLanguage; + + let href = pathname; + + if (locale) { + if (pathname.startsWith("/404")) { + href = `/${locale}`; + } else { + href = pathname.replace("[lang]", locale); + } + } else { + if (language) { + href = `/${language}${href}`; + } else { + href = `/${href}`; + } + } + + // Fix double slashes + href = href.replace(/([^:]\/)\/+/g, "$1").replace("//", "/"); + + router.push(href); + }; + + function outsideClick(menu: 'learning' | 'tooling' | 'community' | null) { + if (open !== menu) return; + setOpen(null); + } + + const learningRef = useOutsideClick(() => outsideClick('learning')); + const toolingRef = useOutsideClick(() => outsideClick('tooling')); + const communityRef = useOutsideClick(() => outsideClick('community')); + + function showMenu(menu: 'learning' | 'tooling' | 'community' | null) { + if (open === menu) return; + setOpen(menu); + } + + function showOnClickMenu(menu: 'learning' | 'tooling' | 'community' | null) { + if (!isMobile) return; + if (open === menu) return setOpen(null); + setOpen(menu); + } + + useEffect(() => { + setMobileMenuOpen(false); + setOpen(null); + }, [asPath]); + + return ( +
    +
    + {!hideLogo && ( +
    +
    + + + + + +
    +
    + )} + +
    + + + + +
    + + + +
    + + {/* Mobile menu, show/hide based on mobile menu state. */} + {mobileMenuOpen && setMobileMenuOpen(false)} />} + +
    + ) +} diff --git a/components/navigation/otherItems.tsx b/components/navigation/otherItems.tsx index e9d4144d6e8..9bfcf24d7c4 100644 --- a/components/navigation/otherItems.tsx +++ b/components/navigation/otherItems.tsx @@ -1,6 +1,16 @@ -export default [ - { text: "Case Studies", href: '/casestudies' }, - { text: 'Blog', href: '/blog' }, - // { text: 'Shop', href: 'https://asyncapi.threadless.com', target: '_blank' }, - { text: "Roadmap", href: '/roadmap', className: 'text-secondary-500 font-bold' }, - ] \ No newline at end of file +interface OtherItem { + text: string; + href: string; + target?: string; + className?: string; +} + +const otherItems: OtherItem[] = [ + { text: "Case Studies", href: "/casestudies" }, + { text: "Blog", href: "/blog" }, + // If you want to add target for a specific item, you can do it here + // { text: "Shop", href: "https://asyncapi.threadless.com", target: "_blank" }, + { text: "Roadmap", href: "/roadmap", className: "text-secondary-500 font-bold" }, +]; + +export default otherItems; From 1bf07bbd25b8f22c663a0d0d83c6d43d5a0130a0 Mon Sep 17 00:00:00 2001 From: Vishvamsinh Vaghela Date: Tue, 12 Mar 2024 21:43:37 +0530 Subject: [PATCH 11/63] lint fix --- components/AsyncAPILogo.tsx | 117 +++++---- components/ClickableLogo.tsx | 7 +- components/data/buckets.tsx | 26 +- components/form/Select.tsx | 12 +- components/helpers/applyFilter.ts | 228 +++++++++--------- components/helpers/is-mobile.ts | 1 + components/helpers/use-outside-click.ts | 14 +- .../languageSelector/LanguageSelect.tsx | 10 +- components/link.tsx | 1 + components/navigation/CommunityPanel.tsx | 2 +- components/navigation/DocsMobileMenu.tsx | 60 ++--- components/navigation/DocsNav.tsx | 35 +-- components/navigation/DocsNavItem.tsx | 10 +- components/navigation/EventFilter.tsx | 29 +-- components/navigation/EventPostItem.tsx | 27 ++- components/navigation/Filter.tsx | 35 +-- components/navigation/FlyoutMenu.tsx | 13 +- components/navigation/JobPostItem.tsx | 93 +++---- components/navigation/Label.tsx | 32 +-- components/navigation/LearningPanel.tsx | 10 +- components/navigation/MenuBlocks.tsx | 26 +- components/navigation/MobileNavMenu.tsx | 104 ++++---- components/navigation/NavBar.tsx | 120 ++++----- components/navigation/NavItem.tsx | 19 +- components/navigation/NavMenu.tsx | 22 +- components/navigation/StickyNavbar.tsx | 7 +- components/navigation/SubCategoryDocsNav.tsx | 36 +-- components/navigation/ToolsPanel.tsx | 10 +- components/navigation/TutorialList.tsx | 18 +- components/navigation/communityItems.tsx | 26 +- components/navigation/learningItems.tsx | 24 +- components/navigation/otherItems.tsx | 6 +- components/navigation/toolingItems.tsx | 17 +- 33 files changed, 613 insertions(+), 584 deletions(-) diff --git a/components/AsyncAPILogo.tsx b/components/AsyncAPILogo.tsx index ea4149bfbf5..ed656873ccf 100644 --- a/components/AsyncAPILogo.tsx +++ b/components/AsyncAPILogo.tsx @@ -2,62 +2,61 @@ interface classType{ className : string; } -export default function AsyncAPILogo({className}:classType) { - return ( - - AsyncAPI Logo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) - } \ No newline at end of file +export default function AsyncAPILogo({ className }:classType) { + return ( + + AsyncAPI Logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/components/ClickableLogo.tsx b/components/ClickableLogo.tsx index e3147588df3..f056fc19923 100644 --- a/components/ClickableLogo.tsx +++ b/components/ClickableLogo.tsx @@ -1,5 +1,6 @@ -import React from 'react'; import Link from 'next/link'; +import React from 'react'; + import AsyncAPILogo from './AsyncAPILogo'; interface ClickableLogoProps { @@ -8,11 +9,11 @@ interface ClickableLogoProps { logoClassName?: string; } -export default function ClickableLogo({ href = '/', className = 'flex', logoClassName }: ClickableLogoProps){ +export default function ClickableLogo({ href = '/', className = 'flex', logoClassName }: ClickableLogoProps) { return ( - + ); diff --git a/components/data/buckets.tsx b/components/data/buckets.tsx index 2a33296db0a..efdec790c93 100644 --- a/components/data/buckets.tsx +++ b/components/data/buckets.tsx @@ -1,10 +1,10 @@ import IconGettingStarted from '../icons/GettingStarted'; -import IconTutorials from '../icons/Tutorials'; -import IconUseCases from '../icons/UseCases'; import IconGuide from '../icons/Guide'; +import IconMigration from '../icons/Migration'; import IconSpec from '../icons/Spec'; +import IconTutorials from '../icons/Tutorials'; +import IconUseCases from '../icons/UseCases'; import IconUsers from '../icons/Users'; -import IconMigration from '../icons/Migration'; interface Bucket { name: string; @@ -28,7 +28,7 @@ export const buckets: Bucket[] = [ borderClassName: 'border-secondary-200', Icon: IconGettingStarted, href: '/docs/concepts', - icon: IconGettingStarted, + icon: IconGettingStarted }, { name: 'tutorials', @@ -39,18 +39,18 @@ export const buckets: Bucket[] = [ borderClassName: 'border-pink-100', Icon: IconTutorials, href: '/docs/tutorials', - icon: IconTutorials, + icon: IconTutorials }, { name: 'guides', title: 'Guides', - description: "Our Guides section teaches AsyncAPI's capabilities at a high level.", + description: 'Our Guides section teaches AsyncAPI\'s capabilities at a high level.', link: '/docs/guides', className: 'bg-primary-200', borderClassName: 'border-primary-200', Icon: IconGuide, href: '/docs/guides', - icon: IconGuide, + icon: IconGuide }, { name: 'tools', @@ -61,7 +61,7 @@ export const buckets: Bucket[] = [ borderClassName: 'border-green-200', Icon: IconUseCases, href: '/docs/tools', - icon: IconUseCases, + icon: IconUseCases }, { name: 'reference', @@ -72,7 +72,7 @@ export const buckets: Bucket[] = [ borderClassName: 'border-yellow-200', Icon: IconSpec, href: '/docs/reference', - icon: IconSpec, + icon: IconSpec }, { name: 'migration', @@ -83,7 +83,7 @@ export const buckets: Bucket[] = [ borderClassName: 'border-blue-400', Icon: IconMigration, href: '/docs/migration', - icon: IconMigration, + icon: IconMigration }, { name: 'community', @@ -94,12 +94,12 @@ export const buckets: Bucket[] = [ borderClassName: 'border-orange-200', Icon: IconUsers, href: '/docs/community', - icon: IconUsers, - }, + icon: IconUsers + } ].map(bucket => { return { ...bucket, href: bucket.link, - icon: bucket.Icon, + icon: bucket.Icon }; }); diff --git a/components/form/Select.tsx b/components/form/Select.tsx index 8ac3996d881..b26fd988c18 100644 --- a/components/form/Select.tsx +++ b/components/form/Select.tsx @@ -1,12 +1,14 @@ -import React, { ChangeEvent } from 'react'; +import type { ChangeEvent } from 'react'; +import React from 'react'; import { twMerge } from 'tailwind-merge'; -import { SelectProps } from '@/types/form/Select'; + +import type { SelectProps } from '@/types/form/Select'; export default function Select({ className = '', onChange = () => {}, options, - selected, + selected }: SelectProps) { const handleOnChange = (ev: ChangeEvent) => { onChange(ev.target.value); @@ -14,13 +16,13 @@ export default function Select({ return ( onChange(ev.target.value)} - className={twMerge( - `form-select h-full py-0 px-3 pr-7 inline-flex justify-center rounded-md border border-gray-300 shadow-sm py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:border-gray-500 focus:outline-none focus:ring-0 focus:ring-black ${className}` - )} + className={twMerge(`form-select h-full py-0 px-3 pr-7 inline-flex justify-center rounded-md border border-gray-300 shadow-sm py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:border-gray-500 focus:outline-none focus:ring-0 focus:ring-black ${className}`)} value={selected} > {options.map((option, index) => ( - ))} diff --git a/components/link.tsx b/components/link.tsx index 584fd55d373..d076647cc19 100644 --- a/components/link.tsx +++ b/components/link.tsx @@ -1,5 +1,6 @@ import Link from 'next/link'; import { useRouter } from 'next/router'; + import { defaultLanguage, languages } from '../utils/i18n'; import i18nPaths from '../utils/i18nPaths'; diff --git a/components/navigation/CommunityPanel.tsx b/components/navigation/CommunityPanel.tsx index c8303b3a2f0..ce5b8379bc2 100644 --- a/components/navigation/CommunityPanel.tsx +++ b/components/navigation/CommunityPanel.tsx @@ -1,5 +1,5 @@ -import FlyoutMenu from './FlyoutMenu'; import communityItems from './communityItems'; +import FlyoutMenu from './FlyoutMenu'; export default function CommunityPanel() { return ; diff --git a/components/navigation/DocsMobileMenu.tsx b/components/navigation/DocsMobileMenu.tsx index fa64b78d5fc..063c3eb6f53 100644 --- a/components/navigation/DocsMobileMenu.tsx +++ b/components/navigation/DocsMobileMenu.tsx @@ -1,60 +1,62 @@ import React from 'react'; + +import type { DocsMobileMenuProps } from '@/types/navigation/DocsMobileMenu'; + +import { DOCS_INDEX_NAME, SearchButton } from '../AlgoliaSearch'; import ClickableLogo from '../ClickableLogo'; -import DocsNav from './DocsNav'; -import { SearchButton, DOCS_INDEX_NAME } from '../AlgoliaSearch'; import IconLoupe from '../icons/Loupe'; -import { DocsMobileMenuProps } from '@/types/navigation/DocsMobileMenu'; +import DocsNav from './DocsNav'; -export default function DocsMobileMenu ({ +export default function DocsMobileMenu({ post, navigation, - onClickClose = () => {}, + onClickClose = () => {} }:DocsMobileMenuProps) { return ( -
    -
    -
    +
    +
    +
    -
    -
    +
    +
    -
    -
    - +
    +
    +
    -
    +
    - Search docs... + Search docs...
    -
    -
    +
    {/* Force sidebar to shrink to fit close icon */}
    diff --git a/components/navigation/DocsNav.tsx b/components/navigation/DocsNav.tsx index 6e7768d8f2e..f1836e4da53 100644 --- a/components/navigation/DocsNav.tsx +++ b/components/navigation/DocsNav.tsx @@ -1,29 +1,32 @@ -import { useState, useEffect } from 'react'; -import DocsNavItem from './DocsNavItem'; +import { useEffect, useState } from 'react'; + +import type { DocsNavProps, SerializedBuckets } from '@/types/navigation/DocsNav'; + +import { buckets } from '../data/buckets'; +import DocsArrow from '../icons/DocsArrow'; import IconHome from '../icons/Home'; +import DocsNavItem from './DocsNavItem'; import SubCategoryDocsNav from './SubCategoryDocsNav'; -import DocsArrow from '../icons/DocsArrow'; -import { buckets } from '../data/buckets'; -import { SerializedBuckets , DocsNavProps } from '@/types/navigation/DocsNav'; const serializedBuckets: SerializedBuckets = buckets.reduce((acc, bucket) => { acc[bucket.name || ''] = { ...bucket, - className: `${bucket.className || ''} ${bucket.borderClassName || ''}`, + className: `${bucket.className || ''} ${bucket.borderClassName || ''}` }; + return acc; }, { welcome: { icon: IconHome, - className: 'bg-gray-300 border-gray-300', - }, + className: 'bg-gray-300 border-gray-300' + } } as SerializedBuckets); -export default function DocsNav ({ +export default function DocsNav({ item, active, - onClick = () => {}, -}:DocsNavProps ) { + onClick = () => {} +}:DocsNavProps) { const subCategories = item.children; const bucket = serializedBuckets[item.item.rootSectionId]; const [openSubCategory, setOpenSubCategory] = useState(active.startsWith(item.item.slug)); @@ -31,23 +34,23 @@ export default function DocsNav ({ const onClickHandler = () => { setOpenSubCategory(!openSubCategory); onClick(); - } + }; useEffect(() => { setOpenSubCategory(active.startsWith(item.item.slug)); - }, [active]) + }, [active]); return ( -
  • +
  • 0} activeDropDownItem={openSubCategory} onClick={() => setOpenSubCategory(!openSubCategory)} />
    {openSubCategory && ( -