]> git.draconx.ca Git - homepage.git/blobdiff - content/style.scss
Improve clicky table generation.
[homepage.git] / content / style.scss
index 22df5d950a9ff8ef49c73aa3e014daec840018ae..dc306d72efcc51c0dae8e87f87b229f6b382b64f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Nick's web site: default stylesheet
  *
- * Copyright © 2018-2020 Nick Bowler
+ * Copyright © 2018-2021 Nick Bowler
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -118,6 +118,7 @@ td, th {
 
 thead>tr, tbody>tr { border: solid $ruledefaultcolour; }
 th, thead>tr { border-bottom: 1px solid $rulestrongcolour; }
+tbody+tbody { border-bottom: 1px solid $ruledefaultcolour; }
 *>table, *>th { border: none; }
 thead>tr { border-width: 1px; }
 tbody>tr { border-width: 0 1px; }
@@ -137,13 +138,47 @@ table.cc {
     }
 }
 
-$sortcols: name, date, size;
-@each $col in $sortcols {
-    #filelist-#{$col}-sort {
+// CSS rules for stortable clicky table headers: Update the display of
+// the /table based on the current state.  Each column has its own set
+// nearly-identical rules, only the class names differ.
+//
+// The clickytables.xsl stylesheet generates two inputs for each column.
+// These inputs are siblings of the table and all precede it in document
+// order.  Moreover, the inputs for a column are ordered with respect to
+// each other, in this sequence:
+//
+//   input.clicky-NAME     -- checked iff NAME is selected for sorting.
+//   input.clicky-NAME-rev -- checked to select reverse order.
+//
+// One of the column selection inputs will have a 'checked' attribute to
+// indicate the default order.  This input is always first in document
+// order.  No other inputs begin checked.
+//
+// The table itself consists of a thead (where the header labels are
+// located) and two tbody elements.  The bulk of these rules relate
+// to updating the headers to visually indicate the current state.
+//
+// A sortable column's th element has the .clicky-NAME class, matching
+// its corresponding inputs, and has two label children.  The first label
+// is visible only when the column is unselected, and is linked to the
+// .clicky-NAME input to activate that column.  The second label is visible
+// only on the selected column and is linked to the .clicky-NAME-rev input
+// to toggle the reverse order.
+//
+// For the table body, the first tbody contains the default ordering
+// and is not styled by these rules (except to hide it when alternate
+// orderings are selected).  The second tbody contains rows for all
+// alternate orderings, and is revealed by these rules.  When revealed,
+// rows with the NAMEfwd or NAMErev class (for the forward and reverse
+// orderings, respectively) are shown and other rows are hidden.
+
+$clickynames: name, date, size;
+@each $col in $clickynames {
+    input.clicky-#{$col} {
         &:checked {
-            & ~ table.filelist {
-                /* Update table header state */
-                th.#{$col} {
+            &~table {
+                // Update table header state
+                & th.clicky-#{$col} {
                     label~label {
                         display: -moz-inline-box !important;
                         display: inline-block !important;
@@ -151,60 +186,110 @@ $sortcols: name, date, size;
                     label { display: none; }
                 }
 
-                /* Show only appropriate items from the sort body (forward) */
-                tbody+tbody>tr.#{$col} { display: table-row; }
-                tbody+tbody>tr { display: none; }
+                // Show only appropriate items from the sort body (forward)
+                &>tbody+tbody>tr.#{$col}fwd { display: table-row; }
+                &>tbody+tbody>tr { display: none; }
+
+                // Unhide sort body
+                &>tbody {
+                    &+tbody { display: table-row-group !important; }
+                    display: none;
+                }
             }
 
-            & ~ #filelist-#{$col}-rev:checked ~ table.filelist {
-                /* Show only appropriate items from sort body (reversed) */
-                tbody+tbody>tr.#{$col}rev { display: table-row; }
-                tbody+tbody>tr { display: none; }
+            // reverse state for selected sort column
+            &~input.clicky-#{$col}-rev {
+                &:checked ~ table {
+                    // Show only appropriate items from sort body (reversed)
+                    &>tbody+tbody>tr.#{$col}rev { display: table-row; }
+                    &>tbody+tbody>tr { display: none; }
+
+                    // Unhide sort body
+                    &>tbody {
+                        &+tbody { display: table-row-group !important; }
+                        display: none;
+                    }
+                }
+
+                // Unhide to allow keyboard navigation to this input
+                display: block !important;
             }
+        }
 
-            /* Unhide associated checkbox for keyboard navigation */
-            & ~ #filelist-#{$col}-rev { display: block !important; }
+        // If default input element is the only one selected, match it to
+        // return to default view (overriding the changes above).  It is
+        // always the first input element among all the sibling elements.
+        // This seems to interoperate better than using the [checked] or
+        // :first-of-type selectors.
+        @at-root &:first-child, :not(input)+& {
+            &:checked~table>tbody {
+                &+tbody { display: none !important; }
+                display: table-row-group;
+            }
         }
 
-        &:focus ~ table.filelist th>label~label>span {
-            border: 1px dotted;
-            padding: 0;
+        &:focus ~ table th.clicky-#{$col}>label~label>span {
+            border-color: $foregroundcolour;
         }
 
+        // Unhide to allow keyboard navigation
         display: block !important;
+        pointer-events: none;
         position: absolute;
-        z-index: -1;
         opacity: 0;
+        z-index: -1;
     }
 
-    #filelist-#{$col}-rev {
-        &:checked ~ table.filelist {
-            /* Update table header state */
-            th.#{$col} {
+    input.clicky-#{$col}-rev {
+        &:checked ~ table {
+            // Update table header state
+            & th.clicky-#{$col} {
                 img+img {
                     display: -moz-inline-box !important;
-                    display: inline !important;
+                    display: inline-block !important;
                 }
                 img { display: none; }
             }
         }
 
-        &:focus ~ table.filelist th>label~label>img {
-            border: 1px dotted;
-            padding: 0;
+        &:focus ~ table th.clicky-#{$col}>label~label>img {
+            border-color: $foregroundcolour;
         }
 
+        pointer-events: none;
         position: absolute;
-        z-index: -2;
         opacity: 0;
+        z-index: -2;
     }
-}
 
-/* Enable the sorted tables only when non-default option is selected */
-#filelist-name-rev, #filelist-date-sort, #filelist-size-sort {
-    &:checked~table.filelist>tbody {
-        &+tbody { display: table-row-group !important; }
-        display: none;
+    th.clicky-#{$col}>label {
+        &, &>* {
+            white-space: nowrap;
+            vertical-align: middle;
+            display: -moz-inline-box;
+            display: inline-block;
+            cursor: pointer;
+        }
+
+        &>* { border: 1px dotted transparent; }
+
+        // Expand the first label a bit so the table (hopefully)
+        // does not reshape as columns are selected.
+        &:first-child {
+            margin-right: 1.75em;
+            padding-right: 2px;
+        }
+
+        &:active { color: $linkactivecolour; }
+        &:first-child:active>span, &~label:active>img {
+            border-color: $linkactivecolour;
+        }
+
+        img {
+            margin-left: 0.25em;
+            width: 1.5em;
+            height: auto;
+        }
     }
 }
 
@@ -214,21 +299,7 @@ table.filelist {
         width: 0;
     }
 
-    th>label>* { padding: 1px; }
-    th>label, th>label>* {
-        white-space: nowrap;
-        vertical-align: middle;
-        display: -moz-inline-box;
-        display: inline-block;
-        cursor: pointer;
-    }
-    th img { margin-left: 0.5ex; }
-
-    tbody+tbody {
-        border-bottom: solid 1px $ruledefaultcolour;
-    }
-
-    img {
+    tbody img {
         display: block;
         height: 1.5em;
         width: auto;