diff --git a/components/Common/LanguageDropDown/index.stories.tsx b/components/Common/LanguageDropDown/index.stories.tsx index 1454c5940a3f4..8cb9b3f37462f 100644 --- a/components/Common/LanguageDropDown/index.stories.tsx +++ b/components/Common/LanguageDropDown/index.stories.tsx @@ -12,7 +12,7 @@ export const Default: Story = { { name: 'French', code: 'fr' }, { name: 'Spanish', code: 'es' }, ], - currentLanguage: { name: 'English', code: 'en' }, + currentLanguage: 'en', }, }; diff --git a/components/Common/LanguageDropDown/index.tsx b/components/Common/LanguageDropDown/index.tsx index 48d6abe97c576..121e85cb0c423 100644 --- a/components/Common/LanguageDropDown/index.tsx +++ b/components/Common/LanguageDropDown/index.tsx @@ -12,7 +12,7 @@ type SimpleLocaleConfig = Pick; type LanguageDropDownProps = { onChange?: (newLocale: SimpleLocaleConfig) => void; - currentLanguage: SimpleLocaleConfig; + currentLanguage: string; availableLanguages: SimpleLocaleConfig[]; }; @@ -45,7 +45,7 @@ const LanguageDropdown: FC = ({ key={code} onClick={() => onChange({ name, code })} className={classNames(styles.dropDownItem, { - [styles.currentDropDown]: code === currentLanguage.code, + [styles.currentDropDown]: code === currentLanguage, })} > {name} diff --git a/components/Containers/NavigationBar/index.module.css b/components/Containers/NavigationBar/index.module.css new file mode 100644 index 0000000000000..259518930e9b7 --- /dev/null +++ b/components/Containers/NavigationBar/index.module.css @@ -0,0 +1,108 @@ +.container { + @apply border-neutral-200 + dark:border-neutral-900 + md:flex + md:h-16 + md:flex-row + md:items-center + md:gap-8 + md:border-b + md:px-8; +} + +.nodeIconAndMobileItemsToggler { + @apply flex + h-16 + shrink-0 + items-center + border-b + border-neutral-200 + px-4 + dark:border-neutral-900 + md:flex + md:h-full + md:items-center + md:border-0 + md:px-0; +} + +.nodeIconWrapper { + @apply h-[30px] + flex-1; +} + +.nodejsLogoDark { + @apply h-6 + w-20 + dark:hidden; +} + +.nodejsLogoLight { + @apply hidden + h-6 + w-20 + dark:block; +} + +.navInteractionIcon { + @apply h-6 + w-6; +} + +.sidebarItemTogglerLabel { + @apply block + cursor-pointer + md:hidden; +} + +.main { + @apply hidden + flex-1 + flex-col + md:flex + md:flex-row + md:items-center; +} + +.navItems { + @apply flex + flex-col + gap-1 + border-b + border-neutral-200 + p-4 + dark:border-neutral-900 + md:flex-1 + md:flex-row + md:border-0 + md:p-0; +} + +.actionsWrapper { + @apply flex + items-center + gap-2 + border-b + border-neutral-200 + p-4 + dark:border-neutral-900 + md:border-0 + md:p-0; +} + +.ghIconWrapper { + @apply h-9 + w-9 + rounded-md + p-2; + + svg { + @apply fill-neutral-700 + dark:fill-neutral-300; + } + + &:hover { + @apply bg-neutral-100 + dark:bg-neutral-900; + } +} diff --git a/components/Containers/NavigationBar/index.stories.tsx b/components/Containers/NavigationBar/index.stories.tsx new file mode 100644 index 0000000000000..b9a6d9629fb28 --- /dev/null +++ b/components/Containers/NavigationBar/index.stories.tsx @@ -0,0 +1,48 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +import NavigationBar from './index'; + +type Story = StoryObj; +type Meta = MetaObj; + +export const Default: Story = { + args: { + navItems: [ + { + text: 'Learn', + href: '/learn', + }, + { + text: 'About', + href: '/about', + }, + { + text: 'Docs', + href: '/docs', + }, + { + text: 'Download', + href: '/download', + }, + { + text: 'Blog', + href: '/blog', + }, + { + text: 'Certification', + href: 'https://openjsf.org/certification', + }, + ], + languages: { + availableLanguages: [ + { name: 'English', code: 'en' }, + { name: 'French', code: 'fr' }, + { name: 'Spanish', code: 'es' }, + ], + currentLanguage: 'en', + }, + onThemeTogglerClick: () => {}, + }, +}; + +export default { component: NavigationBar } as Meta; diff --git a/components/Containers/NavigationBar/index.tsx b/components/Containers/NavigationBar/index.tsx new file mode 100644 index 0000000000000..0f2dbeb338ed6 --- /dev/null +++ b/components/Containers/NavigationBar/index.tsx @@ -0,0 +1,80 @@ +'use client'; + +import Hamburger from '@heroicons/react/24/solid/Bars3Icon'; +import XMark from '@heroicons/react/24/solid/XMarkIcon'; +import * as Label from '@radix-ui/react-label'; +import { useState } from 'react'; +import type { FC, ComponentProps } from 'react'; + +import LanguageDropdown from '@/components/Common/LanguageDropDown'; +import ThemeToggle from '@/components/Common/ThemeToggle'; +import NavItem from '@/components/Containers/NavItem'; +import GithubLogo from '@/components/Icons/GitHubLogo'; +import NodejsLogoDark from '@/components/Icons/NodejsLogoDark'; +import NodejsLogoLight from '@/components/Icons/NodejsLogoLight'; + +import style from './index.module.css'; + +type NavItem = { text: string; href: string }; + +type NavbarProps = { + navItems: NavItem[]; + languages: ComponentProps; + onThemeTogglerClick: () => void; +}; + +const navInteractionIcons = { + show: , + close: , +}; + +const NavigationBar: FC = ({ + navItems, + languages, + onThemeTogglerClick, +}) => { + const [isMenuOpen, setIsMenuOpen] = useState(false); + return ( + + ); +}; + +export default NavigationBar; diff --git a/components/Icons/GitHubLogo.tsx b/components/Icons/GitHubLogo.tsx new file mode 100644 index 0000000000000..459cd56c5890b --- /dev/null +++ b/components/Icons/GitHubLogo.tsx @@ -0,0 +1,16 @@ +import type { FC, HTMLAttributes } from 'react'; + +const GitHubLogo: FC> = props => ( + + + +); + +export default GitHubLogo; diff --git a/components/Icons/NodejsLogoDark.tsx b/components/Icons/NodejsLogoDark.tsx new file mode 100644 index 0000000000000..068f70892c8de --- /dev/null +++ b/components/Icons/NodejsLogoDark.tsx @@ -0,0 +1,118 @@ +import type { FC, HTMLAttributes } from 'react'; + +const NodejsLogoDark: FC> = props => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export default NodejsLogoDark; diff --git a/components/Icons/NodejsLogoLight.tsx b/components/Icons/NodejsLogoLight.tsx new file mode 100644 index 0000000000000..8b6207311960a --- /dev/null +++ b/components/Icons/NodejsLogoLight.tsx @@ -0,0 +1,118 @@ +import type { FC, HTMLAttributes } from 'react'; + +const NodejsLogoLight: FC> = props => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export default NodejsLogoLight; diff --git a/package-lock.json b/package-lock.json index 9735ced66b6a9..10c1641406c06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@radix-ui/react-accessible-icon": "^1.0.3", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toast": "^1.1.5", @@ -5587,6 +5588,29 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz", + "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-menu": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", diff --git a/package.json b/package.json index 0ab63adf3c33e..1bb1c7827f306 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@radix-ui/react-accessible-icon": "^1.0.3", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toast": "^1.1.5",