From bd15521a0765ae0210c28843e2eccaaaf7a2dfef Mon Sep 17 00:00:00 2001 From: Kirk Haines Date: Tue, 13 Feb 2024 16:05:21 -0700 Subject: [PATCH] Refactor navigation (#29) --- config.ts | 4 ++ package-lock.json | 18 ++++++ package.json | 1 + src/components/NavGroup.tsx | 105 ++++++++++++++++++++++++++++++ src/components/NavItem.tsx | 108 +++++++++++++++++++++++++++++++ src/components/StaticNav.tsx | 121 +++-------------------------------- 6 files changed, 246 insertions(+), 111 deletions(-) create mode 100644 src/components/NavGroup.tsx create mode 100644 src/components/NavItem.tsx diff --git a/config.ts b/config.ts index c3047ab..8e8a7fb 100644 --- a/config.ts +++ b/config.ts @@ -25,6 +25,10 @@ const config: Config = { { label: 'What is Topos?', content: [ + { + label: 'Getting Started', + path: '/', + }, { label: 'Chapter overview: What is Topos?', path: '/content/module-1', diff --git a/package-lock.json b/package-lock.json index 1be1c35..98ba9b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "katex": "^0.13.24", "prismjs": "^1.29.0", "react": "^18.2.0", + "react-collapsed": "^4.1.2", "react-cookie": "^6.1.1", "react-dom": "^18.2.0", "react-icons": "^5.0.1", @@ -20739,6 +20740,18 @@ "node": ">=0.10.0" } }, + "node_modules/react-collapsed": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/react-collapsed/-/react-collapsed-4.1.2.tgz", + "integrity": "sha512-vusqVnW/VbFCKJx29KNTnjJrwCbV7k3bB/FiO9/0Fj7JNoNarkU1xU7yK4FZHqPW0Q2izB7o6fYzG16zgdDNMQ==", + "dependencies": { + "tiny-warning": "^1.0.3" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, "node_modules/react-cookie": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-6.1.1.tgz", @@ -23811,6 +23824,11 @@ "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-2.2.0.tgz", "integrity": "sha512-bO4IgID5M2x5YQFBru/wREgT30YuA8aoOd/F8Rd6LKRIn1gOe9aREjT74J9EVukHqI/2RB+4SM1RgXM0gwxoWw==" }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, "node_modules/tinycolor2": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", diff --git a/package.json b/package.json index 2017f2d..9dff363 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "katex": "^0.13.24", "prismjs": "^1.29.0", "react": "^18.2.0", + "react-collapsed": "^4.1.2", "react-cookie": "^6.1.1", "react-dom": "^18.2.0", "react-icons": "^5.0.1", diff --git a/src/components/NavGroup.tsx b/src/components/NavGroup.tsx new file mode 100644 index 0000000..8f79909 --- /dev/null +++ b/src/components/NavGroup.tsx @@ -0,0 +1,105 @@ +import { NavItem } from './NavItem'; +import React, { Dispatch, SetStateAction, useState } from 'react'; +import { useCollapse } from 'react-collapsed'; +import config from '../../config'; +import { NavigationItemType } from '../interfaces/Config'; +import { IconCaretDown } from './icons/IconCaretDown'; +import { Link } from 'gatsby'; +import { BiCollapseVertical, BiExpandVertical } from 'react-icons/bi'; +import { twMerge } from 'tailwind-merge'; + +interface NavGroupItem { + label: string; + path?: string; + content?: Array; +} + +interface NavGroupProps { + navGroupItem: NavigationItemType; + navGroupIndex: number; + setNavExpanded: Dispatch>; + pageTransition: boolean; +} + +export const NavGroup: React.FC = ({ + navGroupItem, + navGroupIndex, + setNavExpanded, + pageTransition, +}) => { + const [isExpanded, setExpanded] = useState(false); + const { getCollapseProps, getToggleProps } = useCollapse({ isExpanded }); + + // if (navGroupItem.path && !isExpanded) setExpanded(true); + + return ( +
  • setExpanded((prevExpanded) => !prevExpanded), + })} + > + {navGroupItem.path ? ( + + {isExpanded ? ( + + ) : ( + + )} + setNavExpanded(false)} + getProps={(props) => { + if (props.isCurrent && !isExpanded) { + setExpanded(true); + } + + return { + className: props.isCurrent + ? 'text-action-reg text-semibold' + : 'hover:text-action-reg', + }; + }} + > + {navGroupItem.label} + + + ) : ( + + {isExpanded ? ( + + ) : ( + + )} + {navGroupItem.label} + + )} + {navGroupItem.content && ( +
      + {navGroupItem.content.map( + (navItem: NavigationItemType, navItemIndex: number) => ( + + ) + )} +
    + )} +
  • + ); +}; diff --git a/src/components/NavItem.tsx b/src/components/NavItem.tsx new file mode 100644 index 0000000..b83f36a --- /dev/null +++ b/src/components/NavItem.tsx @@ -0,0 +1,108 @@ +import React, { Dispatch, SetStateAction, useState } from 'react'; +import config from '../../config'; +import { NavigationItemType } from '../interfaces/Config'; +import { IconCaretDown } from './icons/IconCaretDown'; +import { Link } from 'gatsby'; +import { twMerge } from 'tailwind-merge'; + +interface NavItem { + label: string; + path?: string; + content?: Array; +} + +interface NavItemProps { + navItem: NavigationItemType; + isExpanded: boolean; + setExpanded: Dispatch>; + setNavExpanded: Dispatch>; +} + +// class NavItem extends React.Component<{ location: Location }> { +export const NavItem: React.FC = ({ + navItem, + isExpanded, + setExpanded, + setNavExpanded, +}) => { + const PAGE_IN_PROGRESS_PATH = '/content/page-in-progress.html'; + + const disabled = navItem.label.toLowerCase().startsWith('disabled:'); + const navItemLabel = navItem.label.replace('disabled:', ''); + const disabledStyle = disabled + ? { + opacity: 0.5, + pointerEvents: 'none', + } + : {}; + return
  • + {navItem.path ? ( + { + if (disabled) { + event.stopPropagation(); + } else { + setNavExpanded(false); + } + }, + }} + getProps={(props) => { + if (props.isCurrent && !isExpanded) { + setExpanded(true); + } + + return { + className: props.isCurrent + ? 'text-action-reg text-semibold' + : 'hover:text-action-reg', + }; + }} + > + {navItemLabel} + + ) : ( + + )} + {navItem.content && ( +
      + {navItem.content.map( + (subNavItem: NavigationItemType, subNavItemIndex: number) => { + return ( + + ); + } + )} +
    + )} +
  • ; +}; diff --git a/src/components/StaticNav.tsx b/src/components/StaticNav.tsx index 312ba07..6f05c9f 100644 --- a/src/components/StaticNav.tsx +++ b/src/components/StaticNav.tsx @@ -1,3 +1,4 @@ +import { NavGroup } from './NavGroup'; import React, { Dispatch, SetStateAction, useState } from 'react'; import config from '../../config'; import { NavigationItemType } from '../interfaces/Config'; @@ -5,93 +6,11 @@ import { IconCaretDown } from './icons/IconCaretDown'; import { Link } from 'gatsby'; import { twMerge } from 'tailwind-merge'; -interface NavItem { - label: string; - path?: string; - content?: Array; -} - -interface NavItemProps { - navItem: NavigationItemType; - setNavExpanded: Dispatch>; -} - interface StaticNavProps { setNavExpanded: Dispatch>; pageTransition: boolean; } -export const NavItem: React.FC = ({ - navItem, - setNavExpanded, -}) => { - const [expanded, setExpanded] = useState(false); - const PAGE_IN_PROGRESS_PATH = '/content/page-in-progress.html'; - return ( -
  • - {navItem.path ? ( - setNavExpanded(false)} - > - {navItem.label} - - ) : ( - - )} - {navItem.content && ( -
      - {navItem.content.map( - (subNavItem: NavigationItemType, subNavItemIndex: number) => { - return ( -
    • - setNavExpanded(false)} - > - {subNavItem.label} - -
    • - ); - } - )} -
    - )} -
  • - ); -}; - export const StaticNav: React.FC = ({ setNavExpanded, pageTransition, @@ -101,35 +20,15 @@ export const StaticNav: React.FC = ({ className={twMerge('px-8', pageTransition && 'pointer-events-none')} arial-label="Main" > -
      - {config.navigation.map((navGroup, navGroupIndex: number) => ( -
    • - {navGroup.path ? ( - setNavExpanded(false)} - > - {navGroup.label} - - ) : ( - {navGroup.label} - )} - {navGroup.content && ( -
        - {navGroup.content.map( - (navItem: NavigationItemType, navItemIndex: number) => ( - - ) - )} -
      - )} -
    • +
        + {config.navigation.map((navGroupItem, navGroupIndex: number) => ( + ))}