Skip to content

Commit f116376

Browse files
committed
style
1 parent b65127e commit f116376

File tree

1 file changed

+151
-141
lines changed

1 file changed

+151
-141
lines changed
Lines changed: 151 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts">
22
import { createVirtualizer } from "@tanstack/svelte-virtual";
3-
import { onDestroy, onMount } from "svelte";
3+
import { onDestroy } from "svelte";
44
import { derived } from "svelte/store";
55
66
import type { Entry } from "../entries";
@@ -22,86 +22,78 @@
2222
2323
const { entries, showChangeAndBalance = false }: Props = $props();
2424
25-
let sortedEntries = $state.raw<Entry[]>([]);
26-
let journalShow = derived(journalShowStore, (t) => new Set(t));
27-
28-
let head: HTMLLIElement;
29-
30-
const filter = derived(
31-
[journalSortOrder, journalShow],
32-
([$order, $show]) =>
33-
[$order, $show] as [JournalSortOrder, Set<JournalShowEntry>],
34-
);
35-
36-
let unsub: () => void;
37-
onMount(() => {
38-
unsub = filter.subscribe(([journalSortOrder, journalShow]) => {
39-
let column: SortColumn<Entry>;
40-
switch (journalSortOrder[0]) {
41-
case "flag":
42-
column = new StringColumn("flag", (e) => e.sortFlag);
43-
break;
44-
case "narration":
45-
column = new StringColumn("narration", (e) => e.sortNarration);
46-
break;
47-
default:
48-
column = new DateColumn("date");
49-
break;
25+
let head = $state<HTMLLIElement>();
26+
$effect(() => {
27+
const order = $journalSortOrder;
28+
head?.querySelectorAll<HTMLSpanElement>("span[data-sort]").forEach((el) => {
29+
el.removeAttribute("data-order");
30+
if (el.getAttribute("data-sort-name") === order[0]) {
31+
el.setAttribute("data-order", order[1] ?? "asc");
5032
}
51-
const sorter = new Sorter(column, journalSortOrder[1] ?? "asc");
52-
53-
const headers = head.querySelectorAll<HTMLSpanElement>("span[data-sort]");
54-
headers.forEach((el) => {
55-
el.removeAttribute("data-order");
56-
if (el.getAttribute("data-sort-name") === column.name) {
57-
el.setAttribute("data-order", sorter.order);
58-
}
59-
});
33+
});
34+
});
6035
61-
// TODO: remove logging
62-
console.time("filter");
63-
const filtered = entries.filter((e) => {
64-
if (journalShow.has(e.t.toLowerCase() as JournalShowEntry)) {
65-
if (e.t === "Transaction") {
66-
let flagOpt: JournalShowEntry;
67-
switch (e.flag) {
68-
case "*":
69-
flagOpt = "cleared";
70-
break;
71-
case "!":
72-
flagOpt = "pending";
73-
break;
74-
default:
75-
flagOpt = "other";
76-
}
77-
return journalShow.has(flagOpt);
78-
} else if (e.t === "Document") {
79-
if (e.tags) {
80-
if (e.tags.includes("discovered")) {
81-
return journalShow.has("discovered");
82-
}
83-
if (e.tags.includes("linked")) {
84-
return journalShow.has("linked");
85-
}
36+
let sortedEntries = $state.raw<Entry[]>([]);
37+
const journalShow = derived(journalShowStore, (t) => new Set(t));
38+
const filter = derived([journalSortOrder, journalShow], (it) => it);
39+
const unsub = filter.subscribe(([journalSortOrder, journalShow]) => {
40+
let column: SortColumn<Entry>;
41+
switch (journalSortOrder[0]) {
42+
case "flag":
43+
column = new StringColumn("flag", (e) => e.sortFlag);
44+
break;
45+
case "narration":
46+
column = new StringColumn("narration", (e) => e.sortNarration);
47+
break;
48+
default:
49+
column = new DateColumn("date");
50+
break;
51+
}
52+
const sorter = new Sorter(column, journalSortOrder[1] ?? "asc");
53+
54+
// TODO: remove logging
55+
console.time("filter");
56+
const filtered = entries.filter((e) => {
57+
if (journalShow.has(e.t.toLowerCase() as JournalShowEntry)) {
58+
if (e.t === "Transaction") {
59+
let flagOpt: JournalShowEntry;
60+
switch (e.flag) {
61+
case "*":
62+
flagOpt = "cleared";
63+
break;
64+
case "!":
65+
flagOpt = "pending";
66+
break;
67+
default:
68+
flagOpt = "other";
69+
}
70+
return journalShow.has(flagOpt);
71+
} else if (e.t === "Document") {
72+
if (e.tags) {
73+
if (e.tags.includes("discovered")) {
74+
return journalShow.has("discovered");
8675
}
87-
} else if (e.t === "Custom") {
88-
if (e.type === "budget") {
89-
return journalShow.has("budget");
76+
if (e.tags.includes("linked")) {
77+
return journalShow.has("linked");
9078
}
9179
}
92-
return true;
80+
} else if (e.t === "Custom") {
81+
if (e.type === "budget") {
82+
return journalShow.has("budget");
83+
}
9384
}
94-
return false;
95-
});
96-
console.timeEnd("filter");
85+
return true;
86+
}
87+
return false;
88+
});
89+
console.timeEnd("filter");
9790
98-
// TODO: remove logging
99-
console.time("sort");
100-
const sorted = sorter.sort(filtered);
101-
console.timeEnd("sort");
91+
// TODO: remove logging
92+
console.time("sort");
93+
const sorted = sorter.sort(filtered);
94+
console.timeEnd("sort");
10295
103-
sortedEntries = sorted as Entry[];
104-
});
96+
sortedEntries = sorted as Entry[];
10597
});
10698
10799
onDestroy(() => {
@@ -115,8 +107,8 @@
115107
$journalSortOrder = [name as JournalSortOrder[0], order];
116108
}
117109
118-
let ol = $state<HTMLDivElement>();
119-
let lis = $state<HTMLLIElement[]>([]);
110+
let vlistOuter = $state<HTMLDivElement>();
111+
let vlistItems = $state<HTMLLIElement[]>([]);
120112
121113
function depend<T, R>(t: T, fn: (t: T) => R) {
122114
return fn(t);
@@ -132,106 +124,120 @@
132124
let virtualizer = $derived(
133125
createVirtualizer({
134126
overscan: 5,
135-
count: sortedEntries.length,
127+
count: sortedEntries.length + 1,
136128
getItemKey: depend(
137129
sortedEntries,
138130
(sortedEntries) => (i) => sortedEntries[i]?.entry_hash ?? i,
139131
),
140-
getScrollElement: depend(ol, (ol) => () => ol ?? null),
132+
getScrollElement: depend(vlistOuter, (ol) => () => ol ?? null),
141133
estimateSize: () => 50,
142134
}),
143135
);
144136
145137
let items = $derived($virtualizer.getVirtualItems());
146138
147139
$effect(() => {
148-
if (lis.length) {
149-
lis.forEach((li) => {
140+
if (vlistItems.length) {
141+
vlistItems.forEach((li) => {
150142
$virtualizer.measureElement(li);
151143
});
152144
}
153145
});
154146
</script>
155147

156-
<JournalFilters />
157-
158-
<ol class="flex-table journal">
159-
<li class="head" bind:this={head}>
160-
<p>
161-
<!-- TODO: ARIA tags -->
162-
<span
163-
class="datecell"
164-
data-sort="date"
165-
data-sort-name="date"
166-
onclick={headerClick}
167-
aria-hidden="true"
168-
>
169-
{_("Date")}
170-
</span>
171-
<span
172-
class="flag"
173-
data-sort="string"
174-
data-sort-name="flag"
175-
onclick={headerClick}
176-
aria-hidden="true"
177-
>
178-
{_("F")}
179-
</span>
180-
<span
181-
class="description"
182-
data-sort="string"
183-
data-sort-name="narration"
184-
onclick={headerClick}
185-
aria-hidden="true"
186-
>
187-
{_("Payee")}/{_("Narration")}
188-
</span>
189-
<span class="num">{_("Units")}</span>
190-
<span class="cost num">
191-
{_("Cost")}
192-
{#if showChangeAndBalance}
193-
/ {_("Change")}
194-
{/if}
195-
</span>
196-
<span class="num">
197-
{_("Price")}
198-
{#if showChangeAndBalance}
199-
/ {_("Balance")}
200-
{/if}
201-
</span>
202-
</p>
203-
</li>
204-
205-
<div bind:this={ol} class="vlist-outer">
206-
<div class="vlist-inner" style="height: {$virtualizer.getTotalSize()}px;">
207-
<div
148+
<div class="fixed-fullsize-container">
149+
<div bind:this={vlistOuter} class="vlist-outer">
150+
<div
151+
class="flex-table journal vlist-inner"
152+
style="height: {$virtualizer.getTotalSize()}px;"
153+
>
154+
<ol
208155
class="vlist-items"
209156
style="transform: translateY({items[0]?.start ?? 0}px);"
210157
>
211158
{#each items as row (row.index)}
212-
<JournalEntry
213-
index={row.index}
214-
e={getSortedEntry(row.index)}
215-
{showChangeAndBalance}
216-
journalShow={$journalShow}
217-
bind:li={lis[row.index]}
218-
/>
159+
{#if row.index === 0}
160+
<li class="head" bind:this={head}>
161+
<div class="filter-container">
162+
<JournalFilters />
163+
</div>
164+
<p>
165+
<!-- TODO: ARIA tags -->
166+
<span
167+
class="datecell"
168+
data-sort="date"
169+
data-sort-name="date"
170+
onclick={headerClick}
171+
aria-hidden="true"
172+
>
173+
{_("Date")}
174+
</span>
175+
<span
176+
class="flag"
177+
data-sort="string"
178+
data-sort-name="flag"
179+
onclick={headerClick}
180+
aria-hidden="true"
181+
>
182+
{_("F")}
183+
</span>
184+
<span
185+
class="description"
186+
data-sort="string"
187+
data-sort-name="narration"
188+
onclick={headerClick}
189+
aria-hidden="true"
190+
>
191+
{_("Payee")}/{_("Narration")}
192+
</span>
193+
<span class="num">{_("Units")}</span>
194+
<span class="cost num">
195+
{_("Cost")}
196+
{#if showChangeAndBalance}
197+
/ {_("Change")}
198+
{/if}
199+
</span>
200+
<span class="num">
201+
{_("Price")}
202+
{#if showChangeAndBalance}
203+
/ {_("Balance")}
204+
{/if}
205+
</span>
206+
</p>
207+
</li>
208+
{:else}
209+
<JournalEntry
210+
index={row.index}
211+
e={getSortedEntry(row.index - 1)}
212+
{showChangeAndBalance}
213+
journalShow={$journalShow}
214+
bind:li={vlistItems[row.index]}
215+
/>
216+
{/if}
219217
{/each}
220-
</div>
218+
</ol>
221219
</div>
222220
</div>
223-
</ol>
221+
</div>
224222

225223
<style>
224+
.fixed-fullsize-container {
225+
position: absolute;
226+
top: 0;
227+
left: 0;
228+
}
229+
226230
.vlist-outer {
227-
height: 1000px;
231+
height: 100%;
232+
padding: 1.5em;
228233
contain: strict;
229234
overflow-y: auto;
230235
}
231236
232237
.vlist-inner {
233238
position: relative;
234239
width: 100%;
240+
margin: 0;
235241
}
236242
237243
.vlist-items {
@@ -240,4 +246,8 @@
240246
left: 0;
241247
width: 100%;
242248
}
249+
250+
.filter-container {
251+
margin-bottom: 0.25rem;
252+
}
243253
</style>

0 commit comments

Comments
 (0)