diff --git a/assets/css/sortTable.css b/assets/css/sortTable.css new file mode 100644 index 0000000..47ca04f --- /dev/null +++ b/assets/css/sortTable.css @@ -0,0 +1,101 @@ +.sr-only { + position: absolute; + top: -30em; +} + +table.sortable td, +table.sortable th { + padding: 0.125em 0.25em; + width: 22em; +} + +table.sortable th { + font-weight: bold; + border-bottom: thin solid #888; + position: relative; +} + +table.sortable th.no-sort { + padding-top: 0.35em; +} + +table.sortable th:nth-child(5) { + width: 10em; +} + +table.sortable th button { + padding: 4px; + margin: 1px; + font-size: 100%; + font-weight: bold; + background: transparent; + border: none; + display: inline; + right: 0; + left: 0; + top: 0; + bottom: 0; + width: 100%; + text-align: left; + outline: none; + cursor: pointer; +} + +table.sortable th button span { + position: absolute; + right: 4px; +} + +table.sortable th[aria-sort="descending"] span::after { + content: "▼"; + color: currentcolor; + font-size: 100%; + top: 0; +} + +table.sortable th[aria-sort="ascending"] span::after { + content: "▲"; + color: currentcolor; + font-size: 100%; + top: 0; +} + +table.show-unsorted-icon th:not([aria-sort]) button span::after { + content: "♢"; + color: currentcolor; + font-size: 100%; + position: relative; + top: -3px; + left: -4px; +} + +table.sortable td.num { + text-align: right; +} + +table.sortable tbody tr:nth-child(odd) { + background-color: #ddd; +} + +/* Focus and hover styling */ + +table.sortable th button:focus, +table.sortable th button:hover { + padding: 2px; + border: 2px solid currentcolor; + background-color: #e5f4ff; +} + +table.sortable th button:focus span, +table.sortable th button:hover span { + right: 2px; +} + +table.sortable th:not([aria-sort]) button:focus span::after, +table.sortable th:not([aria-sort]) button:hover span::after { + content: "▼"; + color: currentcolor; + font-size: 100%; + top: 0; +} + diff --git a/assets/js/sortable-table.js b/assets/js/sortable-table.js new file mode 100644 index 0000000..65be61c --- /dev/null +++ b/assets/js/sortable-table.js @@ -0,0 +1,168 @@ +/* + * This content is licensed according to the W3C Software License at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document + * + * File: sortable-table.js + * + * Desc: Adds sorting to a HTML data table that implements ARIA Authoring Practices + */ + +'use strict'; + +class SortableTable { + constructor(tableNode) { + this.tableNode = tableNode; + + this.columnHeaders = tableNode.querySelectorAll('thead th'); + + this.sortColumns = []; + + for (var i = 0; i < this.columnHeaders.length; i++) { + var ch = this.columnHeaders[i]; + var buttonNode = ch.querySelector('button'); + if (buttonNode) { + this.sortColumns.push(i); + buttonNode.setAttribute('data-column-index', i); + buttonNode.addEventListener('click', this.handleClick.bind(this)); + } + } + + this.optionCheckbox = document.querySelector( + 'input[type="checkbox"][value="show-unsorted-icon"]' + ); + + if (this.optionCheckbox) { + this.optionCheckbox.addEventListener( + 'change', + this.handleOptionChange.bind(this) + ); + if (this.optionCheckbox.checked) { + this.tableNode.classList.add('show-unsorted-icon'); + } + } + } + + setColumnHeaderSort(columnIndex) { + if (typeof columnIndex === 'string') { + columnIndex = parseInt(columnIndex); + } + + for (var i = 0; i < this.columnHeaders.length; i++) { + var ch = this.columnHeaders[i]; + var buttonNode = ch.querySelector('button'); + if (i === columnIndex) { + var value = ch.getAttribute('aria-sort'); + if (value === 'descending') { + ch.setAttribute('aria-sort', 'ascending'); + this.sortColumn( + columnIndex, + 'ascending', + ch.classList.contains('num') + ); + } else { + ch.setAttribute('aria-sort', 'descending'); + this.sortColumn( + columnIndex, + 'descending', + ch.classList.contains('num') + ); + } + } else { + if (ch.hasAttribute('aria-sort') && buttonNode) { + ch.removeAttribute('aria-sort'); + } + } + } + } + + sortColumn(columnIndex, sortValue, isNumber) { + function compareValues(a, b) { + if (sortValue === 'ascending') { + if (a.value === b.value) { + return 0; + } else { + if (isNumber) { + return a.value - b.value; + } else { + return a.value < b.value ? -1 : 1; + } + } + } else { + if (a.value === b.value) { + return 0; + } else { + if (isNumber) { + return b.value - a.value; + } else { + return a.value > b.value ? -1 : 1; + } + } + } + } + + if (typeof isNumber !== 'boolean') { + isNumber = false; + } + + var tbodyNode = this.tableNode.querySelector('tbody'); + var rowNodes = []; + var dataCells = []; + + var rowNode = tbodyNode.firstElementChild; + + var index = 0; + while (rowNode) { + rowNodes.push(rowNode); + var rowCells = rowNode.querySelectorAll('th, td'); + var dataCell = rowCells[columnIndex]; + + var data = {}; + data.index = index; + data.value = dataCell.textContent.toLowerCase().trim(); + if (isNumber) { + data.value = parseFloat(data.value); + } + dataCells.push(data); + rowNode = rowNode.nextElementSibling; + index += 1; + } + + dataCells.sort(compareValues); + + // remove rows + while (tbodyNode.firstChild) { + tbodyNode.removeChild(tbodyNode.lastChild); + } + + // add sorted rows + for (var i = 0; i < dataCells.length; i += 1) { + tbodyNode.appendChild(rowNodes[dataCells[i].index]); + } + } + + /* EVENT HANDLERS */ + + handleClick(event) { + var tgt = event.currentTarget; + this.setColumnHeaderSort(tgt.getAttribute('data-column-index')); + } + + handleOptionChange(event) { + var tgt = event.currentTarget; + + if (tgt.checked) { + this.tableNode.classList.add('show-unsorted-icon'); + } else { + this.tableNode.classList.remove('show-unsorted-icon'); + } + } +} + +// Initialize sortable table buttons +window.addEventListener('load', function () { + var sortableTables = document.querySelectorAll('table.sortable'); + for (var i = 0; i < sortableTables.length; i++) { + new SortableTable(sortableTables[i]); + } +}); + diff --git a/docs/flux/flux-commands-table.html b/docs/flux/flux-commands-table.html new file mode 100644 index 0000000..b06ebdd --- /dev/null +++ b/docs/flux/flux-commands-table.html @@ -0,0 +1,122 @@ + + +
+ + + + + + ++ + | +description | +options | ++ + + | ++ + + | +example in Playground | +java class | +
---|---|---|---|---|---|---|
add-oreaggregation | +Adds ore:Aggregation to an Europeana Data Model stream. The aggregation id is set by emitting literal('aggregation_id', id) | +- | +StreamReceiver | +StreamReceiver | +- | +org.metafacture.linkeddata.OreAggregationAdder | +
add-preamble-epilogue | +Adds a String preamle and/or epilogue to the stream | +preamble (String), epilogue (String) | +String | +String | +example in Playground | +org.metafacture.formatting.PreambleEpilogueAdder | +
badd-preamble-epilogue | +bAdds a String preamle and/or epilogue to the stream | +preamble (String), epilogue (String) | +String | +StreamReceiver | +example in Playground | +org.metafacture.formatting.PreambleEpilogueAdder | +
badd-preamble-epilogue | +bAdds a String preamle and/or epilogue to the stream | +preamble (String), epilogue (String) | +StreamReceiver | +String | +example in Playground | +org.metafacture.formatting.PreambleEpilogueAdder | +