@@ -316,6 +316,12 @@ Extras.Sync.TabPane = Core.extend(Echo.Render.ComponentSync, {
316316 * @type Boolean
317317 */
318318 _rtl : false ,
319+
320+ /**
321+ * Focus proxy element which keeps track of the browser focus for the tab pane.
322+ */
323+ _focusAnchor : null ,
324+ _focusAnchorDiv : null ,
319325
320326 /**
321327 * Constructor.
@@ -733,6 +739,16 @@ Extras.Sync.TabPane = Core.extend(Echo.Render.ComponentSync, {
733739
734740 // Render Header Container.
735741 this . _headerContainerDiv = document . createElement ( "div" ) ;
742+
743+ if ( this . _focusAnchor == null ) {
744+ this . _focusAnchorDiv = document . createElement ( "div" ) ;
745+ this . _focusAnchorDiv . style . cssText = "width:0;height:0;overflow:hidden;" ;
746+ this . _focusAnchor = document . createElement ( "input" ) ;
747+ this . _focusAnchorDiv . appendChild ( this . _focusAnchor ) ;
748+ this . _headerContainerDiv . appendChild ( this . _focusAnchorDiv ) ;
749+ this . _addEventHandlers ( ) ;
750+ }
751+
736752 this . _headerContainerDiv . style . cssText = "position:absolute;left:0;right:0;top:0;bottom:0;" ;
737753
738754 Echo . Sync . Font . render ( this . component . render ( "font" ) , this . _headerContainerDiv ) ;
@@ -846,6 +862,8 @@ Extras.Sync.TabPane = Core.extend(Echo.Render.ComponentSync, {
846862 this . _borderDiv = null ;
847863 this . _headerContainerBoundsDiv = null ;
848864 this . _headerContainerDiv = null ;
865+ this . _focusAnchor = null ;
866+ this . _focusAnchorDiv = null ;
849867 this . _contentContainerDiv = null ;
850868 if ( this . _previousControlDiv ) {
851869 Core . Web . Event . removeAll ( this . _previousControlDiv ) ;
@@ -1099,6 +1117,80 @@ Extras.Sync.TabPane = Core.extend(Echo.Render.ComponentSync, {
10991117 }
11001118 }
11011119 this . _activeTabId = this . component . children . length === 0 ? null : this . component . children [ 0 ] . renderId ;
1120+ } ,
1121+
1122+ /**
1123+ * Keydown event handler to change tabs with the keyboard's arrow keys
1124+ *
1125+ * @param e the event
1126+ */
1127+ _processKeyDown : function ( e ) {
1128+ var activeTabIx = - 1 ;
1129+ for ( var i = 0 ; i < this . _tabs . length ; i ++ ) {
1130+ if ( this . _tabs [ i ] . id == this . _activeTabId ) {
1131+ activeTabIx = i ;
1132+ }
1133+ }
1134+ if ( e . keyCode == 37 ) {
1135+ // left
1136+ if ( activeTabIx != - 1 ) {
1137+ if ( activeTabIx === 0 ) {
1138+ this . component . doTabSelect ( this . _tabs [ this . _tabs . length - 1 ] . id ) ;
1139+ } else {
1140+ this . component . doTabSelect ( this . _tabs [ activeTabIx - 1 ] . id ) ;
1141+ }
1142+ }
1143+ } else if ( e . keyCode == 39 ) {
1144+ // right
1145+ if ( activeTabIx != - 1 ) {
1146+ this . component . doTabSelect ( this . _tabs [ ( activeTabIx + 1 ) % this . _tabs . length ] . id ) ;
1147+ }
1148+ }
1149+ return true ;
1150+ } ,
1151+
1152+ /**
1153+ * Registers event handlers on the text component.
1154+ */
1155+ _addEventHandlers : function ( ) {
1156+ Core . Web . Event . add ( this . _focusAnchor , "keydown" , Core . method ( this , this . _processKeyDown ) , false ) ;
1157+ Core . Web . Event . add ( this . _focusAnchor , "focus" , Core . method ( this , this . processFocus ) , false ) ;
1158+ Core . Web . Event . add ( this . _focusAnchor , "blur" , Core . method ( this , this . processBlur ) , false ) ;
1159+ } ,
1160+
1161+ /**
1162+ * Processes a focus blur event.
1163+ * Overriding implementations must invoke.
1164+ */
1165+ processBlur : function ( e ) {
1166+ this . _focused = false ;
1167+ this . _headerUpdateRequired = true ;
1168+ this . renderDisplay ( ) ;
1169+ return true ;
1170+ } ,
1171+
1172+ /**
1173+ * Processes a focus event. Notifies application of focus.
1174+ * Overriding implementations must invoke.
1175+ */
1176+ processFocus : function ( e ) {
1177+ this . _focused = true ;
1178+ this . _headerUpdateRequired = true ;
1179+ this . renderDisplay ( ) ;
1180+ return false ;
1181+ } ,
1182+
1183+ /**
1184+ * Focuses the tab pane
1185+ */
1186+ renderFocus : function ( ) {
1187+ if ( this . _focused ) {
1188+ return ;
1189+ }
1190+ this . _focused = true ;
1191+ this . _headerUpdateRequired = true ;
1192+ this . renderDisplay ( ) ;
1193+ Core . Web . DOM . focusElement ( this . _focusAnchor ) ;
11021194 }
11031195} ) ;
11041196
@@ -1250,14 +1342,19 @@ Extras.Sync.TabPane.Tab = Core.extend({
12501342 * @param {String } name the name of the property, first letter capitalized, e.g., "Background"
12511343 * @param {Boolean } active the active state
12521344 * @param {Boolean } rollover the rollover state
1345+ * @param {Boolean } focus the focus state of the parent tab pane
12531346 * @return the property value
12541347 */
1255- _getProperty : function ( name , active , rollover ) {
1348+ _getProperty : function ( name , active , rollover , focus ) {
12561349 var value = this . _layoutData [ ( active ? "active" : "inactive" ) + name ] ||
12571350 this . _parent . component . render ( ( active ? "tabActive" : "tabInactive" ) + name ) ;
12581351 if ( ! active && rollover ) {
12591352 value = this . _layoutData [ "rollover" + name ] || this . _parent . component . render ( "tabRollover" + name ) || value ;
12601353 }
1354+ if ( active && focus ) {
1355+ // Only use the focus style for the active tab
1356+ value = this . _layoutData [ "focused" + name ] || this . _parent . component . render ( "tabFocused" + name ) || value ;
1357+ }
12611358 return value ;
12621359 } ,
12631360
@@ -1271,16 +1368,18 @@ Extras.Sync.TabPane.Tab = Core.extend({
12711368 */
12721369 _getSurroundHeight : function ( active ) {
12731370 var insets , imageBorder , border , padding ;
1371+
1372+ var focus = this . _parent . _focused ;
12741373
1275- insets = Echo . Sync . Insets . toPixels ( this . _getProperty ( "Insets" , active , false ) || Extras . Sync . TabPane . _DEFAULTS . tabInsets ) ;
1374+ insets = Echo . Sync . Insets . toPixels ( this . _getProperty ( "Insets" , active , false , focus ) || Extras . Sync . TabPane . _DEFAULTS . tabInsets ) ;
12761375 padding = insets . top + insets . bottom ;
12771376
12781377 if ( this . _useImageBorder ) {
1279- imageBorder = this . _getProperty ( "ImageBorder" , active , false ) ;
1378+ imageBorder = this . _getProperty ( "ImageBorder" , active , false , focus ) ;
12801379 insets = Echo . Sync . Insets . toPixels ( imageBorder . contentInsets ) ;
12811380 return padding + insets . top + insets . bottom ;
12821381 } else {
1283- border = this . _getProperty ( "Border" , active , false ) ||
1382+ border = this . _getProperty ( "Border" , active , false , focus ) ||
12841383 ( active ? this . _parent . _tabActiveBorder : this . _parent . _tabInactiveBorder ) ;
12851384 return padding + Echo . Sync . Border . getPixelSize ( border , this . _parent . _tabSide ) ;
12861385 }
@@ -1291,7 +1390,7 @@ Extras.Sync.TabPane.Tab = Core.extend({
12911390 */
12921391 _loadProperties : function ( ) {
12931392 this . _layoutData = this . _childComponent . render ( "layoutData" ) || { } ;
1294- this . _useImageBorder = this . _getProperty ( "ImageBorder" , false , false ) ;
1393+ this . _useImageBorder = this . _getProperty ( "ImageBorder" , false , false , false ) ;
12951394 this . _tabCloseEnabled = this . _parent . _tabCloseEnabled && this . _layoutData . closeEnabled ;
12961395 this . _activeSurroundHeight = this . _getSurroundHeight ( true ) ;
12971396 this . _inactiveSurroundHeight = this . _getSurroundHeight ( false ) ;
@@ -1314,7 +1413,7 @@ Extras.Sync.TabPane.Tab = Core.extend({
13141413 this . _parent . component . doTabClose ( this . id ) ;
13151414 } else {
13161415 // tab clicked
1317- this . _parent . component . doTabSelect ( this . id ) ;
1416+ this . _parent . component . doTabSelect ( this . id , this . _parent . _activeTabId == this . id ) ;
13181417 }
13191418 } ,
13201419
@@ -1417,6 +1516,8 @@ Extras.Sync.TabPane.Tab = Core.extend({
14171516 _renderHeader : function ( active ) {
14181517 var tabPane = this . _parent . component ,
14191518 img , table , tr , td ;
1519+
1520+ var focus = this . _parent . _focused ;
14201521
14211522 Core . Web . Event . removeAll ( this . _headerDiv ) ;
14221523 Core . Web . DOM . removeAllChildren ( this . _headerDiv ) ;
@@ -1435,8 +1536,8 @@ Extras.Sync.TabPane.Tab = Core.extend({
14351536 var headerDivContent = this . _labelDiv ;
14361537
14371538 if ( this . _useImageBorder ) {
1438- var imageBorder = this . _getProperty ( "ImageBorder" , active , false ) ;
1439- var backgroundInsets = this . _getProperty ( "BackgroundInsets" , active , false ) ;
1539+ var imageBorder = this . _getProperty ( "ImageBorder" , active , false , focus ) ;
1540+ var backgroundInsets = this . _getProperty ( "BackgroundInsets" , active , false , focus ) ;
14401541 this . _fibContainer = headerDivContent =
14411542 Echo . Sync . FillImageBorder . renderContainer ( imageBorder , { child : this . _labelDiv } ) ;
14421543 var fibContent = Echo . Sync . FillImageBorder . getContainerContent ( this . _fibContainer ) ;
@@ -1454,7 +1555,7 @@ Extras.Sync.TabPane.Tab = Core.extend({
14541555 headerDivContent . firstChild . firstChild . appendChild ( td ) ;
14551556 }
14561557 } else {
1457- var border = this . _getProperty ( "Border" , active , false ) ||
1558+ var border = this . _getProperty ( "Border" , active , false , focus ) ||
14581559 ( active ? this . _parent . _tabActiveBorder : this . _parent . _tabInactiveBorder ) ;
14591560 this . _backgroundDiv = null ;
14601561 this . _fibContainer = null ;
@@ -1544,11 +1645,13 @@ Extras.Sync.TabPane.Tab = Core.extend({
15441645 */
15451646 _renderHeaderState : function ( active , rollover , force ) {
15461647 var fullRender = ! this . _labelDiv || force ;
1547-
1648+
15481649 if ( fullRender ) {
15491650 this . _renderHeader ( active ) ;
15501651 }
1551-
1652+
1653+ var focus = this . _parent . _focused ;
1654+
15521655 if ( ! force && this . _active == active && ( active || ! this . _parent . _tabRolloverEnabled || this . _rolloverState == rollover ) ) {
15531656 return ;
15541657 }
@@ -1565,10 +1668,10 @@ Extras.Sync.TabPane.Tab = Core.extend({
15651668 var tabPane = this . _parent . component ,
15661669 img , table , tr , td ;
15671670
1568- Echo . Sync . Color . renderClear ( this . _getProperty ( "Foreground" , active , rollover ) , this . _labelDiv , "color" ) ;
1569- Echo . Sync . Font . renderClear ( this . _getProperty ( "Font" , active , rollover ) , this . _labelDiv ) ;
1671+ Echo . Sync . Color . renderClear ( this . _getProperty ( "Foreground" , active , rollover , focus ) , this . _labelDiv , "color" ) ;
1672+ Echo . Sync . Font . renderClear ( this . _getProperty ( "Font" , active , rollover , focus ) , this . _labelDiv ) ;
15701673 this . _labelDiv . style . cursor = active ? "default" : "pointer" ;
1571- Echo . Sync . Insets . render ( this . _getProperty ( "Insets" , active , false ) || Extras . Sync . TabPane . _DEFAULTS . tabInsets ,
1674+ Echo . Sync . Insets . render ( this . _getProperty ( "Insets" , active , false , focus ) || Extras . Sync . TabPane . _DEFAULTS . tabInsets ,
15721675 this . _labelDiv , "padding" ) ;
15731676
15741677 this . _headerDiv . style [ this . _parent . _tabPositionBottom ? "top" : "bottom" ] =
@@ -1579,27 +1682,27 @@ Extras.Sync.TabPane.Tab = Core.extend({
15791682 ( parseInt ( this . _labelDiv . style [ this . _parent . _tabPositionBottom ? "paddingTop" : "paddingBottom" ] , 10 ) +
15801683 ( this . _parent . _tabActiveHeightIncreasePx + this . _parent . _tabInactivePositionAdjustPx ) ) + "px" ;
15811684 }
1582-
1685+
15831686 if ( ! fullRender ) {
15841687 if ( this . _useImageBorder ) {
15851688 // Render FillImageBorder style.
1586- var imageBorder = this . _getProperty ( "ImageBorder" , active , rollover ) ;
1587- var backgroundInsets = this . _getProperty ( "BackgroundInsets" , active , rollover ) ;
1689+ var imageBorder = this . _getProperty ( "ImageBorder" , active , rollover , focus ) ;
1690+ var backgroundInsets = this . _getProperty ( "BackgroundInsets" , active , rollover , focus ) ;
15881691 Echo . Sync . FillImageBorder . renderContainer ( imageBorder , { update : this . _fibContainer } ) ;
15891692 Echo . Sync . Insets . renderPosition ( backgroundInsets || imageBorder . borderInsets , this . _backgroundDiv ) ;
15901693 } else {
15911694 // Render CSS border style.
1592- var border = this . _getProperty ( "Border" , active , rollover ) ||
1695+ var border = this . _getProperty ( "Border" , active , rollover , focus ) ||
15931696 ( active ? this . _parent . _tabActiveBorder : this . _parent . _tabInactiveBorder ) ;
15941697 Echo . Sync . Border . render ( border , this . _labelDiv , this . _parent . _tabPositionBottom ? "borderBottom" : "borderTop" ) ;
15951698 Echo . Sync . Border . render ( border , this . _labelDiv , "borderLeft" ) ;
15961699 Echo . Sync . Border . render ( border , this . _labelDiv , "borderRight" ) ;
15971700 }
15981701 }
15991702
1600- Echo . Sync . Color . renderClear ( this . _getProperty ( "Background" , active , rollover ) ,
1703+ Echo . Sync . Color . renderClear ( this . _getProperty ( "Background" , active , rollover , focus ) ,
16011704 this . _backgroundDiv || this . _labelDiv , "backgroundColor" ) ;
1602- Echo . Sync . FillImage . renderClear ( this . _getProperty ( "BackgroundImage" , active , rollover ) ,
1705+ Echo . Sync . FillImage . renderClear ( this . _getProperty ( "BackgroundImage" , active , rollover , focus ) ,
16031706 this . _backgroundDiv || this . _labelDiv , null ) ;
16041707
16051708 // Update icon.
0 commit comments