Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ui/leafwiki-ui/src/features/search/SearchResultCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ const SearchResultCard = forwardRef<HTMLAnchorElement, SearchResultCardProps>(
const isEditorActive = currentEditorPageId === item.page_id
const isActive = isRouteActive || isEditorActive || isSelected
const kindLabel = item.kind === 'section' ? 'Section' : 'Page'
const searchQuery = new URLSearchParams(location.search).get('q') || undefined

return (
<Link
ref={ref}
to={resultUrl}
state={createNavigationVisitState()}
state={createNavigationVisitState({ leafwikiSearchQuery: searchQuery })}
data-testid={`search-result-card-${item.page_id}`}
aria-current={isRouteActive ? 'page' : undefined}
onMouseEnter={onMouseEnter}
Expand Down
2 changes: 2 additions & 0 deletions ui/leafwiki-ui/src/features/viewer/PageViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import Breadcrumbs from './Breadcrumbs'
import EmptySectionChildrenList from './EmptySectionChildrenList'
import { PageMetadata } from './PageMetadata'
import { useScrollToHeadline } from './useScrollToHeadline'
import { useScrollToSearchTerm } from './useScrollToSearchTerm'
import { useSetPageTitle } from './useSetPageTitle'
import { useToolbarActions } from './useToolbarActions'
import { useViewerStore } from './viewer'
Expand Down Expand Up @@ -81,6 +82,7 @@ export default function PageViewer() {

useScrollRestoration(getNavigationVisitKey(location), loading)
useScrollToHeadline({ content: page?.content || '', isLoading: loading })
useScrollToSearchTerm({ content: page?.content || '', isLoading: loading })
useToolbarActions(actions)
useSetPageTitle({ page })

Expand Down
84 changes: 84 additions & 0 deletions ui/leafwiki-ui/src/features/viewer/useScrollToSearchTerm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'

const SEARCH_QUERY_STATE_KEY = 'leafwikiSearchQuery'

export function getNavigationSearchQuery(state: unknown): string | undefined {
if (typeof state === 'object' && state !== null) {
const s = state as Record<string, unknown>
const q = s[SEARCH_QUERY_STATE_KEY]
if (typeof q === 'string' && q.length > 0) return q
}
return undefined
}

function cleanupHighlights(root: Element) {
root.querySelectorAll('mark.search-highlight').forEach((el) => {
const parent = el.parentNode
Comment on lines +7 to +9
if (!parent) return
parent.replaceChild(document.createTextNode(el.textContent ?? ''), el)
parent.normalize()
})
}

function highlightAndScroll(searchTerm: string) {
const contentEl = document.querySelector('.page-viewer__content')
if (!contentEl) return

cleanupHighlights(contentEl)

const walker = document.createTreeWalker(contentEl, NodeFilter.SHOW_TEXT)
const term = searchTerm.toLowerCase()

let node: Node | null
while ((node = walker.nextNode())) {
const text = node.textContent ?? ''
const idx = text.toLowerCase().indexOf(term)
if (idx === -1) continue

const parent = node.parentNode
if (!parent) continue

const mark = document.createElement('mark')
mark.className = 'search-highlight'
mark.textContent = text.slice(idx, idx + searchTerm.length)

Comment on lines +34 to +37
const fragment = document.createDocumentFragment()
const before = text.slice(0, idx)
const after = text.slice(idx + searchTerm.length)
if (before) fragment.appendChild(document.createTextNode(before))
fragment.appendChild(mark)
if (after) fragment.appendChild(document.createTextNode(after))

parent.replaceChild(fragment, node)
mark.scrollIntoView({ behavior: 'smooth', block: 'center' })
return
}
}

type Options = {
content?: string
isLoading?: boolean
}

export function useScrollToSearchTerm({ content, isLoading }: Options) {
const location = useLocation()
const searchQuery = getNavigationSearchQuery(location.state)

useEffect(() => {
if (isLoading || !content || !searchQuery || location.hash) return

const scrollContainer = document.getElementById('scroll-container')
if (!scrollContainer) return

const timeout = setTimeout(() => {
highlightAndScroll(searchQuery)
}, 500)

return () => {
clearTimeout(timeout)
const contentEl = document.querySelector('.page-viewer__content')
if (contentEl) cleanupHighlights(contentEl)
}
}, [content, isLoading, searchQuery, location.hash])
}
4 changes: 4 additions & 0 deletions ui/leafwiki-ui/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,10 @@
@apply text-muted text-xs;
}

.search-highlight {
@apply rounded-xs bg-yellow-300/70 dark:bg-yellow-400/30;
}
Comment on lines +1392 to +1394

.tags-result-card__link {
@apply block text-inherit no-underline;
}
Expand Down
Loading