Changeset 7935


Ignore:
Timestamp:
Apr 30, 2021, 9:51:20 AM (2 years ago)
Author:
Nicklas Nordborg
Message:

References #2246: Sticky table headers

Fixed an issue with columns that had subtitles.

Implemented a callback method using the IntersectionObserver API to detect when a column becomes "stuck" to either the left or right. The callback will toggle "stuck-left" and "stuck-right" classes on the main table element. In the CSS this is used to set a solid border to either the left or right on the stuck column.

Tested on samples, extracts and raw bioassays list pages.

Location:
branches/3.18-stable
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • branches/3.18-stable/src/clients/web/net/sf/basedb/clients/web/taglib/table/ColumnSubtitles.java

    r7905 r7935  
    8888        sb.append(">\n");
    8989        int colspan = table.numHeaderColumns()-table.numVisibleColumns();
     90        if (table.hasStickyHeaders() && colspan > 0)
     91        {
     92          sb.append("<th class=\"row-index bg-filled-100\"></th>");
     93          colspan--;
     94        }
    9095        if (colspan > 0)
    9196        {
  • branches/3.18-stable/src/clients/web/net/sf/basedb/clients/web/taglib/table/Table.java

    r7933 r7935  
    602602    for (String column : visibleColumns)
    603603    {
    604       sb.append("<th class=\"subtitle\">");
     604      sb.append("<th class=\"subtitle"+(isColumnSticky(column)?" sticky-col bg-filled-100":"")+"\">");
    605605      String content = columnSubtitle.get(column);
    606606      if (content != null)
  • branches/3.18-stable/www/biomaterials/extracts/list_extracts.jsp

    r7930 r7935  
    243243      item="<%=itemType%>"
    244244      filterrows="<%=cc.getFilterRows()%>"
    245       subclass="fulltable sticky-headers"
     245      subclass="fulltable"
    246246      data-inherited-annotations="true"
    247247      data-relateditem-columns="true"
     248      stickyheaders="name"
    248249      >
    249250      <tbl:hidden
     
    697698        <tbl:headers>
    698699          <tbl:headerrow>
    699             <tbl:header colspan="3" />
     700            <tbl:header clazz="row-index bg-filled-100" />
    700701            <tbl:columnheaders />
    701702          </tbl:headerrow>
     
    708709            %>
    709710            <tbl:headerrow>
    710               <tbl:header subclass="index" />
    711               <tbl:header
    712                 subclass="check"
    713                 visible="<%=mode.hasCheck()%>"
    714                 ><base:icon
    715                   subclass="link table-check"
    716                   image="check_uncheck.png"
    717                   tooltip="Toggle all (use CTRL, ALT or SHIFT to check/uncheck)"
    718                   visible="<%=lastRow%>"
    719                 /></tbl:header>
    720               <tbl:header
    721                 subclass="check"
    722                 visible="<%=mode.hasRadio()%>"
    723                 />
    724               <tbl:header
    725                 subclass="icons"
    726                 visible="<%=mode.hasIcons()%>"
    727                 >
    728                 <base:icon
    729                   subclass="link table-filter-row-action"
    730                   image="add.png"
    731                   tooltip="Add extra filter row"
    732                   visible="<%=lastRow%>"
    733                 /><base:icon
    734                   subclass="link table-filter-row-action"
    735                   image="remove.png"
    736                   tooltip="Remove this filter row"
    737                   visible="<%=numRows > 1 || numFilters > 0 %>"
    738                   data-remove-row="<%=filterNo%>"
    739                 />
     711              <tbl:header subclass="row-index bg-filled-100">
     712                <div class="index-<%=mode.getName()%>">
     713                  <div class="index"></div>
     714                  <div class="check">
     715                    <base:icon
     716                      subclass="link table-check"
     717                      image="check_uncheck.png"
     718                      tooltip="Toggle all (use CTRL, ALT or SHIFT to check/uncheck)"
     719                      visible="<%=lastRow && mode.hasCheck()%>"
     720                    />
     721                  </div>
     722                  <div class="icons">
     723                    <base:icon
     724                      subclass="link table-filter-row-action"
     725                      image="add.png"
     726                      tooltip="Add extra filter row"
     727                      visible="<%=lastRow%>"
     728                    /><base:icon
     729                      subclass="link table-filter-row-action"
     730                      image="remove.png"
     731                      tooltip="Remove this filter row"
     732                      visible="<%=numRows > 1 || numFilters > 0 %>"
     733                      data-remove-row="<%=filterNo%>"
     734                    />
     735                  </div>
     736                </div>
    740737              </tbl:header>
    741738              <tbl:propertyfilter row="<%=filterNo%>" />
     
    780777              %>
    781778              <tbl:row>
    782                 <tbl:header
    783                   clazz="index"
    784                   ><%=index%></tbl:header>
    785                 <tbl:header
    786                   clazz="check"
    787                   visible="<%=mode.hasCheck()%>"
    788                   ><input
    789                     type="checkbox"
    790                     name="<%=itemId%>"
    791                     value="<%=itemId%>"
    792                     title="<%=name%>"
    793                     <%=cc.getSelected().contains(itemId) ? "checked" : ""%>
    794                   ></tbl:header>
    795                 <tbl:header
    796                   clazz="check"
    797                   visible="<%=mode.hasRadio()%>"
    798                   ><input
    799                     type="radio"
    800                     name="item_id"
    801                     value="<%=itemId%>"
    802                     title="<%=name%>"
    803                     <%=selectedItemId == itemId ? "checked" : ""%>
    804                   ></tbl:header>
    805                 <tbl:header
    806                   clazz="icons"
    807                   visible="<%=mode.hasIcons()%>"
    808                   ><base:icon
    809                     image="deleted.png"
    810                     id="<%="delete."+itemId %>"
    811                     subclass="<%=deletePermission ? "table-delete-item" : "disabled" %>"
    812                     data-item-id="<%=itemId%>"
    813                     tooltip="This item has been scheduled for deletion"
    814                     visible="<%=item.isRemoved()%>"
    815                   /><base:icon
    816                     image="shared.png"
    817                     id="<%="share."+itemId %>"
    818                     subclass="<%=sharePermission ? "table-share-item" : "disabled" %>"
    819                     data-item-id="<%=itemId%>"
    820                     tooltip="This item is shared to other users, groups and/or projects"
    821                     visible="<%=item.isShared()%>"
    822                   />&nbsp;</tbl:header>
     779                <tbl:header clazz="row-index bg-filled-100">
     780                  <div class="index-<%=mode.getName()%>">
     781                    <div class="index <%=index>999?"index-smaller":""%>"><%=index%></div>
     782                    <div class="check">
     783                      <base:input
     784                        type="checkbox"
     785                        name="<%=itemId%>"
     786                        value="<%=itemId%>"
     787                        title="<%=name%>"
     788                        checked="<%=cc.getSelected().contains(itemId)%>"
     789                        visible="<%=mode.hasCheck()%>"
     790                      />
     791                      <base:input
     792                        type="radio"
     793                        name="item_id"
     794                        value="<%=itemId%>"
     795                        title="<%=name%>"
     796                        checked="<%=selectedItemId == itemId%>"
     797                        visible="<%=mode.hasRadio()%>"
     798                      />
     799                    </div>
     800                    <div class="icons">
     801                      <base:icon
     802                        image="deleted.png"
     803                        id="<%="delete."+itemId %>"
     804                        subclass="<%=deletePermission ? "table-delete-item" : "disabled" %>"
     805                        data-item-id="<%=itemId%>"
     806                        tooltip="This item has been scheduled for deletion"
     807                        visible="<%=item.isRemoved()%>"
     808                      /><base:icon
     809                        image="shared.png"
     810                        id="<%="share."+itemId %>"
     811                        subclass="<%=sharePermission ? "table-share-item" : "disabled" %>"
     812                        data-item-id="<%=itemId%>"
     813                        tooltip="This item is shared to other users, groups and/or projects"
     814                        visible="<%=item.isShared()%>"
     815                      />
     816                    </div>
     817                  </div>
     818                </tbl:header>
    823819                <tbl:cell column="name"><div
    824820                  class="link table-item"
  • branches/3.18-stable/www/biomaterials/samples/list_samples.jsp

    r7933 r7935  
    691691                <tbl:header clazz="row-index bg-filled-100">
    692692                  <div class="index-<%=mode.getName()%>">
    693                     <div class="index"><%=index%></div>
     693                    <div class="index <%=index>999?"index-smaller":""%>"><%=index%></div>
    694694                    <div class="check">
    695695                      <base:input
  • branches/3.18-stable/www/include/scripts/table.js

    r7894 r7935  
    176176      Buttons.addClickHandler(item, Buttons.shareItem, attributes);
    177177    }
     178   
     179    // Add handler for detecting when a sticky-col is stuck
     180    // The area is defined by the <tbl:data> element
     181    // If there is a "row-index" column we must apply a margin since the
     182    // sticky-col:s will stick to the right of the row-index column.
     183    var stickyCols = element.getElementsByClassName('sticky-col');
     184    var data = element.getElementsByClassName('data');
     185    if (stickyCols.length > 0 && data.length > 0)
     186    {
     187      var rowIndex = element.getElementsByClassName('row-index');
     188      var leftPos = rowIndex.length > 0 ? Doc.getElementPosition(rowIndex[0]).right : 0;
     189      var leftPos1 = leftPos-1;
     190      for (var i = 0; i < stickyCols.length; i++)
     191      {
     192        stickyCols[i].style.left = leftPos1+'px';
     193      }
     194     
     195      var observer = new IntersectionObserver(internal.handleStickyCol,
     196        {
     197          root: data[0],
     198          rootMargin: '0px 0px 0px -'+(leftPos)+'px',
     199          threshold: 1.0
     200        });
     201      observer.table = element;
     202      observer.observe(stickyCols[0]);
     203    }
    178204  }
    179205  Doc.addElementInitializer(internal.initTable);
     206 
     207  internal.handleStickyCol = function(entries, observer)
     208  {
     209    for (var i = 0; i < entries.length; i++)
     210    {
     211      var e = entries[i];
     212      Doc.addOrRemoveClass(observer.table, 'stuck-left', e.intersectionRatio < 1 && e.boundingClientRect.x < e.intersectionRect.x);
     213      Doc.addOrRemoveClass(observer.table, 'stuck-right', e.intersectionRatio < 1 && e.boundingClientRect.right > e.intersectionRect.right);
     214    }
     215  }
    180216 
    181217  /**
  • branches/3.18-stable/www/include/styles/table.css

    r7933 r7935  
    192192{
    193193  justify-content: flex-end;
     194  font-size: 95%;
     195}
     196.itemlist div.data th.row-index > div > div.index.index-smaller
     197{
     198  font-size: 85%;
    194199}
    195200.itemlist div.data th.row-index > div > div.check
     
    532537  position: sticky;
    533538  left: 0;
    534   min-width: 8em;
     539  min-width: 7rem;
    535540  border-right-width: 1px;
    536541  border-right-style: dotted;
     542  z-index: 8;
    537543}
    538544
     
    540546{
    541547  position: sticky;
    542   left: 8em;
    543   right: 0;
    544 }
     548  left: calc(7rem - 1px); /* This is also updated in table.js#initTable() */
     549  right: -1px;
     550  z-index: 6;
     551}
     552
     553.itemlist.sticky-headers.stuck-right div.data .sticky-col
     554{
     555  border-left-width: 1px;
     556  border-left-style: solid;
     557}
     558
     559.itemlist.sticky-headers.stuck-left div.data .sticky-col
     560{
     561  border-right-width: 1px;
     562  border-right-style: solid;
     563}
     564
     565
  • branches/3.18-stable/www/views/rawbioassays/list_rawbioassays.jsp

    r7932 r7935  
    200200      sc="<%=sc%>"
    201201      item="<%=itemType%>"
    202       subclass="fulltable sticky-headers"
     202      subclass="fulltable"
    203203      filterrows="<%=cc.getFilterRows()%>"
    204204      data-inherited-annotations="true"
    205205      data-relateditem-columns="true"
     206      stickyheaders="name"
    206207      >
    207208      <tbl:hidden
     
    618619        <tbl:headers>
    619620          <tbl:headerrow>
    620             <tbl:header colspan="3" />
     621            <tbl:header clazz="row-index bg-filled-100" />
    621622            <tbl:columnheaders />
    622623          </tbl:headerrow>
     
    629630            %>
    630631            <tbl:headerrow>
    631               <tbl:header subclass="index" />
    632               <tbl:header
    633                 subclass="check"
    634                 visible="<%=mode.hasCheck()%>"
    635                 ><base:icon
    636                   subclass="link table-check"
    637                   image="check_uncheck.png"
    638                   tooltip="Toggle all (use CTRL, ALT or SHIFT to check/uncheck)"
    639                   visible="<%=lastRow%>"
    640                 /></tbl:header>
    641               <tbl:header
    642                 subclass="check"
    643                 visible="<%=mode.hasRadio()%>"
    644                 />
    645               <tbl:header
    646                 subclass="icons"
    647                 visible="<%=mode.hasIcons()%>"
    648                 >
    649                 <base:icon
    650                   subclass="link table-filter-row-action"
    651                   image="add.png"
    652                   tooltip="Add extra filter row"
    653                   visible="<%=lastRow%>"
    654                 /><base:icon
    655                   subclass="link table-filter-row-action"
    656                   image="remove.png"
    657                   tooltip="Remove this filter row"
    658                   visible="<%=numRows > 1 || numFilters > 0 %>"
    659                   data-remove-row="<%=filterNo%>"
    660                 />
     632              <tbl:header subclass="row-index bg-filled-100">
     633                <div class="index-<%=mode.getName()%>">
     634                  <div class="index"></div>
     635                  <div class="check">
     636                    <base:icon
     637                      subclass="link table-check"
     638                      image="check_uncheck.png"
     639                      tooltip="Toggle all (use CTRL, ALT or SHIFT to check/uncheck)"
     640                      visible="<%=lastRow && mode.hasCheck()%>"
     641                    />
     642                  </div>
     643                  <div class="icons">
     644                    <base:icon
     645                      subclass="link table-filter-row-action"
     646                      image="add.png"
     647                      tooltip="Add extra filter row"
     648                      visible="<%=lastRow%>"
     649                    /><base:icon
     650                      subclass="link table-filter-row-action"
     651                      image="remove.png"
     652                      tooltip="Remove this filter row"
     653                      visible="<%=numRows > 1 || numFilters > 0 %>"
     654                      data-remove-row="<%=filterNo%>"
     655                    />
     656                  </div>
     657                </div>
    661658              </tbl:header>
    662659              <tbl:propertyfilter row="<%=filterNo%>" />
     
    705702              %>
    706703              <tbl:row>
    707                 <tbl:header
    708                   clazz="index"
    709                   ><%=index%></tbl:header>
    710                 <tbl:header
    711                   clazz="check"
    712                   visible="<%=mode.hasCheck()%>"
    713                   ><input
    714                     type="checkbox"
    715                     name="<%=itemId%>"
    716                     value="<%=itemId%>"
    717                     title="<%=name%>"
    718                     <%=cc.getSelected().contains(itemId) ? "checked" : ""%>
    719                   ></tbl:header>
    720                 <tbl:header
    721                   clazz="check"
    722                   visible="<%=mode.hasRadio()%>"
    723                   ><input
    724                     type="radio"
    725                     name="item_id"
    726                     value="<%=itemId%>"
    727                     title="<%=name%>"
    728                     <%=selectedItemId == itemId ? "checked" : ""%>
    729                   ></tbl:header>
    730                 <tbl:header
    731                   clazz="icons"
    732                   visible="<%=mode.hasIcons()%>"
    733                   ><base:icon
    734                     image="deleted.png"
    735                     id="<%="delete."+itemId %>"
    736                     subclass="<%=deletePermission ? "table-delete-item" : "disabled" %>"
    737                     data-item-id="<%=itemId%>"
    738                     tooltip="This item has been scheduled for deletion"
    739                     visible="<%=item.isRemoved()%>"
    740                   /><base:icon
    741                     image="shared.png"
    742                     id="<%="share."+itemId %>"
    743                     subclass="<%=sharePermission ? "table-share-item" : "disabled" %>"
    744                     data-item-id="<%=itemId%>"
    745                     tooltip="This item is shared to other users, groups and/or projects"
    746                     visible="<%=item.isShared()%>"
    747                   />&nbsp;</tbl:header>
     704                <tbl:header clazz="row-index bg-filled-100">
     705                  <div class="index-<%=mode.getName()%>">
     706                    <div class="index <%=index>999?"index-smaller":""%>"><%=index%></div>
     707                    <div class="check">
     708                      <base:input
     709                        type="checkbox"
     710                        name="<%=itemId%>"
     711                        value="<%=itemId%>"
     712                        title="<%=name%>"
     713                        checked="<%=cc.getSelected().contains(itemId)%>"
     714                        visible="<%=mode.hasCheck()%>"
     715                      />
     716                      <base:input
     717                        type="radio"
     718                        name="item_id"
     719                        value="<%=itemId%>"
     720                        title="<%=name%>"
     721                        checked="<%=selectedItemId == itemId%>"
     722                        visible="<%=mode.hasRadio()%>"
     723                      />
     724                    </div>
     725                    <div class="icons">
     726                      <base:icon
     727                        image="deleted.png"
     728                        id="<%="delete."+itemId %>"
     729                        subclass="<%=deletePermission ? "table-delete-item" : "disabled" %>"
     730                        data-item-id="<%=itemId%>"
     731                        tooltip="This item has been scheduled for deletion"
     732                        visible="<%=item.isRemoved()%>"
     733                      /><base:icon
     734                        image="shared.png"
     735                        id="<%="share."+itemId %>"
     736                        subclass="<%=sharePermission ? "table-share-item" : "disabled" %>"
     737                        data-item-id="<%=itemId%>"
     738                        tooltip="This item is shared to other users, groups and/or projects"
     739                        visible="<%=item.isShared()%>"
     740                      />
     741                    </div>
     742                  </div>
     743                </tbl:header>
    748744                <tbl:cell column="name"><div
    749745                  class="link table-item"
Note: See TracChangeset for help on using the changeset viewer.