mirror of
https://github.com/linsa-io/linsa.git
synced 2026-01-12 12:20:23 +01:00
* feat: keyboard nav * fix: link update * feat: reusable learning state * chore: use new learning state * feat: add to my profile * . * . * feat: on enter open the link * fix: lint * fix: use eslint v8 instead of v9 * fix: add to my profile * chore: update personal link schema * chore: update personal page schema * fix: update detail wrapper * fix: update page section * removing option for learning status * removing option for learning status for topic * feat: add createdAt and updatedAt for personal Page * chore: update page section component * chore: remove chevron from sub menu * fix: sidebar * chore: add focus and disable toast * feat: la editor add execption for no command class * fix: la editor style and fix page detail * fix: title * fix: topic learning state * chore: add showSearch for learning state * fix: bunch stuff * chore: link list and item handle learning state * chore: set expand to false * feat: personal link for topic detail * chore: hook use topic data * chore: go to list * fix: link and topic * feat(utils): new keyboard utils * feat(store): add linkOpenPopoverForIdAtom for link * chore: using memo for use topic data * fix: remove duplicate component * chore: performance for topic detail lint item * refactor: remove LinkOptions component * chore: improve performance for list * feat: added LinkRoute copmonent * chore: link manage * feat: bottom bar * fix: link * fix: page wrapper * fix: import thing * chore: added a displayname * refactor: page detail * refactor: page detail * fix: add topic to personal link form link * fix: only show page count if more than zero * fix: sidebar topic section --------- Co-authored-by: Nikita <github@nikiv.dev> Co-authored-by: marshennikovaolga <marshennikova@gmail.com>
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import { useState, useRef, useCallback, useEffect } from "react"
|
|
import { Link as LinkSchema } from "@/lib/schema"
|
|
import { ensureUrlProtocol } from "@/lib/utils"
|
|
|
|
export function useLinkNavigation(allLinks: (LinkSchema | null)[]) {
|
|
const [activeIndex, setActiveIndex] = useState(-1)
|
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
const linkRefs = useRef<(HTMLLIElement | null)[]>(allLinks.map(() => null))
|
|
|
|
const scrollToLink = useCallback((index: number) => {
|
|
if (linkRefs.current[index] && containerRef.current) {
|
|
const linkElement = linkRefs.current[index]
|
|
const container = containerRef.current
|
|
|
|
const linkRect = linkElement?.getBoundingClientRect()
|
|
const containerRect = container.getBoundingClientRect()
|
|
|
|
if (linkRect && containerRect) {
|
|
if (linkRect.bottom > containerRect.bottom) {
|
|
container.scrollTop += linkRect.bottom - containerRect.bottom
|
|
} else if (linkRect.top < containerRect.top) {
|
|
container.scrollTop -= containerRect.top - linkRect.top
|
|
}
|
|
}
|
|
}
|
|
}, [])
|
|
|
|
const handleKeyDown = useCallback(
|
|
(e: KeyboardEvent) => {
|
|
console.log("handleKeyDown")
|
|
if (e.key === "ArrowDown") {
|
|
e.preventDefault()
|
|
setActiveIndex(prevIndex => {
|
|
const newIndex = (prevIndex + 1) % allLinks.length
|
|
scrollToLink(newIndex)
|
|
return newIndex
|
|
})
|
|
} else if (e.key === "ArrowUp") {
|
|
e.preventDefault()
|
|
setActiveIndex(prevIndex => {
|
|
const newIndex = (prevIndex - 1 + allLinks.length) % allLinks.length
|
|
scrollToLink(newIndex)
|
|
return newIndex
|
|
})
|
|
} else if (e.key === "Enter" && activeIndex !== -1) {
|
|
const link = allLinks[activeIndex]
|
|
if (link) {
|
|
window.open(ensureUrlProtocol(link.url), "_blank")
|
|
}
|
|
}
|
|
},
|
|
[activeIndex, allLinks, scrollToLink]
|
|
)
|
|
|
|
useEffect(() => {
|
|
window.addEventListener("keydown", handleKeyDown)
|
|
return () => window.removeEventListener("keydown", handleKeyDown)
|
|
}, [handleKeyDown])
|
|
|
|
return { activeIndex, setActiveIndex, containerRef, linkRefs }
|
|
}
|