1- import { ReactNode , useEffect , useMemo , useState } from 'react' ;
1+ import { ReactNode , useEffect , useMemo , useRef , useState } from 'react' ;
22import { useAnnotator , useSelection } from '@annotorious/react' ;
33import { isRevived , NOT_ANNOTATABLE_CLASS , TextAnnotation , TextAnnotator } from '@recogito/text-annotator' ;
44import { isMobile } from './isMobile' ;
55import {
6+ arrow ,
67 autoUpdate ,
78 flip ,
9+ FloatingArrow ,
10+ FloatingArrowProps ,
811 FloatingFocusManager ,
912 FloatingPortal ,
1013 inline ,
@@ -22,6 +25,10 @@ interface TextAnnotationPopupProps {
2225
2326 ariaCloseWarning ?: string ;
2427
28+ arrow ?: boolean ;
29+
30+ arrowProps ?: Omit < FloatingArrowProps , 'context' | 'ref' > ;
31+
2532 popup ( props : TextAnnotationPopupContentProps ) : ReactNode ;
2633
2734}
@@ -46,6 +53,22 @@ export const TextAnnotatorPopup = (props: TextAnnotationPopupProps) => {
4653
4754 const [ isOpen , setOpen ] = useState ( selected ?. length > 0 ) ;
4855
56+ const arrowRef = useRef ( null ) ;
57+
58+ // Conditional floating-ui middleware
59+ const middleware = useMemo ( ( ) => {
60+ const m = [
61+ inline ( ) ,
62+ offset ( 10 ) ,
63+ flip ( { crossAxis : true } ) ,
64+ shift ( { crossAxis : true , padding : 10 } )
65+ ] ;
66+
67+ return props . arrow
68+ ? [ ...m , arrow ( { element : arrowRef } ) ]
69+ : m ;
70+ } , [ props . arrow ] ) ;
71+
4972 const { refs, floatingStyles, update, context } = useFloating ( {
5073 placement : isMobile ( ) ? 'bottom' : 'top' ,
5174 open : isOpen ,
@@ -55,12 +78,7 @@ export const TextAnnotatorPopup = (props: TextAnnotationPopupProps) => {
5578 r ?. cancelSelected ( ) ;
5679 }
5780 } ,
58- middleware : [
59- offset ( 10 ) ,
60- inline ( ) ,
61- flip ( ) ,
62- shift ( { mainAxis : false , crossAxis : true , padding : 10 } )
63- ] ,
81+ middleware,
6482 whileElementsMounted : autoUpdate
6583 } ) ;
6684
@@ -132,6 +150,13 @@ export const TextAnnotatorPopup = (props: TextAnnotationPopupProps) => {
132150 event
133151 } ) }
134152
153+ { props . arrow && (
154+ < FloatingArrow
155+ ref = { arrowRef }
156+ context = { context }
157+ { ...( props . arrowProps || { } ) } />
158+ ) }
159+
135160 < button className = "r6o-popup-sr-only" aria-live = "assertive" onClick = { onClose } >
136161 { props . ariaCloseWarning || 'Click or leave this dialog to close it.' }
137162 </ button >
0 commit comments