11'use client' ;
22
3- import { ReactNode , useCallback , useRef } from 'react' ;
3+ import { ReactNode , useEffect , useRef } from 'react' ;
44
55interface SelectableTextProps {
66 children : ReactNode ;
@@ -16,46 +16,61 @@ export default function SelectableText({
1616 minSelectionLength = 2 ,
1717} : SelectableTextProps ) {
1818 const containerRef = useRef < HTMLDivElement > ( null ) ;
19+ const onTextSelectRef = useRef ( onTextSelect ) ;
20+ const onSelectionClearRef = useRef ( onSelectionClear ) ;
1921
20- const handleMouseUp = useCallback ( ( ) => {
21- setTimeout ( ( ) => {
22+ // Keep refs updated with latest callbacks
23+ useEffect ( ( ) => {
24+ onTextSelectRef . current = onTextSelect ;
25+ onSelectionClearRef . current = onSelectionClear ;
26+ } ) ;
27+
28+ useEffect ( ( ) => {
29+ const handleSelectionChange = ( ) => {
2230 const selection = window . getSelection ( ) ;
2331
2432 if ( ! selection || selection . isCollapsed ) {
25- onSelectionClear ( ) ;
33+ onSelectionClearRef . current ( ) ;
2634 return ;
2735 }
2836
2937 const selectedText = selection . toString ( ) . trim ( ) ;
3038
3139 if ( selectedText . length < minSelectionLength ) {
32- onSelectionClear ( ) ;
40+ onSelectionClearRef . current ( ) ;
3341 return ;
3442 }
3543
44+ // Check if selection is within our container
3645 if ( containerRef . current ) {
3746 const range = selection . getRangeAt ( 0 ) ;
3847 const selectionContainer = range . commonAncestorContainer ;
3948
4049 if ( ! containerRef . current . contains ( selectionContainer ) ) {
4150 return ;
4251 }
43- }
4452
45- const range = selection . getRangeAt ( 0 ) ;
46- const rect = range . getBoundingClientRect ( ) ;
53+ const rect = range . getBoundingClientRect ( ) ;
54+
55+ const position = {
56+ x : rect . left + rect . width / 2 ,
57+ y : rect . top ,
58+ } ;
59+
60+ onTextSelectRef . current ( selectedText , position ) ;
61+ }
62+ } ;
4763
48- const position = {
49- x : rect . left + rect . width / 2 ,
50- y : rect . top ,
51- } ;
64+ // Use selectionchange event - works on desktop (mouse/keyboard) and mobile (touch)
65+ document . addEventListener ( 'selectionchange' , handleSelectionChange ) ;
5266
53- onTextSelect ( selectedText , position ) ;
54- } , 10 ) ;
55- } , [ onTextSelect , onSelectionClear , minSelectionLength ] ) ;
67+ return ( ) => {
68+ document . removeEventListener ( 'selectionchange' , handleSelectionChange ) ;
69+ } ;
70+ } , [ minSelectionLength ] ) ;
5671
5772 return (
58- < div ref = { containerRef } onMouseUp = { handleMouseUp } className = "cursor-text" >
73+ < div ref = { containerRef } className = "cursor-text" >
5974 { children }
6075 </ div >
6176 ) ;
0 commit comments