@@ -1450,14 +1450,25 @@ def set_sticky(
1450
1450
Whether to make the index or column headers sticky.
1451
1451
pixel_size : int, optional
1452
1452
Required to configure the width of index cells or the height of column
1453
- header cells when sticking a MultiIndex. Defaults to 75 and 25 respectively.
1453
+ header cells when sticking a MultiIndex (or with a named Index).
1454
+ Defaults to 75 and 25 respectively.
1454
1455
levels : list of int
1455
1456
If ``axis`` is a MultiIndex the specific levels to stick. If ``None`` will
1456
1457
stick all levels.
1457
1458
1458
1459
Returns
1459
1460
-------
1460
1461
self : Styler
1462
+
1463
+ Notes
1464
+ -----
1465
+ This method uses the CSS 'position: sticky;' property to display. It is
1466
+ designed to work with visible axes, therefore both:
1467
+
1468
+ - `styler.set_sticky(axis="index").hide_index()`
1469
+ - `styler.set_sticky(axis="columns").hide_columns()`
1470
+
1471
+ may produce strange behaviour due to CSS controls with missing elements.
1461
1472
"""
1462
1473
if axis in [0 , "index" ]:
1463
1474
axis , obj , tag , pos = 0 , self .data .index , "tbody" , "left"
@@ -1469,15 +1480,42 @@ def set_sticky(
1469
1480
raise ValueError ("`axis` must be one of {0, 1, 'index', 'columns'}" )
1470
1481
1471
1482
if not isinstance (obj , pd .MultiIndex ):
1472
- return self .set_table_styles (
1473
- [
1483
+ # handling MultiIndexes requires different CSS
1484
+ props = "position:sticky; background-color:white;"
1485
+
1486
+ if axis == 1 :
1487
+ # stick the first <tr> of <head> and, if index names, the second <tr>
1488
+ # if self._hide_columns then no <thead><tr> here will exist: no conflict
1489
+ styles : CSSStyles = [
1474
1490
{
1475
- "selector" : f" { tag } th " ,
1476
- "props" : f"position:sticky; { pos } : 0px; background-color:white ;" ,
1491
+ "selector" : "thead tr:first-child " ,
1492
+ "props" : props + "top: 0px; z-index:2 ;" ,
1477
1493
}
1478
- ],
1479
- overwrite = False ,
1480
- )
1494
+ ]
1495
+ if not self .index .names [0 ] is None :
1496
+ styles [0 ]["props" ] = (
1497
+ props + f"top:0px; z-index:2; height:{ pixel_size } px;"
1498
+ )
1499
+ styles .append (
1500
+ {
1501
+ "selector" : "thead tr:nth-child(2)" ,
1502
+ "props" : props
1503
+ + f"top:{ pixel_size } px; z-index:2; height:{ pixel_size } px; " ,
1504
+ }
1505
+ )
1506
+ else :
1507
+ # stick the first <th> of each <tr> in both <thead> and <tbody>
1508
+ # if self._hide_index then no <th> will exist in <tbody>: no conflict
1509
+ # but <th> will exist in <thead>: conflict with initial element
1510
+ styles = [
1511
+ {
1512
+ "selector" : "tr th:first-child" ,
1513
+ "props" : props + "left:0px; z-index:1;" ,
1514
+ }
1515
+ ]
1516
+
1517
+ return self .set_table_styles (styles , overwrite = False )
1518
+
1481
1519
else :
1482
1520
range_idx = list (range (obj .nlevels ))
1483
1521
0 commit comments