commit 1c2b346d97271735b994f8fe504600e40baa227b
parent 5e24bc5c5de6855d927328e6f76c01f6bd99d93d
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date: Fri, 2 Jul 2021 11:54:30 -0700
Refactor reference tables to use data-table
Diffstat:
7 files changed, 57 insertions(+), 206 deletions(-)
diff --git a/css/reference.css b/css/reference.css
@@ -38,26 +38,11 @@
}
/* Table */
-.referenceTable {
- text-align: left;
- overflow-x: auto;
-}
.referenceTable table {
+ width: auto;
margin: auto;
}
-.referenceTable th {
- background-color: var(--hover-color);
- border: 1px solid var(--border-color);
- position: sticky;
- position: -webkit-sticky;
- top: 0;
-}
-.referenceTable th div {
- display: flex;
- align-items: center;
-}
.referenceTable td {
- border: 1px solid var(--border-color);
cursor: pointer;
}
diff --git a/index.html b/index.html
@@ -9,6 +9,7 @@
<link rel="manifest" href="manifest.json">
<link rel="icon" type="image/png" href="images/favicon-32.png">
<link rel="apple-touch-icon" sizes="180x180" href="images/favicon-180.png">
+ <link rel="stylesheet" href="vendor/data-table.min.css">
<link rel="stylesheet" href="css/global.css">
<link rel="stylesheet" href="css/app.css">
<link rel="stylesheet" href="css/filtersPage.css">
@@ -16,6 +17,7 @@
<link rel="stylesheet" href="css/quizzer.css">
<link rel="stylesheet" href="css/reference.css">
<script src="vendor/diff.js"></script>
+ <script src="vendor/data-table.min.js"></script>
<script src="vendor/papaparse.js"></script>
<script src="vendor/vue.js"></script>
<script src="vendor/vue-router.js"></script>
diff --git a/js/reference.js b/js/reference.js
@@ -9,17 +9,30 @@ const referenceTables = Vue.component("referenceTables", {
data: function() {
return {
category: "Choose a category",
- query: "",
conjugationColors: true,
- sortIndex: 0,
- sortAccending: true,
+ tableTheme: null,
+ table: null,
}
},
watch: {
- category: function() {
- // Reset sortIndex and sortAccending
- this.sortIndex = 0;
- this.sortAccending = true;
+ category: function(value) {
+ // Update table
+ this.table.setData({
+ headers: this.tableData[value][0],
+ body: this.tableData[value].slice(1),
+ bodyClasses: value === "verbs" && this.conjugationColors ? this.conjugationColorClasses : null,
+ });
+ this.table.sort(0, true);
+ },
+ tableTheme: function(value) {
+ // Update table theme
+ this.table.theme = value;
+ },
+ conjugationColors: function(value) {
+ // Update table body classes
+ this.table.setData({
+ bodyClasses: this.category === "verbs" && value ? this.conjugationColorClasses : null,
+ });
}
},
computed: {
@@ -58,7 +71,7 @@ const referenceTables = Vue.component("referenceTables", {
}
}
}
- return result;
+ return result.slice(1);
},
/**
* The data used by the table
@@ -75,55 +88,7 @@ const referenceTables = Vue.component("referenceTables", {
search: function(args) {
if (args) args.target.blur();
this.$refs.search.blur();
- this.query = this.$refs.search.value;
- },
-
- /**
- * Sort the table by values in a column
- * @param {Number} index The index of the column to sort by
- * @param {Boolean} accending Whether to sort accending or descending
- */
- sortColumn: function(index, accending) {
- // Get sort direction
- let direction;
- if (accending !== undefined) {
- direction = accending;
- }
- else if (this.sortIndex === index) {
- direction = !this.sortAccending;
- }
- else {
- direction = true;
- }
-
- // Remove headers
- let headers = this.data[this.category][0];
- this.data[this.category] = this.data[this.category].slice(1);
-
- // Sort data
- if (this.sortIndex === index && this.sortAccending === direction) {
- // Data is sorted by correct column AND in correct direction
- }
- else if (this.sortIndex === index && this.sortAccending !== direction) {
- // Data is sorted by correct column but in wrong direction
- this.data[this.category].reverse();
- }
- else {
- // Data is sorted by incorrect column AND in incorrect direction
- this.data[this.category].sort((a, b) => {
- if (a[index] === b[index]) return 0;
- else if (a[index] < b[index]) return -1;
- else return 1;
- });
- if (!direction) this.data[this.category].reverse();
- }
-
- // Reinsert headers
- this.data[this.category].unshift(headers);
-
- // Set sortStatus
- this.sortIndex = index;
- this.sortAccending = direction;
+ this.table.search(this.$refs.search.value);
},
/**
@@ -178,6 +143,17 @@ const referenceTables = Vue.component("referenceTables", {
},
},
mounted: function() {
+ // Generate table
+ this.table = new DataTable(".referenceTable", {
+ sortable: true,
+ unsortable: false,
+ bodyEventHandlers: {
+ click: (row, column, args) => {
+ this.Read(this.tableData[this.category][row + 1][column], this.tableData[this.category][0][column]);
+ }
+ }
+ });
+
// Set table height
this.setTableHeight();
@@ -195,8 +171,10 @@ const referenceTables = Vue.component("referenceTables", {
window.removeEventListener("keyup", this.keyup);
},
activated: function() {
- // Update conjugationColors setting
- this.conjugationColors = getSettings().conjugationColors;
+ // Update settings
+ let settings = getSettings();
+ this.conjugationColors = settings.conjugationColors;
+ this.tableTheme = settings.darkTheme ? "basic-dark" : "basic-light";
},
template: `
<div>
@@ -213,23 +191,6 @@ const referenceTables = Vue.component("referenceTables", {
</div>
<div class="referenceTable" ref="referenceTable">
- <table>
- <tr v-for="(row, rowIndex) in data[category]" v-show="rowIndex === 0 || row.join(',').toLowerCase().includes(query.toLowerCase())">
- <th v-if="rowIndex === 0" v-for="(column, columnIndex) in row" @click="sortColumn(columnIndex)">
- <div>
- <span>{{ column }}</span>
- <button class="icon">
- <img v-if="sortIndex === columnIndex && sortAccending" src="images/chevron-up.svg">
- </button>
- <button class="icon">
- <img v-if="sortIndex === columnIndex && !sortAccending" src="images/chevron-down.svg">
- </button>
- </div>
- </th>
- <td v-if="rowIndex !== 0" v-for="(column, columnIndex) in row" @click="Read(column, data[category][0][columnIndex])"
- :lang="getLang(data[category][0][columnIndex])" :class="(conjugationColors && category === 'verbs') ? conjugationColorClasses[rowIndex][columnIndex] : 'normal'">{{ column }}</td>
- </tr>
- </table>
</div>
</div>
`
diff --git a/service-worker.js b/service-worker.js
@@ -1,5 +1,5 @@
// Initialize constants
-const version = "spanish-quizzer-6";
+const version = "spanish-quizzer-7";
const resources = [
"./css/app.css",
"./css/filtersPage.css",
@@ -29,6 +29,8 @@ const resources = [
"./js/reference.js",
"./js/settingsPage.js",
"./vendor/diff.js",
+ "./vendor/data-table.min.css",
+ "./vendor/data-table.min.js",
"./vendor/papaparse.js",
"./vendor/vue-router.js",
"./vendor/vue.js",
diff --git a/tests/test.reference.js b/tests/test.reference.js
@@ -10,36 +10,12 @@ describe("ReferenceTables", function() {
expect(ReferenceTables.category).to.equal("Choose a category");
});
- it("Query should be empty", function() {
- expect(ReferenceTables.query).to.equal("");
- });
-
it("ConjugationColors should be true", function() {
expect(ReferenceTables.conjugationColors).to.be.true;
});
- it("SortIndex should be 0", function() {
- expect(ReferenceTables.sortIndex).to.equal(0);
- });
-
- it("SortAccending should be true", function() {
- expect(ReferenceTables.sortAccending).to.be.true;
- });
- });
-
- describe("Category watch", function() {
- it("Should reset sortIndex and sortAccending", async function() {
- // Set sortIndex and sortAccending
- ReferenceTables.sortIndex = 1;
- ReferenceTables.sortAccending = false;
-
- // Set category
- ReferenceTables.category = "new category";
- await ReferenceTables.$nextTick();
-
- // Assert sortIndex and sortAccending are reset
- expect(ReferenceTables.sortIndex).to.equal(0);
- expect(ReferenceTables.sortAccending).to.be.true;
+ it("tableTheme should be null", function() {
+ expect(ReferenceTables.tableTheme).to.be.null;
});
});
@@ -65,9 +41,6 @@ describe("ReferenceTables", function() {
// Assert conjugationColorClasses is correct
expect(ReferenceTables.conjugationColorClasses).to.deep.equal([
- // Test table headers
- ["normal","normal","normal","normal","normal","normal","normal","normal","normal","normal","normal","normal","normal","normal"],
-
// Test regular conjugations and orthographic conjugations
["normal","normal","regular","regular","regular","regular","regular","regular","nonregular","nonregular","nonregular","nonregular","nonregular","nonregular"],
@@ -108,88 +81,4 @@ describe("ReferenceTables", function() {
expect(ReferenceTables.tableData).to.deep.equal({...{"Choose a category":[]}, ...data});
});
});
-
- describe("SortColumn method", function() {
- it("Should correctly sort table", function() {
- // Set table data and category
- ReferenceTables.data = {"category1":[
- ["English", "Spanish"],
- ["Red", "Rojo"],
- ["Green", "Verde"],
- ["Blue", "Azul"],
- ]};
- ReferenceTables.category = "category1";
-
- // Sort table
- ReferenceTables.sortColumn(1, false);
-
- // Assert table data is correct
- expect(ReferenceTables.data).to.deep.equal({"category1":[
- ["English", "Spanish"],
- ["Green", "Verde"],
- ["Red", "Rojo"],
- ["Blue", "Azul"],
- ]});
-
- // Assert sortIndex and sortAccending are correct
- expect(ReferenceTables.sortIndex).to.equal(1);
- expect(ReferenceTables.sortAccending).to.be.false;
- });
-
- it("Should correctly choose sort direction if column is already sorted", function() {
- // Set variables
- ReferenceTables.data = {"category1":[
- ["English", "Spanish"],
- ["Blue", "Azul"],
- ["Red", "Rojo"],
- ["Green", "Verde"],
- ]};
- ReferenceTables.category = "category1";
- ReferenceTables.sortIndex = 1;
- ReferenceTables.sortAccending = true;
-
- // Sort table
- ReferenceTables.sortColumn(1);
-
- // Assert table data is correct
- expect(ReferenceTables.data).to.deep.equal({"category1":[
- ["English", "Spanish"],
- ["Green", "Verde"],
- ["Red", "Rojo"],
- ["Blue", "Azul"],
- ]});
-
- // Assert sortIndex and sortAccending are correct
- expect(ReferenceTables.sortIndex).to.equal(1);
- expect(ReferenceTables.sortAccending).to.be.false;
- });
-
- it("Should correctly choose sort direction if column is not already sorted", function() {
- // Set variables
- ReferenceTables.data = {"category1":[
- ["English", "Spanish"],
- ["Blue", "Azul"],
- ["Green", "Verde"],
- ["Red", "Rojo"],
- ]};
- ReferenceTables.category = "category1";
- ReferenceTables.sortIndex = 0;
- ReferenceTables.sortAccending = true;
-
- // Sort table
- ReferenceTables.sortColumn(1);
-
- // Assert table data is correct
- expect(ReferenceTables.data.category1).to.deep.equal([
- ["English", "Spanish"],
- ["Blue", "Azul"],
- ["Red", "Rojo"],
- ["Green", "Verde"],
- ]);
-
- // Assert sortIndex and sortAccending are correct
- expect(ReferenceTables.sortIndex).to.equal(1);
- expect(ReferenceTables.sortAccending).to.be.true;
- });
- });
});
diff --git a/vendor/data-table.min.css b/vendor/data-table.min.css
@@ -0,0 +1,6 @@
+/*
+ * data-table 1.0.0
+ * (c) 2021 Asher Morgan (MIT)
+ * https://github.com/ashermorgan/data-table
+ */
+.data-table{overflow-x:auto;overflow-y:auto}.data-table table{width:100%;border-spacing:0;text-align:left}.data-table table th{position:sticky;position:-webkit-sticky;top:0}.data-table table th button{margin:0;padding:0;border:none;background:0 0;cursor:pointer;vertical-align:middle}.data-table table th button svg{height:24px;width:24px}.data-table table.basic-light{border:1px solid #000;border-collapse:separate}.data-table table.basic-light td{border:1px solid #000;color:#000;background-color:#fff}.data-table table.basic-light th{border:1px solid #000;color:#000;background-color:#e0e0e0}.data-table table.basic-light svg{color:#000}.data-table table.basic-dark{border:1px solid grey;border-collapse:separate}.data-table table.basic-dark td{border:1px solid grey;color:#e0e0e0;background-color:#202020}.data-table table.basic-dark th{border:1px solid grey;color:#e0e0e0;background-color:#404040}.data-table table.basic-dark svg{color:#e0e0e0}
diff --git a/vendor/data-table.min.js b/vendor/data-table.min.js
@@ -0,0 +1,6 @@
+/*
+ * data-table 1.0.0
+ * (c) 2021 Asher Morgan (MIT)
+ * https://github.com/ashermorgan/data-table
+ */
+let DataTable=function(e,t){"use strict";let o=[];Object.defineProperty(this,"body",{get:function(){return o}});let r=null;Object.defineProperty(this,"bodyClasses",{get:function(){return r}});let l={};Object.defineProperty(this,"bodyEventHandlers",{get:function(){return l}});let i=!1;Object.defineProperty(this,"dataIsHTML",{get:function(){return i},set:function(e){i=e,this.render()}});let n=[];Object.defineProperty(this,"headers",{get:function(){return n}});let s=null;Object.defineProperty(this,"headerClasses",{get:function(){return s}});let d={};Object.defineProperty(this,"headerEventHandlers",{get:function(){return d}});let a={up:'<svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="data-table-up"><polyline points="18 16 12 8 6 16 18 16"></polyline></svg>',down:'<svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="data-table-down"><polyline points="18 8 12 16 6 8 18 8"></polyline></svg>',updown:'<svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="data-table-updown"><polyline points="15 10 12 6 9 10 15 10"></polyline><polyline points="15 14 12 18 9 14 15 14"></polyline></svg>'};Object.defineProperty(this,"downIcon",{get:function(){return a.down},set:function(e){a.down=e,this.render()}}),Object.defineProperty(this,"upIcon",{get:function(){return a.up},set:function(e){a.up=e,this.render()}}),Object.defineProperty(this,"updownIcon",{get:function(){return a.updown},set:function(e){a.updown=e,this.render()}});let u="";Object.defineProperty(this,"searchQuery",{get:function(){return u}});let c=null;Object.defineProperty(this,"selector",{get:function(){return c}});let h=!1;Object.defineProperty(this,"sortable",{get:function(){return h},set:function(e){h=e,this.render()}});let f=null;Object.defineProperty(this,"sortAscending",{get:function(){return f}});let b=null;Object.defineProperty(this,"sortIndex",{get:function(){return b}});let p={headers:[],body:[]},y="basic-light";Object.defineProperty(this,"theme",{get:function(){return y},set:function(e){y=e,this.render()}});let v=!0;Object.defineProperty(this,"unsortable",{get:function(){return v},set:function(e){v=e,this.render()}});var g,w;let j=function(){p.headers=[];for(let e=0;e<n.length;e++)p.headers.push({value:n[e],class:s?s[e]:""});p.body=[];for(let n=0;n<o.length;n++){let t={visible:!0,columns:[],row:n};for(let e=0;e<o[n].length;e++)t.columns.push({value:o[n][e],class:r?r[n][e]:""});p.body.push(t)}C(u);var e=b,t=f;b=null,f=null,O(e,t)};this.render=function(){let e=document.querySelector(this.selector);e.classList.add("data-table");let t=`<table class="${y?y.toLowerCase():""}">`;if(0<p.headers.length){t+="<thead><tr>";for(let e=0;e<p.headers.length;e++)t+=`<th class="${p.headers[e].class}">${i?p.headers[e].value:m(p.headers[e].value)}`,h&&(b!==e?t+=`<button>${a.updown}</button>`:b===e&&!0===f?t+=`<button>${a.up}</button>`:b===e&&!1===f&&(t+=`<button>${a.down}</button>`)),t+="</th>";t+="</thead></tr>"}if(0<p.body.length){t+="<tbody>";for(var n of p.body){t+=`<tr${n.visible?"":' hidden=""'}>`;for(var o of n.columns)t+=`<td class="${o.class}">${i?o.value:m(o.value)}</td>`;t+="</tr>"}t+="</tbody>"}t+="</table>",e.innerHTML=t;let r=document.querySelectorAll(`${this.selector} thead th`),s=document.querySelectorAll(`${this.selector} tbody td`);if(h)for(let e=0;e<r.length;e++)r[e].addEventListener("click",()=>{b!==e?this.sort(e,!0):!0===f?this.sort(e,!1):v?this.sort(e,null):this.sort(e,!0)});for(let o in l){let e=-1;for(let n=0;n<p.body.length;n++)for(let t=0;t<p.body[n].columns.length;t++)e++,s[e].addEventListener(o,e=>{l[o](p.body[n].row,t,e)})}for(let n in d)for(let t=0;t<p.headers.length;t++)r[t].addEventListener(n,e=>{d[n](t,e)})};let m=function(e){return e=(e=(e=(e=(e=e.replace(/&/g,"&")).replace(/</g,"<")).replace(/>/g,">")).replace(/\n/g,"<br>")).replace(/\t/g," ")},C=function(e){for(var t of p.body){t.visible=!1;for(var n of t.columns)if(n.value.toLowerCase().includes(e.toLowerCase())){t.visible=!0;break}}u=e};this.search=function(e){C(e),this.render()},this.setData=function(e){e&&(void 0!==e.body&&(o=e.body),void 0!==e.bodyClasses&&(r=e.bodyClasses),void 0!==e.headers&&(n=e.headers),void 0!==e.headerClasses&&(s=e.headerClasses)),j(),this.render()};let O=function(n,e){b===n&&f===e||(n<0||n>=p.headers.length||null===e?(p.body.sort((e,t)=>e.row<t.row?-1:1),e=n=null):b===n&&f!==e?p.body.reverse():(p.body.sort((e,t)=>e.columns[n].value===t.columns[n].value?0:e.columns[n].value<t.columns[n].value?-1:1),e||p.body.reverse())),b=n,f=e};this.sort=function(e,t){O(e,t),this.render()},g=this,w=t,c=e,w&&(void 0!==w.body&&(o=w.body),void 0!==w.bodyClasses&&(r=w.bodyClasses),void 0!==w.bodyEventHandlers&&(l=w.bodyEventHandlers),void 0!==w.dataIsHTML&&(i=w.dataIsHTML),void 0!==w.downIcon&&(a.down=w.downIcon),void 0!==w.headers&&(n=w.headers),void 0!==w.headerClasses&&(s=w.headerClasses),void 0!==w.headerEventHandlers&&(d=w.headerEventHandlers),void 0!==w.sortable&&(h=w.sortable),void 0!==w.searchQuery&&(u=w.searchQuery),void 0!==w.sortAscending&&(f=w.sortAscending),void 0!==w.sortIndex&&(b=w.sortIndex),void 0!==w.theme&&(y=w.theme),void 0!==w.unsortable&&(v=w.unsortable),void 0!==w.upIcon&&(a.up=w.upIcon),void 0!==w.updownIcon&&(a.updown=w.updownIcon)),j(),g.render()};Object.defineProperty(DataTable,"version",{get:function(){return"1.0.0"}}),"function"==typeof define&&define.amd?define(function(){return root.DataTable=DataTable,root.DataTable}):"object"==typeof exports&&"undefined"!=typeof module?module.exports=DataTable:this.DataTable=DataTable;