]> git.draconx.ca Git - homepage.git/blob - layouts/clickytable.xsl
Add separator rows to the sorted part of clicky tables.
[homepage.git] / layouts / clickytable.xsl
1 <?xml version='1.0' encoding='UTF-8' ?>
2 <!--
3   Nick's web site: XHTML+CSS sortable clicky table headers.
4
5   Copyright © 2021 Nick Bowler
6
7   This implements the gory markup for clicky table headers, creating
8   input and related label elements to implement the clicking part.
9
10   To use: add the "clicky" attribute to each sortable header th element.
11   Class names corresponding to that header will be generated based on
12   the value of the attribute.  For example, a header with clicky='date'
13   will add class="clicky-date" to the header and generate two input
14   elements: one with class="clicky-date" to indicate when that column
15   is selected for sorting, and another with class="clicky-date-rev" to
16   indicate when the reverse ordering is selected for that column.
17
18   The input table itself must have two tbody sections. The first is a
19   completely normal table body in the default sort order.  The second
20   encodes all other possible orderings.
21
22   This program is free software: you can redistribute it and/or modify
23   it under the terms of the GNU General Public License as published by
24   the Free Software Foundation, either version 3 of the License, or
25   (at your option) any later version.
26
27   This program is distributed in the hope that it will be useful,
28   but WITHOUT ANY WARRANTY; without even the implied warranty of
29   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30   GNU General Public License for more details.
31
32   You should have received a copy of the GNU General Public License
33   along with this program.  If not, see <https://www.gnu.org/licenses/>
34 -->
35 <xsl:stylesheet version='1.0'
36   xmlns='http://www.w3.org/1999/xhtml'
37   xmlns:xhtml='http://www.w3.org/1999/xhtml'
38   xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
39
40 <xsl:template match='xhtml:table/@clicky|xhtml:th/@clicky' />
41 <xsl:template match='xhtml:table[@clicky]'>
42   <xsl:variable name='group' select='generate-id(@clicky)' />
43   <xsl:variable name='clicky' select='@clicky' />
44
45   <div>
46     <script type='x'><![CDATA[]]x><!--]]></script>
47     <xsl:for-each select='xhtml:thead/*/xhtml:th/@clicky'>
48       <!-- hoist default element to be first in document order -->
49       <xsl:sort select='number(.!=$clicky)' data-type='number' />
50
51       <input style='display: none' id='sort-{generate-id(.)}'
52         type='radio' name='{$group}' class='clicky-{.}'>
53         <xsl:if test='.=$clicky'>
54           <xsl:attribute name='checked'>checked</xsl:attribute>
55         </xsl:if>
56       </input>
57     </xsl:for-each>
58     <xsl:for-each select='xhtml:thead/*/xhtml:th/@clicky'>
59       <input style='display: none' id='rev-{generate-id(.)}'
60         type='checkbox' class='clicky-{.}-rev' />
61     </xsl:for-each>
62     <script type='x'>--></script>
63
64     <xsl:copy>
65       <xsl:apply-templates select='node()|@*' />
66     </xsl:copy>
67   </div>
68 </xsl:template>
69
70 <xsl:template match='xhtml:table[@clicky]/xhtml:thead/*/xhtml:th[@clicky]'>
71   <xsl:copy>
72     <xsl:attribute name='class'>
73       <xsl:if test='@class'>
74         <xsl:value-of select='concat(@class, " ")' />
75       </xsl:if>
76       <xsl:value-of select='concat("clicky-", @clicky)' />
77     </xsl:attribute>
78     <xsl:apply-templates select='@*[local-name() != "class"]' />
79
80     <label for='sort-{generate-id(@clicky)}'>
81       <xsl:text>&#x2060;</xsl:text>
82       <span><xsl:apply-templates select='node()' /></span>
83     </label>
84
85     <script type='x'><![CDATA[]]x><!--]]></script>
86     <label for='rev-{generate-id(@clicky)}' style='display: none'>
87       <xsl:text>&#x2060;</xsl:text>
88       <span><xsl:apply-templates select='node()' /></span>
89       <img alt='FWD' width='16' height='16' src='/icons/down.svg' />
90       <img alt='REV' width='16' height='16' src='/icons/up.svg' style='display: none' />
91     </label>
92     <script type='x'>--></script>
93   </xsl:copy>
94 </xsl:template>
95
96 <xsl:template match='xhtml:table[@clicky]/xhtml:tbody[last()]'>
97   <xsl:copy>
98     <xsl:attribute name='style'>
99       <xsl:if test='@style'>
100         <xsl:value-of select='concat(@style, "; ")' />
101       </xsl:if>
102       <xsl:text>display: none</xsl:text>
103     </xsl:attribute>
104     <xsl:apply-templates select='@*[local-name() != "style"]' />
105     <tr>
106       <td>
107         <xsl:for-each select='../*/xhtml:tr'>
108           <xsl:sort select='count(*)' data-type='number' order='descending' />
109           <xsl:if test='position()=1'>
110             <xsl:attribute name='colspan'>
111               <xsl:value-of select='count(*)' />
112             </xsl:attribute>
113           </xsl:if>
114         </xsl:for-each>
115         <hr/>
116       </td>
117     </tr>
118     <tr>
119       <xsl:for-each select='../xhtml:thead[1]/xhtml:tr[1]/*'>
120         <xsl:copy>
121           <xsl:value-of select='.' />
122         </xsl:copy>
123       </xsl:for-each>
124     </tr>
125     <xsl:apply-templates select='node()' />
126   </xsl:copy>
127 </xsl:template>
128
129 <!-- Insert script hack around the second <tbody> -->
130 <xsl:template match='xhtml:table[@clicky]/xhtml:tbody[1]/*[last()]/*[last()]'>
131   <xsl:copy>
132     <xsl:apply-templates select='node()|@*' />
133     <script type='x'><![CDATA[]]x><!--]]></script>
134   </xsl:copy>
135 </xsl:template>
136
137 <!-- Note that it is not allowed for <tr> to have no child elements. -->
138 <xsl:template
139   match='xhtml:table[@clicky]/xhtml:tbody[last()]/*[last()]/*[last()]'>
140   <xsl:copy>
141     <xsl:apply-templates select='node()|@*' />
142     <script type='x'>--></script>
143   </xsl:copy>
144 </xsl:template>
145
146 </xsl:stylesheet>