diff --git a/inertia/components/common/dropdown/dropdown.tsx b/inertia/components/common/dropdown/dropdown.tsx index b578c31..43e7ff0 100644 --- a/inertia/components/common/dropdown/dropdown.tsx +++ b/inertia/components/common/dropdown/dropdown.tsx @@ -1,12 +1,10 @@ -import KEYS from '#constants/keys'; import styled from '@emotion/styled'; import { ReactNode, useRef } from 'react'; -import { useHotkeys } from 'react-hotkeys-hook'; import DropdownContainer from '~/components/common/dropdown/dropdown_container'; import DropdownLabel from '~/components/common/dropdown/dropdown_label'; import useClickOutside from '~/hooks/use_click_outside'; -import useGlobalHotkeys from '~/hooks/use_global_hotkeys'; import useToggle from '~/hooks/use_modal'; +import useShortcut from '~/hooks/use_shortcut'; const DropdownStyle = styled.div<{ opened: boolean; svgSize?: number }>( ({ opened, theme, svgSize = 24 }) => ({ @@ -44,14 +42,9 @@ export default function Dropdown({ }) { const dropdownRef = useRef(null); const { isShowing, toggle, close } = useToggle(); - const { globalHotkeysEnabled } = useGlobalHotkeys(); useClickOutside(dropdownRef, close); - - useHotkeys(KEYS.ESCAPE_KEY, close, { - enabled: globalHotkeysEnabled, - enableOnFormTags: ['INPUT'], - }); + useShortcut('ESCAPE_KEY', close); return ( { - router.visit(appendCollectionId(route('dashboard').url, collectionId)); - }, - { enabled: globalHotkeysEnabled, enableOnFormTags: ['INPUT'] } + useShortcut('ESCAPE_KEY', () => + router.visit(appendCollectionId(route('dashboard').url, collectionId)) ); return <>{children}; } diff --git a/inertia/components/dashboard/collection/list/collection_item.tsx b/inertia/components/dashboard/collection/list/collection_item.tsx index 609880e..32c271e 100644 --- a/inertia/components/dashboard/collection/list/collection_item.tsx +++ b/inertia/components/dashboard/collection/list/collection_item.tsx @@ -1,8 +1,34 @@ +import type Collection from '#models/collection'; import styled from '@emotion/styled'; +import { AiFillFolderOpen, AiOutlineFolder } from 'react-icons/ai'; +import TextEllipsis from '~/components/common/text_ellipsis'; import { Item } from '~/components/dashboard/side_nav/nav_item'; +import useActiveCollection from '~/hooks/use_active_collection'; -const CollectionItem = styled(Item)(({ theme }) => ({ - backgroundColor: theme.colors.secondary, -})); +const CollectionItemStyle = styled(Item)<{ isActive: boolean }>( + ({ theme, isActive }) => ({ + cursor: 'pointer', + color: isActive ? theme.colors.primary : theme.colors.font, + backgroundColor: theme.colors.secondary, + }) +); -export default CollectionItem; +export default function CollectionItem({ + collection, +}: { + collection: Collection; +}) { + const { activeCollection, setActiveCollection } = useActiveCollection(); + const isActiveCollection = collection.id === activeCollection?.id; + const FolderIcon = isActiveCollection ? AiFillFolderOpen : AiOutlineFolder; + + return ( + setActiveCollection(collection)} + isActive={isActiveCollection} + > + + {collection.name} + + ); +} diff --git a/inertia/components/dashboard/collection/list/collection_list.tsx b/inertia/components/dashboard/collection/list/collection_list.tsx index 48e3c2d..c5afb4c 100644 --- a/inertia/components/dashboard/collection/list/collection_list.tsx +++ b/inertia/components/dashboard/collection/list/collection_list.tsx @@ -1,11 +1,9 @@ -import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { AiFillFolderOpen, AiOutlineFolder } from 'react-icons/ai'; -import TextEllipsis from '~/components/common/text_ellipsis'; import CollectionItem from '~/components/dashboard/collection/list/collection_item'; import CollectionListContainer from '~/components/dashboard/collection/list/collection_list_container'; import useActiveCollection from '~/hooks/use_active_collection'; import useCollections from '~/hooks/use_collections'; +import useShortcut from '~/hooks/use_shortcut'; const SideMenu = styled.nav(({ theme }) => ({ height: '100%', @@ -31,33 +29,41 @@ const CollectionListStyle = styled.div({ }); export default function CollectionList() { - const { activeCollection, setActiveCollection } = useActiveCollection(); const { collections } = useCollections(); - const theme = useTheme(); + const { activeCollection, setActiveCollection } = useActiveCollection(); + + const goToPreviousCollection = () => { + const currentCategoryIndex = collections.findIndex( + ({ id }) => id === activeCollection?.id + ); + if (currentCategoryIndex === -1 || currentCategoryIndex === 0) return; + + setActiveCollection(collections[currentCategoryIndex - 1]); + }; + + const goToNextCollection = () => { + const currentCategoryIndex = collections.findIndex( + ({ id }) => id === activeCollection?.id + ); + if ( + currentCategoryIndex === -1 || + currentCategoryIndex === collections.length - 1 + ) + return; + + setActiveCollection(collections[currentCategoryIndex + 1]); + }; + + useShortcut('ARROW_UP', goToPreviousCollection); + useShortcut('ARROW_DOWN', goToNextCollection); + return ( Collections • {collections.length} {collections.map((collection) => ( - setActiveCollection(collection)} - key={collection.id} - > - {collection.id === activeCollection?.id ? ( - - ) : ( - - )} - {collection.name} - + ))} diff --git a/inertia/hooks/use_shortcut.tsx b/inertia/hooks/use_shortcut.tsx new file mode 100644 index 0000000..c5f19fa --- /dev/null +++ b/inertia/hooks/use_shortcut.tsx @@ -0,0 +1,11 @@ +import KEYS from '#constants/keys'; +import { useHotkeys } from 'react-hotkeys-hook'; +import useGlobalHotkeys from '~/hooks/use_global_hotkeys'; + +export default function useShortcut(key: keyof typeof KEYS, cb: () => void) { + const { globalHotkeysEnabled } = useGlobalHotkeys(); + return useHotkeys(KEYS[key], cb, { + enabled: globalHotkeysEnabled, + enableOnFormTags: ['INPUT'], + }); +} diff --git a/inertia/pages/dashboard.tsx b/inertia/pages/dashboard.tsx index 97fd521..5e91621 100644 --- a/inertia/pages/dashboard.tsx +++ b/inertia/pages/dashboard.tsx @@ -1,11 +1,9 @@ -import KEYS from '#constants/keys'; import type Collection from '#models/collection'; import Link from '#models/link'; import styled from '@emotion/styled'; import { router } from '@inertiajs/react'; import { route } from '@izzyjs/route/client'; import { ReactNode, useEffect, useMemo, useState } from 'react'; -import { useHotkeys } from 'react-hotkeys-hook'; import { useSwipeable } from 'react-swipeable'; import CollectionContainer from '~/components/dashboard/collection/collection_container'; import CollectionList from '~/components/dashboard/collection/list/collection_list'; @@ -18,6 +16,7 @@ import FavoritesContext from '~/contexts/favorites_context'; import GlobalHotkeysContext from '~/contexts/global_hotkeys_context'; import { useMediaQuery } from '~/hooks/use_media_query'; import useToggle from '~/hooks/use_modal'; +import useShortcut from '~/hooks/use_shortcut'; import { appendCollectionId } from '~/lib/navigation'; interface DashboardPageProps { @@ -112,21 +111,13 @@ function DashboardProviders( [globalHotkeysEnabled] ); - useHotkeys( - KEYS.OPEN_CREATE_LINK_KEY, - () => { - router.visit( - appendCollectionId(route('link.create-form').url, activeCollection?.id) - ); - }, - { enabled: globalHotkeysEnabled } + useShortcut('OPEN_CREATE_LINK_KEY', () => + router.visit( + appendCollectionId(route('link.create-form').url, activeCollection?.id) + ) ); - useHotkeys( - KEYS.OPEN_CREATE_COLLECTION_KEY, - () => { - router.visit(route('collection.create-form').url); - }, - { enabled: globalHotkeysEnabled } + useShortcut('OPEN_CREATE_COLLECTION_KEY', () => + router.visit(route('collection.create-form').url) ); return (