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