Improve table display in Spinner.

This commit is contained in:
Greyson Parrelli 2023-08-13 23:24:40 -04:00 committed by Cody Henthorne
parent d6adfea9b1
commit df96b05863
8 changed files with 1061 additions and 15 deletions

View file

@ -38,7 +38,7 @@
</form> </form>
<!-- Data Rows --> <!-- Data Rows -->
<table> <table id="data-table" class="invisible">
<tr> <tr>
{{#each queryResult.columns}} {{#each queryResult.columns}}
<th>{{this}}</th> <th>{{this}}</th>
@ -55,6 +55,7 @@
{{else}} {{else}}
<div>Select a table from above and click 'browse'.</div> <div>Select a table from above and click 'browse'.</div>
{{/if}} {{/if}}
<div id="handson-table"></div>
<br /> <br />
@ -70,6 +71,44 @@
<input type="submit" name="action" value="last" {{#if pagingData.lastPage}}disabled{{/if}} /> <input type="submit" name="action" value="last" {{#if pagingData.lastPage}}disabled{{/if}} />
</form> </form>
<script src="/js/lib/handsontable.full.min.js"></script>
{{> partials/suffix}} {{> partials/suffix}}
<script>
function main() {
const table = document.getElementById('data-table')
if (!table) {
return;
}
const data = htmlToHandsonData(table)
table.remove()
const renderers = []
for (const header of data.headers) {
renderers.push({ renderer: 'nullRenderer' })
}
const hot = new Handsontable(document.getElementById('handson-table'), {
data: data.rows,
colHeaders: data.headers,
columns: renderers,
readOnly: true,
filters: true,
columnSorting: true,
manualColumnResize: true,
manualColumnMove: true,
manualRowResize: true,
dropdownMenu: ['filter_by_condition', 'filter_by_value', 'filter_action_bar'],
viewportColumnRenderingOffset: 1000,
licenseKey: 'non-commercial-and-evaluation'
})
}
main()
</script>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

View file

@ -12,18 +12,12 @@ select, input {
} }
table, th, td { table, th, td {
border: 1px solid black; font-family: 'JetBrains Mono', monospace;
font-size: 1rem; font-size: 1rem;
} }
th, td { .handsontable td.htDimmed {
padding: 8px; color: #000;
}
th {
position: sticky;
top: 0;
background: #fff;
} }
.query-input { .query-input {
@ -84,6 +78,10 @@ h2.collapse-header, h2.collapse-header+div {
display: none; display: none;
} }
.invisible {
opacity: 0;
}
table.device-info { table.device-info {
margin-bottom: 16px; margin-bottom: 16px;
} }

File diff suppressed because one or more lines are too long

View file

@ -13,6 +13,45 @@ function init() {
document.querySelector('#database-selector').onchange = (e) => { document.querySelector('#database-selector').onchange = (e) => {
window.location.href = window.location.href.split('?')[0] + '?db=' + e.target.value; window.location.href = window.location.href.split('?')[0] + '?db=' + e.target.value;
} }
if (Handsontable) {
Handsontable.renderers.registerRenderer('nullRenderer', nullRenderer)
}
}
function htmlToHandsonData(table) {
const headers = []
const rows = []
for (const row of table.querySelectorAll('tr')) {
for (const th of row.querySelectorAll('th')) {
headers.push(th.innerText)
}
const cells = []
for (const td of row.querySelectorAll('td')) {
cells.push(td.innerText)
}
if (cells.length > 0) {
rows.push(cells)
}
}
return {
headers: headers,
rows: rows
}
}
function nullRenderer(hot, td, row, column, props, value, cellProperties) {
if (value === 'null') {
td.innerHTML = `<em class="null">null</em>`
} else {
td.innerHTML = value
}
} }
init(); init();

View file

@ -8,7 +8,9 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
<!-- handsontable -->
<link href="/css/lib/handsontable.full.min.css" rel="stylesheet">
<!-- Us -->
<link href="/css/main.css" rel="stylesheet"> <link href="/css/main.css" rel="stylesheet">
<style type="text/css">
</style>
</head> </head>

View file

@ -29,7 +29,7 @@
{{queryResult.timeToFirstRow}} ms to read the first row. <br /> {{queryResult.timeToFirstRow}} ms to read the first row. <br />
{{queryResult.timeToReadRows}} ms to read the rest of the rows. <br /> {{queryResult.timeToReadRows}} ms to read the rest of the rows. <br />
<br /> <br />
<table> <table id="data-table" class="invisible">
<tr> <tr>
{{#each queryResult.columns}} {{#each queryResult.columns}}
<th>{{this}}</th> <th>{{this}}</th>
@ -43,10 +43,14 @@
</tr> </tr>
{{/each}} {{/each}}
</table> </table>
<div id="handson-table"></div>
{{else}} {{else}}
No data. No data.
{{/if}} {{/if}}
<script src="/js/lib/handsontable.full.min.js"></script>
{{> partials/suffix}} {{> partials/suffix}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql-formatter/4.0.2/sql-formatter.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/sql-formatter/4.0.2/sql-formatter.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
@ -87,6 +91,34 @@
bindKey: {win: "Ctrl-Shift-F", mac: "Command-Shift-F"}, bindKey: {win: "Ctrl-Shift-F", mac: "Command-Shift-F"},
exec: formatSql exec: formatSql
}); });
const table = document.getElementById('data-table')
if (!table) {
return;
}
const data = htmlToHandsonData(table)
table.remove()
const renderers = []
for (const header of data.headers) {
renderers.push({ renderer: 'nullRenderer' })
}
const hot = new Handsontable(document.getElementById('handson-table'), {
data: data.rows,
colHeaders: data.headers,
columns: renderers,
readOnly: true,
filters: true,
columnSorting: true,
manualColumnResize: true,
manualColumnMove: true,
manualRowResize: true,
dropdownMenu: ['filter_by_condition', 'filter_by_value', 'filter_action_bar'],
viewportColumnRenderingOffset: 1000,
licenseKey: 'non-commercial-and-evaluation'
})
} }
function onFormatClicked(e) { function onFormatClicked(e) {

View file

@ -61,8 +61,8 @@ internal class SpinnerServer(
try { try {
return when { return when {
session.method == Method.GET && session.uri == "/css/main.css" -> newFileResponse("css/main.css", "text/css") session.method == Method.GET && session.uri.startsWith("/css/") -> newFileResponse(session.uri.substring(1), "text/css")
session.method == Method.GET && session.uri == "/js/main.js" -> newFileResponse("js/main.js", "text/javascript") session.method == Method.GET && session.uri.startsWith("/js/") -> newFileResponse(session.uri.substring(1), "text/javascript")
session.method == Method.GET && session.uri == "/" -> getIndex(dbParam, dbConfig.db()) session.method == Method.GET && session.uri == "/" -> getIndex(dbParam, dbConfig.db())
session.method == Method.GET && session.uri == "/browse" -> getBrowse(dbParam, dbConfig.db()) session.method == Method.GET && session.uri == "/browse" -> getBrowse(dbParam, dbConfig.db())
session.method == Method.POST && session.uri == "/browse" -> postBrowse(dbParam, dbConfig, session) session.method == Method.POST && session.uri == "/browse" -> postBrowse(dbParam, dbConfig, session)