diff options
Diffstat (limited to 'fe/src/lib/Table.svelte')
-rw-r--r-- | fe/src/lib/Table.svelte | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/fe/src/lib/Table.svelte b/fe/src/lib/Table.svelte new file mode 100644 index 0000000..621157e --- /dev/null +++ b/fe/src/lib/Table.svelte | |||
@@ -0,0 +1,114 @@ | |||
1 | <script lang="ts"> | ||
2 | export let data: Array<any> | undefined = undefined; | ||
3 | export let nofooter: boolean = false; | ||
4 | export let noheader: boolean = false; | ||
5 | export let omit: string[] = ["id"]; | ||
6 | export let title: string | undefined = undefined; | ||
7 | |||
8 | export let sortBy: string = 'date'; | ||
9 | |||
10 | type SortComparator = (a, b) => number | ||
11 | |||
12 | function getDataKeys(data: any[]): string[] { | ||
13 | if (!data || data.length === 0) return []; | ||
14 | return Object.keys(data[0]) | ||
15 | .map((k) => k.split("_").join(" ")) | ||
16 | .filter((k) => !omit.includes(k)); | ||
17 | } | ||
18 | |||
19 | function getRow(row: Record<string, any>): Array<any> { | ||
20 | return Object.entries(row).filter((r) => !omit.includes(r[0])); | ||
21 | } | ||
22 | |||
23 | function sort(arr: Array<Record<string, any>>, fn: SortComparator = (a , b) => new Date(b[sortBy]) - new Date(a[sortBy])) { | ||
24 | return arr.sort(fn) | ||
25 | } | ||
26 | |||
27 | const formatter = new Intl.DateTimeFormat("en", { | ||
28 | year: "numeric", | ||
29 | month: "numeric", | ||
30 | day: "numeric", | ||
31 | hour: "numeric", | ||
32 | minute: "2-digit", | ||
33 | second: "2-digit", | ||
34 | timeZone: "America/New_York" | ||
35 | }); | ||
36 | |||
37 | function formatDatum([key, value]: any[]) { | ||
38 | if (key === "date") { | ||
39 | const parsedDate = new Date(value); | ||
40 | return formatter.format(parsedDate); | ||
41 | } | ||
42 | |||
43 | if (key === "user") { | ||
44 | return value["name"]; | ||
45 | } | ||
46 | |||
47 | return value; | ||
48 | } | ||
49 | </script> | ||
50 | |||
51 | <table> | ||
52 | {#if title} | ||
53 | <h2>{title}</h2> | ||
54 | {/if} | ||
55 | {#if !noheader && data} | ||
56 | <thead> | ||
57 | <tr> | ||
58 | {#each getDataKeys(data) as header} | ||
59 | <th>{header}</th> | ||
60 | {/each} | ||
61 | </tr> | ||
62 | </thead> | ||
63 | {/if} | ||
64 | <tbody> | ||
65 | {#if data} | ||
66 | {#each sort(data) as row} | ||
67 | <tr> | ||
68 | {#each getRow(row) as datum} | ||
69 | <td>{formatDatum(datum)}</td> | ||
70 | {/each} | ||
71 | </tr> | ||
72 | {/each} | ||
73 | {:else} | ||
74 | <tr> There is not data.</tr> | ||
75 | {/if} | ||
76 | </tbody> | ||
77 | {#if !nofooter} | ||
78 | <slot name="footer"> | ||
79 | <tfoot> | ||
80 | <tr> | ||
81 | <td>Table Footer</td> | ||
82 | </tr> | ||
83 | </tfoot> | ||
84 | </slot> | ||
85 | {/if} | ||
86 | </table> | ||
87 | |||
88 | <style> | ||
89 | table { | ||
90 | padding: 16px; | ||
91 | margin: 8px; | ||
92 | border: solid 1px black; | ||
93 | border-collapse: collapse; | ||
94 | overflow-y: hidden; | ||
95 | } | ||
96 | |||
97 | th { | ||
98 | text-transform: capitalize; | ||
99 | } | ||
100 | |||
101 | thead tr { | ||
102 | background: rgba(0, 0, 23, 0.34); | ||
103 | } | ||
104 | |||
105 | tbody tr:nth-child(odd) { | ||
106 | background: rgba(0, 0, 23, 0.14); | ||
107 | } | ||
108 | |||
109 | th, | ||
110 | td { | ||
111 | padding: 1em; | ||
112 | border: 1px solid rgba(0, 0, 0, 1); | ||
113 | } | ||
114 | </style> | ||