11import { DEBUG } from './debug.js' ;
22import { createRadialUrchin } from './ui/visuals/RadialUrchin.js' ;
33
4+ console . info ( '[app] app.js loaded' ) ;
5+
46const tabList = document . querySelector ( '.tab-bar[role="tablist"]' ) ;
57if ( tabList instanceof HTMLElement && ! tabList . hasAttribute ( 'aria-orientation' ) ) {
68 tabList . setAttribute ( 'aria-orientation' , 'horizontal' ) ;
@@ -1045,11 +1047,16 @@ let consoleIndicator;
10451047let pendingAutoSwitch = false ;
10461048
10471049function focusTabPanel ( targetTab ) {
1048- const panel = tabPanelMap . get ( targetTab ) ;
1050+ if ( typeof targetTab !== 'string' ) {
1051+ return ;
1052+ }
1053+ const normalized = targetTab . toLowerCase ( ) ;
1054+ collectRootTabElements ( ) ;
1055+ const panel = tabPanelMap . get ( normalized ) ;
10491056 if ( ! panel ) {
10501057 return ;
10511058 }
1052- const heading = tabHeadings . get ( targetTab ) ;
1059+ const heading = tabHeadings . get ( normalized ) ;
10531060 const focusTarget = heading instanceof HTMLElement ? heading : panel ;
10541061 if ( focusTarget && typeof focusTarget . focus === 'function' ) {
10551062 try {
@@ -1060,28 +1067,32 @@ function focusTabPanel(targetTab) {
10601067 }
10611068}
10621069
1063- function goToRootTab ( targetTab , options = { } ) {
1064- console . info ( '[root-tabs] switching to' , targetTab ) ;
1065- const { focusPanel = false } = options ;
1066- if ( typeof targetTab !== 'string' ) {
1070+ function goToRootTab ( id ) {
1071+ if ( typeof id !== 'string' || ! id ) {
10671072 return ;
10681073 }
1069- const normalizedTarget = targetTab . toLowerCase ( ) ;
1074+ console . info ( '[root-tabs] switching to' , id ) ;
1075+ const normalizedTarget = id . toLowerCase ( ) ;
10701076 if ( ! tabOrder . includes ( normalizedTarget ) ) {
10711077 return ;
10721078 }
10731079
1074- tabButtons . forEach ( ( button ) => {
1075- const buttonId = ( button . dataset . rootTab || button . dataset . tab || '' ) . toLowerCase ( ) ;
1080+ collectRootTabElements ( ) ;
1081+
1082+ const tabs = document . querySelectorAll ( '[data-root-tab]' ) ;
1083+ const panels = document . querySelectorAll ( '[data-root-panel]' ) ;
1084+
1085+ tabs . forEach ( ( button ) => {
1086+ const buttonId = ( button . dataset . rootTab || '' ) . toLowerCase ( ) ;
10761087 const isActive = buttonId === normalizedTarget ;
10771088 button . classList . toggle ( 'active' , isActive ) ;
10781089 button . classList . toggle ( 'root-tab--active' , isActive ) ;
10791090 button . setAttribute ( 'aria-selected' , isActive ? 'true' : 'false' ) ;
10801091 button . setAttribute ( 'tabindex' , isActive ? '0' : '-1' ) ;
10811092 } ) ;
10821093
1083- tabPanels . forEach ( ( panel ) => {
1084- const panelId = ( panel . dataset . rootPanel || panel . dataset . tab || '' ) . toLowerCase ( ) ;
1094+ panels . forEach ( ( panel ) => {
1095+ const panelId = ( panel . dataset . rootPanel || '' ) . toLowerCase ( ) ;
10851096 const isActive = panelId === normalizedTarget ;
10861097 panel . classList . toggle ( 'active' , isActive ) ;
10871098 panel . classList . toggle ( 'root-panel--active' , isActive ) ;
@@ -1101,61 +1112,68 @@ function goToRootTab(targetTab, options = {}) {
11011112 } ) ;
11021113 pendingAutoSwitch = false ;
11031114 }
1104- if ( focusPanel ) {
1105- focusTabPanel ( normalizedTarget ) ;
1106- }
11071115}
11081116
11091117function initRootTabs ( ) {
1110- if ( ! document . body || document . body . dataset . rootTabsHydrated === '1' ) {
1118+ if ( ! document . body ) {
1119+ return ;
1120+ }
1121+ if ( document . body . dataset . rootTabsHydrated === '1' ) {
1122+ console . info ( '[root-tabs] already hydrated' ) ;
11111123 return ;
11121124 }
1113-
1114- collectRootTabElements ( ) ;
1115-
1116- const tabs = tabButtons ;
1117- const panels = tabPanels ;
1118-
1119- console . info (
1120- '[root-tabs] found tabs:' ,
1121- tabs . map ( ( tab ) => tab . dataset . rootTab || tab . dataset . tab || '' )
1122- ) ;
1123- console . info (
1124- '[root-tabs] found panels:' ,
1125- panels . map ( ( panel ) => panel . dataset . rootPanel || panel . dataset . tab || '' )
1126- ) ;
1127-
11281125 document . body . dataset . rootTabsHydrated = '1' ;
11291126
1130- if ( tabs . length === 0 || panels . length === 0 ) {
1127+ const bar = document . querySelector ( '[data-root-tabs]' ) ;
1128+ if ( ! bar ) {
1129+ console . warn ( '[root-tabs] no [data-root-tabs] container found' ) ;
11311130 return ;
11321131 }
11331132
1134- tabs . forEach ( ( tab ) => {
1135- tab . addEventListener ( 'click' , ( event ) => {
1136- console . info ( '[root-tabs] click on' , tab . dataset . rootTab || tab . dataset . tab ) ;
1137- event . preventDefault ( ) ;
1138- const targetId = tab . dataset . rootTab || tab . dataset . tab ;
1139- if ( targetId ) {
1140- goToRootTab ( targetId , { focusPanel : true } ) ;
1141- }
1142- } ) ;
1133+ console . info ( '[root-tabs] hydrating' ) ;
1134+
1135+ collectRootTabElements ( ) ;
1136+
1137+ bar . addEventListener ( 'click' , ( event ) => {
1138+ const btn = event . target . closest ( '[data-root-tab]' ) ;
1139+ if ( ! btn ) {
1140+ return ;
1141+ }
1142+ event . preventDefault ( ) ;
1143+ const id = btn . dataset . rootTab ;
1144+ if ( ! id ) {
1145+ return ;
1146+ }
1147+ console . info ( '[root-tabs] click on' , id ) ;
1148+ goToRootTab ( id ) ;
1149+ focusTabPanel ( id ) ;
11431150 } ) ;
11441151
1145- const defaultTabButton =
1146- tabs . find ( ( button ) => button . classList . contains ( 'root-tab--active' ) ) ||
1147- tabs . find ( ( button ) => button . classList . contains ( 'active' ) ) ||
1148- tabs [ 0 ] ;
1152+ tabButtons . forEach ( ( button ) => {
1153+ button . addEventListener ( 'keydown' , handleRootTabKeydown ) ;
1154+ } ) ;
1155+
1156+ if ( consoleTabButton ) {
1157+ consoleTabButton . style . display = 'inline-flex' ;
1158+ consoleTabButton . style . alignItems = 'center' ;
1159+ consoleTabButton . style . justifyContent = 'center' ;
11491160
1150- const defaultTarget =
1151- defaultTabButton ?. dataset . rootTab ||
1152- defaultTabButton ?. dataset . tab ||
1153- tabOrder [ 0 ] ;
1161+ consoleIndicator = document . createElement ( 'span' ) ;
1162+ consoleIndicator . textContent = '•' ;
1163+ consoleIndicator . setAttribute ( 'aria-hidden' , 'true' ) ;
1164+ consoleIndicator . style . marginLeft = '4px' ;
1165+ consoleIndicator . style . fontSize = '10px' ;
1166+ consoleIndicator . style . color = '#22d3ee' ;
1167+ consoleIndicator . style . opacity = '0' ;
1168+ consoleIndicator . style . transition = 'opacity 0.2s ease' ;
1169+ consoleIndicator . style . pointerEvents = 'none' ;
1170+ consoleTabButton . append ( consoleIndicator ) ;
1171+ }
11541172
1155- if ( defaultTarget ) {
1156- goToRootTab ( defaultTarget , { focusPanel : false } ) ;
1157- } else if ( tabOrder . length > 0 ) {
1158- goToRootTab ( tabOrder [ 0 ] , { focusPanel : false } ) ;
1173+ const firstActive = document . querySelector ( '[data-root-tab].root-tab--active' ) ;
1174+ const first = firstActive || document . querySelector ( '[data-root-tab]' ) ;
1175+ if ( first && first . dataset . rootTab ) {
1176+ goToRootTab ( first . dataset . rootTab ) ;
11591177 }
11601178}
11611179
@@ -1164,28 +1182,16 @@ registerIntentHandler(INTENT_TYPES.NAVIGATE_TAB, (payload = {}) => {
11641182 if ( ! target ) {
11651183 return ;
11661184 }
1167- const shouldFocus = payload . focusPanel === true ;
1168- goToRootTab ( target , { focusPanel : shouldFocus } ) ;
1185+ goToRootTab ( target ) ;
1186+ if ( payload . focusPanel === true ) {
1187+ focusTabPanel ( target ) ;
1188+ }
11691189} ) ;
11701190
1171- initRootTabs ( ) ;
1172-
1173- if ( consoleTabButton ) {
1174- consoleTabButton . style . display = 'inline-flex' ;
1175- consoleTabButton . style . alignItems = 'center' ;
1176- consoleTabButton . style . justifyContent = 'center' ;
1177-
1178- consoleIndicator = document . createElement ( 'span' ) ;
1179- consoleIndicator . textContent = '•' ;
1180- consoleIndicator . setAttribute ( 'aria-hidden' , 'true' ) ;
1181- consoleIndicator . style . marginLeft = '4px' ;
1182- consoleIndicator . style . fontSize = '10px' ;
1183- consoleIndicator . style . color = '#22d3ee' ;
1184- consoleIndicator . style . opacity = '0' ;
1185- consoleIndicator . style . transition = 'opacity 0.2s ease' ;
1186- consoleIndicator . style . pointerEvents = 'none' ;
1187- consoleTabButton . append ( consoleIndicator ) ;
1188- }
1191+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
1192+ console . info ( '[app] DOMContentLoaded' ) ;
1193+ initRootTabs ( ) ;
1194+ } ) ;
11891195
11901196function handleRootTabKeydown ( event ) {
11911197 const { key } = event ;
@@ -1241,10 +1247,6 @@ function handleRootTabKeydown(event) {
12411247 }
12421248}
12431249
1244- tabButtons . forEach ( ( button ) => {
1245- button . addEventListener ( 'keydown' , handleRootTabKeydown ) ;
1246- } ) ;
1247-
12481250function tabFromShortcutKey ( key ) {
12491251 const numericKey = Number . parseInt ( key , 10 ) ;
12501252 if ( Number . isNaN ( numericKey ) ) {
0 commit comments