This is the code that runs https://bandolier.learnjsthehardway.com/ for you to review. It uses the https://git.learnjsthehardway.com/learn-javascript-the-hard-way/bandolier-template to create the documentation for the project.
https://bandolier.learnjsthehardway.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
4.2 KiB
179 lines
4.2 KiB
2 years ago
|
<script>
|
||
|
import { onMount, createEventDispatcher } from 'svelte';
|
||
|
import Icon from "./Icon.svelte";
|
||
|
import Pagination from "./Pagination.svelte";
|
||
|
|
||
|
export let rows = [];
|
||
|
export let columns = [];
|
||
|
export let pagination = { currentPage: 1, lastPage: 1 }
|
||
|
let search_text = "";
|
||
|
let sort_order = 1;
|
||
|
let sort_column = "";
|
||
|
|
||
|
let dispatch = createEventDispatcher();
|
||
|
|
||
|
const clear_search = async () => {
|
||
|
search_text = "";
|
||
|
dispatch("clear_search");
|
||
|
}
|
||
|
|
||
|
const search_query = async () => {
|
||
|
dispatch("search_query", search_text);
|
||
|
}
|
||
|
|
||
|
const handle_keypress = (event) => {
|
||
|
if(event.key === "Enter") {
|
||
|
dispatch("search_query", search_text);
|
||
|
} else if(event.key === "Escape") {
|
||
|
dispatch("clear_search");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const cell_click = (row, column, name) => {
|
||
|
dispatch("cell_click", {row, column, name});
|
||
|
}
|
||
|
|
||
|
const sort_by = (name) => {
|
||
|
if(name == sort_column) {
|
||
|
// clicking the same column so just invert the sort order
|
||
|
sort_order *= -1;
|
||
|
} else {
|
||
|
// new column, reset sort order and name
|
||
|
sort_order = 1;
|
||
|
sort_column = name;
|
||
|
}
|
||
|
|
||
|
rows = rows.sort((a, b) => {
|
||
|
let cola = a[sort_column];
|
||
|
let colb = b[sort_column];
|
||
|
|
||
|
if(cola < colb) {
|
||
|
return sort_order * -1;
|
||
|
} else if(cola > colb) {
|
||
|
return Number(sort_order);
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
const page_changed = (change) => {
|
||
|
dispatch("full_query", change);
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<style>
|
||
|
buttons {
|
||
|
display: flex;
|
||
|
flex-direction: row;
|
||
|
flex-wrap: none;
|
||
|
justify-content: space-evenly;
|
||
|
width: 100%;
|
||
|
}
|
||
|
|
||
|
buttons left,
|
||
|
buttons right {
|
||
|
display: flex;
|
||
|
justify-content: space-evenly;
|
||
|
align-items: center;
|
||
|
width: 100%;
|
||
|
}
|
||
|
|
||
|
buttons left paging {
|
||
|
flex-grow: 2;
|
||
|
flex-direction: row-reverse;
|
||
|
}
|
||
|
|
||
|
table {
|
||
|
width: 100%;
|
||
|
}
|
||
|
|
||
|
tr,
|
||
|
td {
|
||
|
width: min-content;
|
||
|
}
|
||
|
|
||
|
td {
|
||
|
outline: 1px solid var(--color-border);
|
||
|
text-align: left;
|
||
|
}
|
||
|
|
||
|
td:hover {
|
||
|
outline: 1px solid var(--color-bg-inverted);
|
||
|
}
|
||
|
|
||
|
tr:nth-child(even) {
|
||
|
background-color: var(--color-bg-tertiary);
|
||
|
}
|
||
|
|
||
|
data-table {
|
||
|
overflow: auto;
|
||
|
max-width: 98vw;
|
||
|
min-width: 98vw;
|
||
|
width: 98vw;
|
||
|
}
|
||
|
</style>
|
||
|
|
||
|
<svelte:window on:keypress={ handle_keypress }/>
|
||
|
|
||
|
<buttons>
|
||
|
<left>
|
||
|
<slot></slot>
|
||
|
|
||
|
<paging>
|
||
|
<Pagination bind:pagination on:change={ page_changed } />
|
||
|
</paging>
|
||
|
</left>
|
||
|
<right>
|
||
|
<input id="search" bind:value={ search_text} name="search" placeholder="Search ..." type="text" />
|
||
|
|
||
|
|
||
|
<span on:click={ search_query } ><Icon name="search" tooltip="Search." size="36" /></span>
|
||
|
<span on:click={ clear_search }><Icon name="x-circle" tip_position="bottom-left" tooltip="Clear search." size="36" /></span>
|
||
|
</right>
|
||
|
</buttons>
|
||
|
|
||
|
<data-table>
|
||
|
{#if rows.length === 0}
|
||
|
<h1 data-testid="nothing-found">Nothing Found</h1>
|
||
|
<p>The table returned no results for your search text <em>{ search_text }</em>. You can use ESC to clear the search.</p>
|
||
|
{:else}
|
||
|
<table>
|
||
|
<thead>
|
||
|
<tr>
|
||
|
{#each columns as name}
|
||
|
{#if name !== "_url"}
|
||
|
<th on:click={ () => sort_by(name) }>{ name }
|
||
|
{#if sort_column === name && sort_order === 1}
|
||
|
<Icon name="arrow-down" size="18" color="var(--color-text-inverted)" />
|
||
|
{:else if sort_column === name && sort_order === -1}
|
||
|
<Icon name="arrow-up" size="18" color="var(--color-text-inverted)" />
|
||
|
{/if}
|
||
|
</th>
|
||
|
{/if}
|
||
|
{/each}
|
||
|
</tr>
|
||
|
</thead>
|
||
|
<tbody>
|
||
|
{#each rows as row, i}
|
||
|
<tr>
|
||
|
{#each columns as name, j}
|
||
|
{#if name === "id"}
|
||
|
<td on:click={ () => cell_click(i, j, name) }>
|
||
|
{#if row._url}
|
||
|
<a data-testid="row-{i}" href={ row._url }>{ row[name] }</a>
|
||
|
{:else}
|
||
|
{ row[name] }
|
||
|
{/if}
|
||
|
</td>
|
||
|
{:else if name !== "_url"}
|
||
|
<td on:click={ () => cell_click(i, j, name) }>{ row[name] }</td>
|
||
|
{/if}
|
||
|
{/each}
|
||
|
</tr>
|
||
|
{/each}
|
||
|
</tbody>
|
||
|
</table>
|
||
|
{/if}
|
||
|
</data-table>
|