mirror of
https://github.com/dergigi/boris.git
synced 2026-01-06 08:24:27 +01:00
feat: add click-to-scroll for highlights
- Clicking a highlight in the main text scrolls to it in the sidebar - Selected highlight is visually highlighted with border and shadow - Add selectedHighlightId state management in Bookmarks component - Add click handlers to mark elements in ContentPanel - Add isSelected prop to HighlightItem with scroll-into-view - Add CSS styles for selected highlight state - Set cursor to pointer on clickable highlights Users can now click on highlighted text to jump to the corresponding highlight in the right sidebar for easy navigation.
This commit is contained in:
@@ -29,6 +29,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
|
||||
const [isHighlightsCollapsed, setIsHighlightsCollapsed] = useState(false)
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('cards')
|
||||
const [showUnderlines, setShowUnderlines] = useState(true)
|
||||
const [selectedHighlightId, setSelectedHighlightId] = useState<string | undefined>(undefined)
|
||||
const activeAccount = Hooks.useActiveAccount()
|
||||
const accountManager = Hooks.useAccountManager()
|
||||
|
||||
@@ -125,6 +126,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
|
||||
selectedUrl={selectedUrl}
|
||||
highlights={highlights}
|
||||
showUnderlines={showUnderlines}
|
||||
onHighlightClick={setSelectedHighlightId}
|
||||
/>
|
||||
</div>
|
||||
<div className="pane highlights">
|
||||
@@ -136,6 +138,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
|
||||
onSelectUrl={handleSelectUrl}
|
||||
selectedUrl={selectedUrl}
|
||||
onToggleUnderlines={setShowUnderlines}
|
||||
selectedHighlightId={selectedHighlightId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ interface ContentPanelProps {
|
||||
selectedUrl?: string
|
||||
highlights?: Highlight[]
|
||||
showUnderlines?: boolean
|
||||
onHighlightClick?: (highlightId: string) => void
|
||||
}
|
||||
|
||||
const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
@@ -23,7 +24,8 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
markdown,
|
||||
selectedUrl,
|
||||
highlights = [],
|
||||
showUnderlines = true
|
||||
showUnderlines = true,
|
||||
onHighlightClick
|
||||
}) => {
|
||||
const contentRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -136,13 +138,27 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
if (originalHTML !== highlightedHTML) {
|
||||
console.log('✅ Applied highlights to DOM')
|
||||
contentRef.current.innerHTML = highlightedHTML
|
||||
|
||||
// Add click handlers to all highlight marks
|
||||
if (onHighlightClick) {
|
||||
const marks = contentRef.current.querySelectorAll('mark.content-highlight')
|
||||
marks.forEach(mark => {
|
||||
const highlightId = mark.getAttribute('data-highlight-id')
|
||||
if (highlightId) {
|
||||
mark.addEventListener('click', () => {
|
||||
onHighlightClick(highlightId)
|
||||
})
|
||||
;(mark as HTMLElement).style.cursor = 'pointer'
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.log('⚠️ No changes made to DOM')
|
||||
}
|
||||
})
|
||||
|
||||
return () => cancelAnimationFrame(rafId)
|
||||
}, [relevantHighlights, html, markdown, showUnderlines])
|
||||
}, [relevantHighlights, html, markdown, showUnderlines, onHighlightClick])
|
||||
|
||||
const highlightedMarkdown = useMemo(() => {
|
||||
if (!markdown || relevantHighlights.length === 0) return markdown
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faQuoteLeft, faLink, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Highlight } from '../types/highlights'
|
||||
@@ -7,9 +7,17 @@ import { formatDistanceToNow } from 'date-fns'
|
||||
interface HighlightItemProps {
|
||||
highlight: Highlight
|
||||
onSelectUrl?: (url: string) => void
|
||||
isSelected?: boolean
|
||||
}
|
||||
|
||||
export const HighlightItem: React.FC<HighlightItemProps> = ({ highlight, onSelectUrl }) => {
|
||||
export const HighlightItem: React.FC<HighlightItemProps> = ({ highlight, onSelectUrl, isSelected }) => {
|
||||
const itemRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (isSelected && itemRef.current) {
|
||||
itemRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
}
|
||||
}, [isSelected])
|
||||
const handleLinkClick = (url: string, e: React.MouseEvent) => {
|
||||
if (onSelectUrl) {
|
||||
e.preventDefault()
|
||||
@@ -27,7 +35,7 @@ export const HighlightItem: React.FC<HighlightItemProps> = ({ highlight, onSelec
|
||||
const sourceLink = getSourceLink()
|
||||
|
||||
return (
|
||||
<div className="highlight-item">
|
||||
<div ref={itemRef} className={`highlight-item ${isSelected ? 'selected' : ''}`} data-highlight-id={highlight.id}>
|
||||
<div className="highlight-quote-icon">
|
||||
<FontAwesomeIcon icon={faQuoteLeft} />
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@ interface HighlightsPanelProps {
|
||||
onSelectUrl?: (url: string) => void
|
||||
selectedUrl?: string
|
||||
onToggleUnderlines?: (show: boolean) => void
|
||||
selectedHighlightId?: string
|
||||
}
|
||||
|
||||
export const HighlightsPanel: React.FC<HighlightsPanelProps> = ({
|
||||
@@ -21,7 +22,8 @@ export const HighlightsPanel: React.FC<HighlightsPanelProps> = ({
|
||||
onToggleCollapse,
|
||||
onSelectUrl,
|
||||
selectedUrl,
|
||||
onToggleUnderlines
|
||||
onToggleUnderlines,
|
||||
selectedHighlightId
|
||||
}) => {
|
||||
const [showUnderlines, setShowUnderlines] = useState(true)
|
||||
|
||||
@@ -121,6 +123,7 @@ export const HighlightsPanel: React.FC<HighlightsPanelProps> = ({
|
||||
key={highlight.id}
|
||||
highlight={highlight}
|
||||
onSelectUrl={onSelectUrl}
|
||||
isSelected={highlight.id === selectedHighlightId}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user