|
|
@ -1,35 +1,47 @@ |
|
|
|
import { Fragment, useEffect, useRef, useState } from 'react'; |
|
|
|
import { |
|
|
|
|
|
|
|
Fragment, |
|
|
|
|
|
|
|
type ReactElement, |
|
|
|
|
|
|
|
useEffect, |
|
|
|
|
|
|
|
useRef, |
|
|
|
|
|
|
|
useState, |
|
|
|
|
|
|
|
} from 'react'; |
|
|
|
import { useKeydown } from '../../hooks/use-keydown'; |
|
|
|
import { useKeydown } from '../../hooks/use-keydown'; |
|
|
|
import { useOutsideClick } from '../../hooks/use-outside-click'; |
|
|
|
import { useOutsideClick } from '../../hooks/use-outside-click'; |
|
|
|
import BestPracticesIcon from '../../icons/best-practices.svg'; |
|
|
|
|
|
|
|
import ClipboardIcon from '../../icons/clipboard.svg'; |
|
|
|
|
|
|
|
import GuideIcon from '../../icons/guide.svg'; |
|
|
|
|
|
|
|
import HomeIcon from '../../icons/home.svg'; |
|
|
|
|
|
|
|
import RoadmapIcon from '../../icons/roadmap.svg'; |
|
|
|
|
|
|
|
import UserIcon from '../../icons/user.svg'; |
|
|
|
|
|
|
|
import GroupIcon from '../../icons/group.svg'; |
|
|
|
|
|
|
|
import VideoIcon from '../../icons/video.svg'; |
|
|
|
|
|
|
|
import { httpGet } from '../../lib/http'; |
|
|
|
import { httpGet } from '../../lib/http'; |
|
|
|
import { isLoggedIn } from '../../lib/jwt'; |
|
|
|
import { isLoggedIn } from '../../lib/jwt'; |
|
|
|
|
|
|
|
import { BestPracticesIcon } from '../ReactIcons/BestPracticesIcon.tsx'; |
|
|
|
|
|
|
|
import { UserIcon } from '../ReactIcons/UserIcon.tsx'; |
|
|
|
|
|
|
|
import { GroupIcon } from '../ReactIcons/GroupIcon.tsx'; |
|
|
|
|
|
|
|
import { RoadmapIcon } from '../ReactIcons/RoadmapIcon.tsx'; |
|
|
|
|
|
|
|
import { ClipboardIcon } from '../ReactIcons/ClipboardIcon.tsx'; |
|
|
|
|
|
|
|
import { GuideIcon } from '../ReactIcons/GuideIcon.tsx'; |
|
|
|
|
|
|
|
import { HomeIcon } from '../ReactIcons/HomeIcon.tsx'; |
|
|
|
|
|
|
|
import { VideoIcon } from '../ReactIcons/VideoIcon.tsx'; |
|
|
|
|
|
|
|
|
|
|
|
export type PageType = { |
|
|
|
export type PageType = { |
|
|
|
id: string; |
|
|
|
id: string; |
|
|
|
url: string; |
|
|
|
url: string; |
|
|
|
title: string; |
|
|
|
title: string; |
|
|
|
group: string; |
|
|
|
group: string; |
|
|
|
icon?: string; |
|
|
|
icon?: ReactElement; |
|
|
|
isProtected?: boolean; |
|
|
|
isProtected?: boolean; |
|
|
|
metadata?: Record<string, any>; |
|
|
|
metadata?: Record<string, any>; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const defaultPages: PageType[] = [ |
|
|
|
const defaultPages: PageType[] = [ |
|
|
|
{ id: 'home', url: '/', title: 'Home', group: 'Pages', icon: HomeIcon.src }, |
|
|
|
{ |
|
|
|
|
|
|
|
id: 'home', |
|
|
|
|
|
|
|
url: '/', |
|
|
|
|
|
|
|
title: 'Home', |
|
|
|
|
|
|
|
group: 'Pages', |
|
|
|
|
|
|
|
icon: <HomeIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
|
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
id: 'account', |
|
|
|
id: 'account', |
|
|
|
url: '/account', |
|
|
|
url: '/account', |
|
|
|
title: 'Account', |
|
|
|
title: 'Account', |
|
|
|
group: 'Pages', |
|
|
|
group: 'Pages', |
|
|
|
icon: UserIcon.src, |
|
|
|
icon: <UserIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
isProtected: true, |
|
|
|
isProtected: true, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
@ -37,7 +49,7 @@ const defaultPages: PageType[] = [ |
|
|
|
url: '/team', |
|
|
|
url: '/team', |
|
|
|
title: 'Teams', |
|
|
|
title: 'Teams', |
|
|
|
group: 'Pages', |
|
|
|
group: 'Pages', |
|
|
|
icon: GroupIcon.src, |
|
|
|
icon: <GroupIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
isProtected: true, |
|
|
|
isProtected: true, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
@ -45,7 +57,7 @@ const defaultPages: PageType[] = [ |
|
|
|
url: '/account/friends', |
|
|
|
url: '/account/friends', |
|
|
|
title: 'Friends', |
|
|
|
title: 'Friends', |
|
|
|
group: 'Pages', |
|
|
|
group: 'Pages', |
|
|
|
icon: GroupIcon.src, |
|
|
|
icon: <GroupIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
isProtected: true, |
|
|
|
isProtected: true, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
@ -53,14 +65,14 @@ const defaultPages: PageType[] = [ |
|
|
|
url: '/roadmaps', |
|
|
|
url: '/roadmaps', |
|
|
|
title: 'Roadmaps', |
|
|
|
title: 'Roadmaps', |
|
|
|
group: 'Pages', |
|
|
|
group: 'Pages', |
|
|
|
icon: RoadmapIcon.src, |
|
|
|
icon: <RoadmapIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
id: 'account-roadmaps', |
|
|
|
id: 'account-roadmaps', |
|
|
|
url: '/account/roadmaps', |
|
|
|
url: '/account/roadmaps', |
|
|
|
title: 'Custom Roadmaps', |
|
|
|
title: 'Custom Roadmaps', |
|
|
|
group: 'Pages', |
|
|
|
group: 'Pages', |
|
|
|
icon: RoadmapIcon.src, |
|
|
|
icon: <RoadmapIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
isProtected: true, |
|
|
|
isProtected: true, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
@ -68,28 +80,28 @@ const defaultPages: PageType[] = [ |
|
|
|
url: '/best-practices', |
|
|
|
url: '/best-practices', |
|
|
|
title: 'Best Practices', |
|
|
|
title: 'Best Practices', |
|
|
|
group: 'Pages', |
|
|
|
group: 'Pages', |
|
|
|
icon: BestPracticesIcon.src, |
|
|
|
icon: <BestPracticesIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
id: 'questions', |
|
|
|
id: 'questions', |
|
|
|
url: '/questions', |
|
|
|
url: '/questions', |
|
|
|
title: 'Questions', |
|
|
|
title: 'Questions', |
|
|
|
group: 'Pages', |
|
|
|
group: 'Pages', |
|
|
|
icon: ClipboardIcon.src, |
|
|
|
icon: <ClipboardIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
id: 'guides', |
|
|
|
id: 'guides', |
|
|
|
url: '/guides', |
|
|
|
url: '/guides', |
|
|
|
title: 'Guides', |
|
|
|
title: 'Guides', |
|
|
|
group: 'Pages', |
|
|
|
group: 'Pages', |
|
|
|
icon: GuideIcon.src, |
|
|
|
icon: <GuideIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
id: 'videos', |
|
|
|
id: 'videos', |
|
|
|
url: '/videos', |
|
|
|
url: '/videos', |
|
|
|
title: 'Videos', |
|
|
|
title: 'Videos', |
|
|
|
group: 'Pages', |
|
|
|
group: 'Pages', |
|
|
|
icon: VideoIcon.src, |
|
|
|
icon: <VideoIcon className="mr-2 h-4 w-4 stroke-2" />, |
|
|
|
}, |
|
|
|
}, |
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
@ -199,7 +211,7 @@ export function CommandMenu() { |
|
|
|
} else if (e.key === 'ArrowUp') { |
|
|
|
} else if (e.key === 'ArrowUp') { |
|
|
|
const canGoPrev = activeCounter > 0; |
|
|
|
const canGoPrev = activeCounter > 0; |
|
|
|
setActiveCounter( |
|
|
|
setActiveCounter( |
|
|
|
canGoPrev ? activeCounter - 1 : searchResults.length - 1 |
|
|
|
canGoPrev ? activeCounter - 1 : searchResults.length - 1, |
|
|
|
); |
|
|
|
); |
|
|
|
} else if (e.key === 'Tab') { |
|
|
|
} else if (e.key === 'Tab') { |
|
|
|
e.preventDefault(); |
|
|
|
e.preventDefault(); |
|
|
@ -242,13 +254,7 @@ export function CommandMenu() { |
|
|
|
{!page.icon && ( |
|
|
|
{!page.icon && ( |
|
|
|
<span className="mr-2 text-gray-400">{page.group}</span> |
|
|
|
<span className="mr-2 text-gray-400">{page.group}</span> |
|
|
|
)} |
|
|
|
)} |
|
|
|
{page.icon && ( |
|
|
|
{page.icon && page.icon} |
|
|
|
<img |
|
|
|
|
|
|
|
alt={page.title} |
|
|
|
|
|
|
|
src={page.icon} |
|
|
|
|
|
|
|
className="mr-2 h-4 w-4" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
{page.title} |
|
|
|
{page.title} |
|
|
|
</a> |
|
|
|
</a> |
|
|
|
</Fragment> |
|
|
|
</Fragment> |
|
|
|