@@ -316,6 +316,12 @@ Extras.Sync.TabPane = Core.extend(Echo.Render.ComponentSync, {
316
316
* @type Boolean
317
317
*/
318
318
_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 ,
319
325
320
326
/**
321
327
* Constructor.
@@ -733,6 +739,16 @@ Extras.Sync.TabPane = Core.extend(Echo.Render.ComponentSync, {
733
739
734
740
// Render Header Container.
735
741
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
+
736
752
this . _headerContainerDiv . style . cssText = "position:absolute;left:0;right:0;top:0;bottom:0;" ;
737
753
738
754
Echo . Sync . Font . render ( this . component . render ( "font" ) , this . _headerContainerDiv ) ;
@@ -846,6 +862,8 @@ Extras.Sync.TabPane = Core.extend(Echo.Render.ComponentSync, {
846
862
this . _borderDiv = null ;
847
863
this . _headerContainerBoundsDiv = null ;
848
864
this . _headerContainerDiv = null ;
865
+ this . _focusAnchor = null ;
866
+ this . _focusAnchorDiv = null ;
849
867
this . _contentContainerDiv = null ;
850
868
if ( this . _previousControlDiv ) {
851
869
Core . Web . Event . removeAll ( this . _previousControlDiv ) ;
@@ -1099,6 +1117,80 @@ Extras.Sync.TabPane = Core.extend(Echo.Render.ComponentSync, {
1099
1117
}
1100
1118
}
1101
1119
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 ) ;
1102
1194
}
1103
1195
} ) ;
1104
1196
@@ -1250,14 +1342,19 @@ Extras.Sync.TabPane.Tab = Core.extend({
1250
1342
* @param {String } name the name of the property, first letter capitalized, e.g., "Background"
1251
1343
* @param {Boolean } active the active state
1252
1344
* @param {Boolean } rollover the rollover state
1345
+ * @param {Boolean } focus the focus state of the parent tab pane
1253
1346
* @return the property value
1254
1347
*/
1255
- _getProperty : function ( name , active , rollover ) {
1348
+ _getProperty : function ( name , active , rollover , focus ) {
1256
1349
var value = this . _layoutData [ ( active ? "active" : "inactive" ) + name ] ||
1257
1350
this . _parent . component . render ( ( active ? "tabActive" : "tabInactive" ) + name ) ;
1258
1351
if ( ! active && rollover ) {
1259
1352
value = this . _layoutData [ "rollover" + name ] || this . _parent . component . render ( "tabRollover" + name ) || value ;
1260
1353
}
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
+ }
1261
1358
return value ;
1262
1359
} ,
1263
1360
@@ -1271,16 +1368,18 @@ Extras.Sync.TabPane.Tab = Core.extend({
1271
1368
*/
1272
1369
_getSurroundHeight : function ( active ) {
1273
1370
var insets , imageBorder , border , padding ;
1371
+
1372
+ var focus = this . _parent . _focused ;
1274
1373
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 ) ;
1276
1375
padding = insets . top + insets . bottom ;
1277
1376
1278
1377
if ( this . _useImageBorder ) {
1279
- imageBorder = this . _getProperty ( "ImageBorder" , active , false ) ;
1378
+ imageBorder = this . _getProperty ( "ImageBorder" , active , false , focus ) ;
1280
1379
insets = Echo . Sync . Insets . toPixels ( imageBorder . contentInsets ) ;
1281
1380
return padding + insets . top + insets . bottom ;
1282
1381
} else {
1283
- border = this . _getProperty ( "Border" , active , false ) ||
1382
+ border = this . _getProperty ( "Border" , active , false , focus ) ||
1284
1383
( active ? this . _parent . _tabActiveBorder : this . _parent . _tabInactiveBorder ) ;
1285
1384
return padding + Echo . Sync . Border . getPixelSize ( border , this . _parent . _tabSide ) ;
1286
1385
}
@@ -1291,7 +1390,7 @@ Extras.Sync.TabPane.Tab = Core.extend({
1291
1390
*/
1292
1391
_loadProperties : function ( ) {
1293
1392
this . _layoutData = this . _childComponent . render ( "layoutData" ) || { } ;
1294
- this . _useImageBorder = this . _getProperty ( "ImageBorder" , false , false ) ;
1393
+ this . _useImageBorder = this . _getProperty ( "ImageBorder" , false , false , false ) ;
1295
1394
this . _tabCloseEnabled = this . _parent . _tabCloseEnabled && this . _layoutData . closeEnabled ;
1296
1395
this . _activeSurroundHeight = this . _getSurroundHeight ( true ) ;
1297
1396
this . _inactiveSurroundHeight = this . _getSurroundHeight ( false ) ;
@@ -1314,7 +1413,7 @@ Extras.Sync.TabPane.Tab = Core.extend({
1314
1413
this . _parent . component . doTabClose ( this . id ) ;
1315
1414
} else {
1316
1415
// tab clicked
1317
- this . _parent . component . doTabSelect ( this . id ) ;
1416
+ this . _parent . component . doTabSelect ( this . id , this . _parent . _activeTabId == this . id ) ;
1318
1417
}
1319
1418
} ,
1320
1419
@@ -1417,6 +1516,8 @@ Extras.Sync.TabPane.Tab = Core.extend({
1417
1516
_renderHeader : function ( active ) {
1418
1517
var tabPane = this . _parent . component ,
1419
1518
img , table , tr , td ;
1519
+
1520
+ var focus = this . _parent . _focused ;
1420
1521
1421
1522
Core . Web . Event . removeAll ( this . _headerDiv ) ;
1422
1523
Core . Web . DOM . removeAllChildren ( this . _headerDiv ) ;
@@ -1435,8 +1536,8 @@ Extras.Sync.TabPane.Tab = Core.extend({
1435
1536
var headerDivContent = this . _labelDiv ;
1436
1537
1437
1538
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 ) ;
1440
1541
this . _fibContainer = headerDivContent =
1441
1542
Echo . Sync . FillImageBorder . renderContainer ( imageBorder , { child : this . _labelDiv } ) ;
1442
1543
var fibContent = Echo . Sync . FillImageBorder . getContainerContent ( this . _fibContainer ) ;
@@ -1454,7 +1555,7 @@ Extras.Sync.TabPane.Tab = Core.extend({
1454
1555
headerDivContent . firstChild . firstChild . appendChild ( td ) ;
1455
1556
}
1456
1557
} else {
1457
- var border = this . _getProperty ( "Border" , active , false ) ||
1558
+ var border = this . _getProperty ( "Border" , active , false , focus ) ||
1458
1559
( active ? this . _parent . _tabActiveBorder : this . _parent . _tabInactiveBorder ) ;
1459
1560
this . _backgroundDiv = null ;
1460
1561
this . _fibContainer = null ;
@@ -1544,11 +1645,13 @@ Extras.Sync.TabPane.Tab = Core.extend({
1544
1645
*/
1545
1646
_renderHeaderState : function ( active , rollover , force ) {
1546
1647
var fullRender = ! this . _labelDiv || force ;
1547
-
1648
+
1548
1649
if ( fullRender ) {
1549
1650
this . _renderHeader ( active ) ;
1550
1651
}
1551
-
1652
+
1653
+ var focus = this . _parent . _focused ;
1654
+
1552
1655
if ( ! force && this . _active == active && ( active || ! this . _parent . _tabRolloverEnabled || this . _rolloverState == rollover ) ) {
1553
1656
return ;
1554
1657
}
@@ -1565,10 +1668,10 @@ Extras.Sync.TabPane.Tab = Core.extend({
1565
1668
var tabPane = this . _parent . component ,
1566
1669
img , table , tr , td ;
1567
1670
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 ) ;
1570
1673
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 ,
1572
1675
this . _labelDiv , "padding" ) ;
1573
1676
1574
1677
this . _headerDiv . style [ this . _parent . _tabPositionBottom ? "top" : "bottom" ] =
@@ -1579,27 +1682,27 @@ Extras.Sync.TabPane.Tab = Core.extend({
1579
1682
( parseInt ( this . _labelDiv . style [ this . _parent . _tabPositionBottom ? "paddingTop" : "paddingBottom" ] , 10 ) +
1580
1683
( this . _parent . _tabActiveHeightIncreasePx + this . _parent . _tabInactivePositionAdjustPx ) ) + "px" ;
1581
1684
}
1582
-
1685
+
1583
1686
if ( ! fullRender ) {
1584
1687
if ( this . _useImageBorder ) {
1585
1688
// 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 ) ;
1588
1691
Echo . Sync . FillImageBorder . renderContainer ( imageBorder , { update : this . _fibContainer } ) ;
1589
1692
Echo . Sync . Insets . renderPosition ( backgroundInsets || imageBorder . borderInsets , this . _backgroundDiv ) ;
1590
1693
} else {
1591
1694
// Render CSS border style.
1592
- var border = this . _getProperty ( "Border" , active , rollover ) ||
1695
+ var border = this . _getProperty ( "Border" , active , rollover , focus ) ||
1593
1696
( active ? this . _parent . _tabActiveBorder : this . _parent . _tabInactiveBorder ) ;
1594
1697
Echo . Sync . Border . render ( border , this . _labelDiv , this . _parent . _tabPositionBottom ? "borderBottom" : "borderTop" ) ;
1595
1698
Echo . Sync . Border . render ( border , this . _labelDiv , "borderLeft" ) ;
1596
1699
Echo . Sync . Border . render ( border , this . _labelDiv , "borderRight" ) ;
1597
1700
}
1598
1701
}
1599
1702
1600
- Echo . Sync . Color . renderClear ( this . _getProperty ( "Background" , active , rollover ) ,
1703
+ Echo . Sync . Color . renderClear ( this . _getProperty ( "Background" , active , rollover , focus ) ,
1601
1704
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 ) ,
1603
1706
this . _backgroundDiv || this . _labelDiv , null ) ;
1604
1707
1605
1708
// Update icon.
0 commit comments