stacker.news/components/use-quote-reply.js

44 lines
1.5 KiB
JavaScript
Raw Normal View History

2023-11-21 18:35:37 +00:00
import { useCallback, useEffect, useRef, useState } from 'react'
import { quote as quoteMd } from '@/lib/md'
export function useQuoteReply ({ text }) {
const ref = useRef(null)
const [quote, setQuote] = useState(null)
2023-11-21 18:35:37 +00:00
const [selection, setSelection] = useState(null)
const to = useRef(null)
2023-11-21 18:35:37 +00:00
const onSelectionChange = useCallback(e => {
clearTimeout(to.current)
const selection = window.getSelection()
const selectedText = selection.isCollapsed ? undefined : selection.toString()
const isSelectedTextInTarget = ref?.current?.contains(selection.anchorNode)
2023-11-21 18:35:37 +00:00
if ((selection.isCollapsed || !isSelectedTextInTarget || !selectedText)) {
// selection is collapsed or not in target or empty
// but on button click we don't want to immediately clear selection
to.current = setTimeout(() => setSelection(null), 1000)
return
}
setSelection(selectedText)
}, [ref?.current, setSelection])
useEffect(() => {
document.addEventListener('selectionchange', onSelectionChange)
return () => document.removeEventListener('selectionchange', onSelectionChange)
}, [])
2023-11-21 18:35:37 +00:00
const quoteReply = useCallback(({ selectionOnly }) => {
if (selectionOnly && !selection) return
const textToQuote = selection || text
setQuote(quoteMd(textToQuote))
2023-11-21 18:35:37 +00:00
}, [selection, text])
const cancelQuote = useCallback(() => {
setQuote(null)
2023-11-21 18:35:37 +00:00
setSelection(null)
}, [setQuote, setSelection])
return { ref, quote, quoteReply, cancelQuote }
}