@@ -100,7 +100,20 @@ export function BaseCard({
100100 const [ isDeleteModalOpen , setIsDeleteModalOpen ] = useState ( false )
101101 const [ isTagsModalOpen , setIsTagsModalOpen ] = useState ( false )
102102 const [ isDeleting , setIsDeleting ] = useState ( false )
103+
104+ /**
105+ * Guards against context menu actions triggering card navigation.
106+ * The card's onClick fires synchronously during the click event bubble phase,
107+ * so the ref is checked before the setTimeout-0 callback resets it.
108+ */
103109 const actionTakenRef = useRef ( false )
110+ const withActionGuard = useCallback ( ( fn : ( ) => void ) => {
111+ actionTakenRef . current = true
112+ fn ( )
113+ setTimeout ( ( ) => {
114+ actionTakenRef . current = false
115+ } , 0 )
116+ } , [ ] )
104117
105118 const searchParams = new URLSearchParams ( {
106119 kbName : title ,
@@ -131,45 +144,25 @@ export function BaseCard({
131144 )
132145
133146 const handleOpenInNewTab = useCallback ( ( ) => {
134- actionTakenRef . current = true
135- window . open ( href , '_blank' )
136- setTimeout ( ( ) => {
137- actionTakenRef . current = false
138- } , 0 )
139- } , [ href ] )
147+ withActionGuard ( ( ) => window . open ( href , '_blank' ) )
148+ } , [ href , withActionGuard ] )
140149
141150 const handleViewTags = useCallback ( ( ) => {
142- actionTakenRef . current = true
143- setIsTagsModalOpen ( true )
144- setTimeout ( ( ) => {
145- actionTakenRef . current = false
146- } , 0 )
147- } , [ ] )
151+ withActionGuard ( ( ) => setIsTagsModalOpen ( true ) )
152+ } , [ withActionGuard ] )
148153
149154 const handleEdit = useCallback ( ( ) => {
150- actionTakenRef . current = true
151- setIsEditModalOpen ( true )
152- setTimeout ( ( ) => {
153- actionTakenRef . current = false
154- } , 0 )
155- } , [ ] )
155+ withActionGuard ( ( ) => setIsEditModalOpen ( true ) )
156+ } , [ withActionGuard ] )
156157
157158 const handleDelete = useCallback ( ( ) => {
158- actionTakenRef . current = true
159- setIsDeleteModalOpen ( true )
160- setTimeout ( ( ) => {
161- actionTakenRef . current = false
162- } , 0 )
163- } , [ ] )
159+ withActionGuard ( ( ) => setIsDeleteModalOpen ( true ) )
160+ } , [ withActionGuard ] )
164161
165162 const handleCopyId = useCallback ( ( ) => {
166163 if ( ! id ) return
167- actionTakenRef . current = true
168- navigator . clipboard . writeText ( id )
169- setTimeout ( ( ) => {
170- actionTakenRef . current = false
171- } , 0 )
172- } , [ id ] )
164+ withActionGuard ( ( ) => navigator . clipboard . writeText ( id ) )
165+ } , [ id , withActionGuard ] )
173166
174167 const handleConfirmDelete = useCallback ( async ( ) => {
175168 if ( ! id || ! onDelete ) return
0 commit comments