@@ -4,7 +4,7 @@ import { useEffect } from 'react';
4
4
* Custom hook for keyboard navigation in chatbot options
5
5
* Provides arrow key navigation, Enter/Space selection, and accessibility features
6
6
*/
7
- const useKeyboardNavigation = ( containerRef = null ) => {
7
+ const useKeyboardNavigation = ( ) => {
8
8
useEffect ( ( ) => {
9
9
// Dedicated checkbox navigation handler
10
10
const handleCheckboxNavigation = ( event , elements ) => {
@@ -277,22 +277,11 @@ const useKeyboardNavigation = (containerRef = null) => {
277
277
} ;
278
278
279
279
// Auto-focus first option when new options appear
280
- const handleNewOptions = ( ) => {
281
- // Use the provided container ref, or fall back to document search as last resort
282
- let chatWindow = null ;
280
+ const handleNewOptions = ( targetContainer = null ) => {
281
+ let chatWindow = targetContainer ;
283
282
284
- if ( containerRef && containerRef . current ) {
285
- // First try to find chat window within the specific bot container
286
- chatWindow = containerRef . current . querySelector ( '.rcb-chat-window' ) ;
287
- if ( ! chatWindow ) {
288
- chatWindow = containerRef . current . querySelector ( '[class*="rcb-chat"]' ) ;
289
- }
290
- if ( ! chatWindow ) {
291
- // If this container IS the qa-bot, use it directly
292
- chatWindow = containerRef . current . classList . contains ( 'qa-bot' ) ? containerRef . current : containerRef . current . querySelector ( '.qa-bot' ) ;
293
- }
294
- } else {
295
- // Fallback to global search (for backwards compatibility)
283
+ // If no specific container provided, try to find one
284
+ if ( ! chatWindow ) {
296
285
chatWindow = document . querySelector ( '.rcb-chat-window' ) ;
297
286
if ( ! chatWindow ) {
298
287
chatWindow = document . querySelector ( '[class*="rcb-chat"]' ) ;
@@ -456,40 +445,33 @@ const useKeyboardNavigation = (containerRef = null) => {
456
445
457
446
458
447
if ( hasNewOptions || hasNewCheckboxes ) {
459
- // Clear tabindex from existing elements, but scope to this bot's container
460
- const scopeElement = ( containerRef && containerRef . current ) ? containerRef . current : document ;
448
+ // Find which bot container this mutation occurred in
449
+ let botContainer = mutation . target ;
450
+ while ( botContainer && ! botContainer . classList ?. contains ( 'qa-bot' ) && ! botContainer . classList ?. contains ( 'rcb-chat-window' ) ) {
451
+ botContainer = botContainer . parentElement ;
452
+ }
453
+
454
+ // If we found a bot container, scope operations to it; otherwise fallback to global
455
+ const scopeElement = botContainer || document ;
456
+
457
+ // Clear tabindex from existing elements within this bot
461
458
scopeElement . querySelectorAll ( '.rcb-options[tabindex], .rcb-checkbox-row-container[tabindex], .rcb-checkbox-next-button[tabindex]' ) . forEach ( el => {
462
459
el . setAttribute ( 'tabindex' , '-1' ) ;
463
460
el . classList . remove ( 'keyboard-focused' ) ;
464
461
} ) ;
465
462
466
- setTimeout ( handleNewOptions , 100 ) ;
463
+ // Handle new options for this specific bot
464
+ setTimeout ( ( ) => handleNewOptions ( botContainer ) , 100 ) ;
467
465
}
468
466
}
469
467
} ) ;
470
468
} ) ;
471
469
472
- // Start observing - scope to this bot's container
473
- let observeTarget = null ;
474
-
475
- if ( containerRef && containerRef . current ) {
476
- // First try to find chat window within the specific bot container
477
- observeTarget = containerRef . current . querySelector ( '.rcb-chat-window' ) ;
478
- if ( ! observeTarget ) {
479
- // If no chat window found, observe the container itself
480
- observeTarget = containerRef . current ;
481
- }
482
- } else {
483
- // Fallback to global search (for backwards compatibility)
484
- observeTarget = document . querySelector ( '.rcb-chat-window' ) ;
485
- }
486
-
487
- if ( observeTarget ) {
488
- observer . observe ( observeTarget , {
489
- childList : true ,
490
- subtree : true
491
- } ) ;
492
- }
470
+ // Start observing the document body for changes in any bot
471
+ observer . observe ( document . body , {
472
+ childList : true ,
473
+ subtree : true
474
+ } ) ;
493
475
494
476
// Add keyboard event listener to document only
495
477
document . addEventListener ( 'keydown' , handleKeyboardNavigation ) ;
@@ -500,29 +482,29 @@ const useKeyboardNavigation = (containerRef = null) => {
500
482
501
483
// Periodic check as backup to catch any missed options and checkboxes
502
484
const periodicCheck = setInterval ( ( ) => {
503
- // Scope the periodic check to this bot's container
504
- const scopeElement = ( containerRef && containerRef . current ) ? containerRef . current : document ;
485
+ // Check each bot container individually
486
+ document . querySelectorAll ( '.qa-bot' ) . forEach ( botContainer => {
487
+ const hasVisibleOptions = botContainer . querySelectorAll ( '.rcb-options-container .rcb-options' ) . length > 0 ;
488
+ const hasVisibleCheckboxes = botContainer . querySelectorAll ( '.rcb-checkbox-row-container' ) . length > 0 ;
505
489
506
- const hasVisibleOptions = scopeElement . querySelectorAll ( '.rcb-options-container .rcb-options' ) . length > 0 ;
507
- const hasVisibleCheckboxes = scopeElement . querySelectorAll ( '.rcb-checkbox-row-container' ) . length > 0 ;
490
+ if ( hasVisibleOptions ) {
491
+ const lastProcessedOptions = botContainer . querySelectorAll ( '.rcb-options[tabindex]' ) . length ;
492
+ const currentOptions = botContainer . querySelectorAll ( '.rcb-options' ) . length ;
508
493
509
- if ( hasVisibleOptions ) {
510
- const lastProcessedOptions = scopeElement . querySelectorAll ( '.rcb-options[tabindex]' ) . length ;
511
- const currentOptions = scopeElement . querySelectorAll ( '.rcb-options' ) . length ;
512
-
513
- if ( currentOptions > lastProcessedOptions ) {
514
- handleNewOptions ( ) ;
494
+ if ( currentOptions > lastProcessedOptions ) {
495
+ handleNewOptions ( botContainer ) ;
496
+ }
515
497
}
516
- }
517
498
518
- if ( hasVisibleCheckboxes ) {
519
- const lastProcessedCheckboxes = scopeElement . querySelectorAll ( '.rcb-checkbox-row-container[tabindex]' ) . length ;
520
- const currentCheckboxes = scopeElement . querySelectorAll ( '.rcb-checkbox-row-container' ) . length ;
499
+ if ( hasVisibleCheckboxes ) {
500
+ const lastProcessedCheckboxes = botContainer . querySelectorAll ( '.rcb-checkbox-row-container[tabindex]' ) . length ;
501
+ const currentCheckboxes = botContainer . querySelectorAll ( '.rcb-checkbox-row-container' ) . length ;
521
502
522
- if ( currentCheckboxes > lastProcessedCheckboxes ) {
523
- handleNewOptions ( ) ;
503
+ if ( currentCheckboxes > lastProcessedCheckboxes ) {
504
+ handleNewOptions ( botContainer ) ;
505
+ }
524
506
}
525
- }
507
+ } ) ;
526
508
} , 1000 ) ;
527
509
528
510
// Cleanup
0 commit comments