From c8fa4e87412cb79ecffb549cff841e8f38c9963a Mon Sep 17 00:00:00 2001 From: Daniel Munday Date: Thu, 20 Dec 2018 11:28:25 +0200 Subject: [PATCH 1/2] Added navigation, search and filter functionality, load screen and minor aesthetic modifications. --- app.css | 77 +++++++++++++++++++++++++++ app.ts | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 21 +++++--- 3 files changed, 243 insertions(+), 8 deletions(-) create mode 100644 app.css create mode 100644 app.ts diff --git a/app.css b/app.css new file mode 100644 index 00000000..e7b8f23f --- /dev/null +++ b/app.css @@ -0,0 +1,77 @@ +body{ + overflow: hidden; + font-family: Verdana, Geneva, sans-serif; + text-align: center; + margin: 4px; +} +table.gridtable{ + width: 100%; + font-size:12px; + color:#333333; + border-color: #D7D7D7; + border-collapse: collapse; +} +table.gridtable th { + border-width: 0.5px; + height: 32px; + border-style: solid; + border-color: #D7D7D7; + background-color: #D3EFFF; +} +table.gridtable td { + border-width: 0.5px; + height: 32px; + border-style: solid; + border-color: #D7D7D7; + background-color: #ffffff; +} +table.gridtable td:hover{ + background-color: #f1f1f1; +} +.footer{ + height: 48px; + left: 50%; + transform: translateX(-50%); + position: absolute; + bottom: 4px; + user-select: none; +} +a{ + color: black; + padding: 8px; + text-decoration: none; + transition: background-color .3s; +} +/* Add a grey background color on mouse-over */ +a:hover:not(.active) {background-color: #D8E9F2;} +/* search field */ +#search{ + display: inline-block; + width: 84px; + font-size: 12px; + padding: 8px; + border: 1px solid #ddd; +} +.loader{ + position: absolute; + left: 46%; + top: 40%; + border: 16px solid #f3f3f3; + border-radius: 50%; + border-top: 16px solid #3498db; + width: 120px; + height: 120px; + -webkit-animation: spin 2s linear infinite; /* Safari */ + animation: spin 2s linear infinite; +} + +/* Safari */ +@-webkit-keyframes spin { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } \ No newline at end of file diff --git a/app.ts b/app.ts new file mode 100644 index 00000000..fba6b958 --- /dev/null +++ b/app.ts @@ -0,0 +1,153 @@ +let firstIndex:number, lastIndex:number; // stores the indices of the first and last rows in the table +let recordCount:number; // stores the number of records in the database +let resizeTimer; // stores time since last window resize + +// builds the grid table from the column headers and row data +function buildTable(columns, records) { + + // create table + let table = document.createElement("table"); + table.className = "gridtable"; + let thead = document.createElement("thead"); + let tbody = document.createElement("tbody"); + let headRow = document.createElement("tr"); + + // create column headers + for(const header of columns) { + var th = document.createElement("th"); + th.appendChild(document.createTextNode(header)); + headRow.appendChild(th); + }; + thead.appendChild(headRow); + table.appendChild(thead); + + // create rows + records.forEach(function (el) { + let tr = document.createElement("tr"); + for (var o in el) { + let td = document.createElement("td"); + td.appendChild(document.createTextNode(el[o])) + tr.appendChild(td); + } + tbody.appendChild(tr); + }); + table.appendChild(tbody); + + // builds footer elements (buttons, search field) + document.getElementById("footer").innerHTML = `« + » + `; + + document.getElementById("loader").remove(); + + return table; +} + +// filters rows by user input ID +function search(e){ + let input = $("#search").val().toString(); + let $table = $("tbody tr").toArray(); + + for(const record of $table){ + let td = record.getElementsByTagName("td")[0]; + record.style.display="block"; + if(td){ + let val = td.innerText || td.textContent; + if(val.indexOf(input) > -1){ + record.style.display = ""; + } + else{ + record.style.display = "none"; + } + } + } + + // if record doesn't exist on current page, query database and display results + if(e.which == 13) + { + let end = parseInt(input) + calculateRows(); + clearTable(); + loadPage(parseInt(input), end); + } +} + +// load the previous page +function goPrevious(){ + let numRows = calculateRows(); + if(lastIndex > numRows){ + clearTable(); + loadPage(firstIndex-numRows, firstIndex-1); + } +} + +// load the next page +function goNext(){ + if(lastIndex < recordCount-1) + { + clearTable(); + loadPage(lastIndex+1, lastIndex+calculateRows()); + } +} + +// clears the table and footer elements +function clearTable(){ + document.getElementById("content").innerHTML=""; + document.getElementById("footer").innerHTML=""; +} + +// loads the columns and rows to be displayed based on start and end row indices +function loadPage(start:number, end:number){ + firstIndex = start; + lastIndex = end; + + // check bounds + if(firstIndex<0) firstIndex = 0; + if(lastIndex>=recordCount) lastIndex = recordCount-1; + if(firstIndex>=recordCount){ + firstIndex = recordCount - calculateRows(); + } + + // create loader + let loader = document.createElement("loader"); + document.getElementById("content").appendChild(loader); + loader.innerHTML=`
`; + + // outter function to fetch column headers from server + $.get("http://localhost:2050/columns", function(columns){ + + // inner function to fetch rows from server and invoke table build function + $.get("http://localhost:2050/records?from="+firstIndex+"&to="+lastIndex, function(rows){ + document.getElementById("content").appendChild(buildTable(JSON.parse(columns), JSON.parse(rows))); + }); + }); +} + +// determines the number of rows to display based on the window height +function calculateRows(){ + let x = (window.innerHeight - document.getElementById("footer").offsetHeight - document.getElementById("tableHeading").offsetHeight - 64)/36; + return Math.floor(x)-1; +} + +window.onload = function () { + // hide browser scroll bar + document.body.style.overflow = "hidden"; + + // get record count + $.get("http://localhost:2050/recordCount", function(data){ + + // load the first page + loadPage(0, calculateRows()); + recordCount = JSON.parse(data); + }); + +} + +// updates the table display when the window is resized +// debounce function to reduce frequency of queries made +window.onresize = () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(function(){ + clearTable(); + loadPage(firstIndex, firstIndex + calculateRows()); + }, 250); +} \ No newline at end of file diff --git a/index.html b/index.html index add5e736..355f7315 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,18 @@ - - + + + + - JS Onboard Project - + + TEST + + + - -

Hello

+

Table Heading

+
+ - - + \ No newline at end of file From 0ba4c5d974ca9276f225164a2d0639402c5595fa Mon Sep 17 00:00:00 2001 From: Daniel Munday Date: Fri, 21 Dec 2018 08:49:01 +0200 Subject: [PATCH 2/2] Code reformatted. Code complexity reduced. Support for loader animation with Safari removed. --- app.css | 18 ++------ app.ts | 137 +++++++++++++++++++++++++------------------------------- 2 files changed, 65 insertions(+), 90 deletions(-) diff --git a/app.css b/app.css index e7b8f23f..c029dcfe 100644 --- a/app.css +++ b/app.css @@ -54,24 +54,12 @@ a:hover:not(.active) {background-color: #D8E9F2;} } .loader{ position: absolute; - left: 46%; + left: 46.5%; top: 40%; border: 16px solid #f3f3f3; border-radius: 50%; border-top: 16px solid #3498db; width: 120px; - height: 120px; - -webkit-animation: spin 2s linear infinite; /* Safari */ - animation: spin 2s linear infinite; + height: 120px; + animation: spin 2s linear infinite; } - -/* Safari */ -@-webkit-keyframes spin { - 0% { -webkit-transform: rotate(0deg); } - 100% { -webkit-transform: rotate(360deg); } - } - - @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } - } \ No newline at end of file diff --git a/app.ts b/app.ts index fba6b958..57896997 100644 --- a/app.ts +++ b/app.ts @@ -1,36 +1,37 @@ -let firstIndex:number, lastIndex:number; // stores the indices of the first and last rows in the table -let recordCount:number; // stores the number of records in the database +let firstIndex: number, lastIndex: number; // stores the indices of the first and last rows in the table +let recordCount: number; // stores the number of records in the database +let table, columns, thead, tbody, headRow; // stores column headers let resizeTimer; // stores time since last window resize // builds the grid table from the column headers and row data -function buildTable(columns, records) { +function buildTable(rows) { - // create table - let table = document.createElement("table"); + // initialise table + table = document.createElement("table"); table.className = "gridtable"; - let thead = document.createElement("thead"); - let tbody = document.createElement("tbody"); - let headRow = document.createElement("tr"); + thead = document.createElement("thead"); + tbody = document.createElement("tbody"); + headRow = document.createElement("tr"); - // create column headers - for(const header of columns) { - var th = document.createElement("th"); + // build column headers + for (const header of columns) { + let th = document.createElement("th"); th.appendChild(document.createTextNode(header)); headRow.appendChild(th); - }; - thead.appendChild(headRow); + }; + thead.appendChild(headRow); table.appendChild(thead); // create rows - records.forEach(function (el) { + for (const el of rows) { let tr = document.createElement("tr"); - for (var o in el) { + for (let o in el) { let td = document.createElement("td"); td.appendChild(document.createTextNode(el[o])) tr.appendChild(td); } tbody.appendChild(tr); - }); + } table.appendChild(tbody); // builds footer elements (buttons, search field) @@ -40,92 +41,75 @@ function buildTable(columns, records) { document.getElementById("loader").remove(); - return table; + return table; } // filters rows by user input ID -function search(e){ +function search(e) { let input = $("#search").val().toString(); let $table = $("tbody tr").toArray(); - for(const record of $table){ + for (const record of $table) { let td = record.getElementsByTagName("td")[0]; - record.style.display="block"; - if(td){ + record.style.display = "block"; + if (td) { let val = td.innerText || td.textContent; - if(val.indexOf(input) > -1){ - record.style.display = ""; - } - else{ - record.style.display = "none"; - } + record.style.display = val.indexOf(input) > -1 ? "" : "none"; } } // if record doesn't exist on current page, query database and display results - if(e.which == 13) - { - let end = parseInt(input) + calculateRows(); - clearTable(); - loadPage(parseInt(input), end); + if (e.which == 13) { + loadPage(parseInt(input)); } } // load the previous page -function goPrevious(){ +function goPrevious() { let numRows = calculateRows(); - if(lastIndex > numRows){ - clearTable(); - loadPage(firstIndex-numRows, firstIndex-1); + if (lastIndex >= numRows) { + loadPage(firstIndex - numRows); } } // load the next page -function goNext(){ - if(lastIndex < recordCount-1) - { - clearTable(); - loadPage(lastIndex+1, lastIndex+calculateRows()); +function goNext() { + if (lastIndex < recordCount - 1) { + loadPage(lastIndex + 1); } } -// clears the table and footer elements -function clearTable(){ - document.getElementById("content").innerHTML=""; - document.getElementById("footer").innerHTML=""; -} - -// loads the columns and rows to be displayed based on start and end row indices -function loadPage(start:number, end:number){ +// loads the columns and rows to be displayed based on index of the first row +function loadPage(start: number) { firstIndex = start; - lastIndex = end; + lastIndex = calculateRows(); + + // clear table + document.getElementById("content").innerHTML = ""; + document.getElementById("footer").innerHTML = ""; // check bounds - if(firstIndex<0) firstIndex = 0; - if(lastIndex>=recordCount) lastIndex = recordCount-1; - if(firstIndex>=recordCount){ + if (firstIndex < 0) firstIndex = 0; + if (lastIndex >= recordCount) lastIndex = recordCount - 1; + if (firstIndex >= recordCount) { firstIndex = recordCount - calculateRows(); - } + } // create loader let loader = document.createElement("loader"); document.getElementById("content").appendChild(loader); - loader.innerHTML=`
`; - - // outter function to fetch column headers from server - $.get("http://localhost:2050/columns", function(columns){ - - // inner function to fetch rows from server and invoke table build function - $.get("http://localhost:2050/records?from="+firstIndex+"&to="+lastIndex, function(rows){ - document.getElementById("content").appendChild(buildTable(JSON.parse(columns), JSON.parse(rows))); - }); + loader.innerHTML = `
`; + + // inner function to fetch rows from server and invoke table build function + $.get("http://localhost:2050/records?from=" + firstIndex + "&to=" + lastIndex, function (rows) { + document.getElementById("content").appendChild(buildTable(JSON.parse(rows))); }); } // determines the number of rows to display based on the window height -function calculateRows(){ - let x = (window.innerHeight - document.getElementById("footer").offsetHeight - document.getElementById("tableHeading").offsetHeight - 64)/36; - return Math.floor(x)-1; +function calculateRows() { + let x = (window.innerHeight - document.getElementById("footer").offsetHeight - document.getElementById("tableHeading").offsetHeight - 64) / 37; + return Math.floor(x); } window.onload = function () { @@ -133,21 +117,24 @@ window.onload = function () { document.body.style.overflow = "hidden"; // get record count - $.get("http://localhost:2050/recordCount", function(data){ + $.get("http://localhost:2050/recordCount", function (numRecords) { + + // get column headers from server + $.get("http://localhost:2050/columns", function (cols) { + columns = JSON.parse(cols); - // load the first page - loadPage(0, calculateRows()); - recordCount = JSON.parse(data); + // load the first page + loadPage(0); + recordCount = JSON.parse(numRecords); + }); }); - } // updates the table display when the window is resized // debounce function to reduce frequency of queries made window.onresize = () => { clearTimeout(resizeTimer); - resizeTimer = setTimeout(function(){ - clearTable(); - loadPage(firstIndex, firstIndex + calculateRows()); + resizeTimer = setTimeout(function () { + loadPage(firstIndex); }, 250); -} \ No newline at end of file +}