Skip to content

Commit dc93d94

Browse files
refactor: simplify table component structure and improve virtualization logic
- Removed unnecessary bindings and conditions in the table body and row components for cleaner rendering. - Updated virtualization utility functions to enhance performance and reduce complexity. - Refactored search worker to streamline data handling and improve state management. - Removed unused cache state logic from sorting utilities to simplify codebase.
1 parent 3b25318 commit dc93d94

11 files changed

Lines changed: 172 additions & 394 deletions

File tree

src/lib/components/table/body.svelte

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,15 @@
1212
rowHeight?: number;
1313
}
1414
15-
let { data, columns, type, padding = { top: 0, bottom: 0 }, rowHeight = $bindable(0) }: Props = $props();
15+
let { data, columns, type, padding = { top: 0, bottom: 0 } }: Props = $props();
1616
</script>
1717

1818
<tbody class="divide-neutral-900">
1919
{#if padding.top > 0}
2020
<tr style:height="{padding.top}px" aria-hidden="true"></tr>
2121
{/if}
22-
{#each data as row, i}
23-
{#if i === 0}
24-
<Row {row} {columns} {type} bind:clientHeight={rowHeight} />
25-
{:else}
26-
<Row {row} {columns} {type} />
27-
{/if}
22+
{#each data as row}
23+
<Row {row} {columns} {type} />
2824
{/each}
2925
{#if padding.bottom > 0}
3026
<tr style:height="{padding.bottom}px" aria-hidden="true"></tr>

src/lib/components/table/row.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
[key: string]: any;
1414
}
1515
16-
let { row, columns, type, clientHeight = $bindable(), ...rest }: Props = $props();
16+
let { row, columns, type, ...rest }: Props = $props();
1717
</script>
1818

19-
<tr class="group hover:bg-yellow-400/5 transition-colors" bind:clientHeight {...rest}>
19+
<tr class="group hover:bg-yellow-400/5 transition-colors" {...rest}>
2020
{#each columns as col}
2121
<StoreItem {row} {col} />
2222
{/each}

src/lib/components/table/table.svelte

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@
2828
const virtualizer = new Virtualizer();
2929
3030
$effect(() => {
31-
virtualizer.update(config, onloadrange);
32-
});
33-
34-
$effect(() => {
35-
virtualizer.checkScroll();
31+
virtualizer.update(config);
3632
});
3733
3834
let lastQuery = "";
@@ -58,8 +54,24 @@
5854
virtualizer.reset();
5955
}
6056
});
57+
58+
$effect(() => {
59+
const { start, end } = virtualizer.targetRange;
60+
const { offset, data, count } = config;
61+
62+
if (count > 0 && data.length >= count) return;
63+
64+
const loadedStart = offset;
65+
const loadedEnd = offset + data.length;
66+
67+
if (start >= loadedStart && end <= loadedEnd) return;
68+
69+
onloadrange(start, end - start);
70+
});
6171
</script>
6272

73+
<svelte:window bind:innerWidth={virtualizer.innerWidth} />
74+
6375
<div class="w-full flex flex-col flex-1 bg-neutral-950 h-full overflow-hidden">
6476
<div class="shrink-0 border-b border-neutral-900 bg-neutral-950">
6577
<Input
@@ -102,7 +114,6 @@
102114
{type}
103115
columns={config.columns}
104116
padding={virtualizer.padding}
105-
bind:rowHeight={virtualizer.rowHeight}
106117
/>
107118
{/if}
108119
</table>

src/lib/components/ui/logo.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
sizeClasses[size],
2727
className
2828
)}>
29-
<span>{"{"} 🍯 Extension Data Viewer {"}"}</span>
29+
<span>{"{"} 🍯 Data Viewer {"}"}</span>
3030
{#if version}
3131
<span class="text-yellow-400/50 text-xs">v{version}</span>
3232
{/if}

src/lib/components/virtualization/scroller.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
});
1818
</script>
1919

20-
<svelte:window bind:innerWidth={virtualizer.innerWidth} />
21-
2220
<div
2321
bind:this={container}
2422
bind:clientHeight={virtualizer.viewportHeight}

src/lib/stores/virtualization.svelte.ts

Lines changed: 16 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,45 @@
1-
import {
2-
getTotalHeight,
3-
getStartIndex,
4-
getVisibleCount,
5-
getPadding,
6-
shouldLoadMore
7-
} from "$lib/utilities/virtualization";
1+
import { getRange, getSpacers } from "$lib/utilities/virtualization";
82

93
import { type TableConfig, TableConfigSchema } from "$lib/types/table";
104

115
export class Virtualizer {
6+
// Scroll State
127
scrollTop = $state(0);
13-
innerWidth = $state(0);
148
viewportHeight = $state(800);
15-
rowHeight = $state(65);
16-
checkInterval = $state(50);
9+
innerWidth = $state(1024);
10+
11+
get rowHeight() {
12+
return this.innerWidth < 768 ? 140 : 70;
13+
}
1714

1815
private config = $state<TableConfig>(TableConfigSchema.parse({}));
19-
private timer: ReturnType<typeof setTimeout> | undefined;
20-
private at = 0;
21-
22-
private onloadrange: (offset: number, limit: number) => void = () => {};
2316

2417
constructor() {}
2518

26-
update(config: TableConfig, onloadrange: (offset: number, limit: number) => void) {
19+
update(config: TableConfig) {
2720
this.config = config;
28-
this.onloadrange = onloadrange;
2921
}
3022

3123
get totalHeight() {
32-
return getTotalHeight(this.config.count, this.rowHeight);
33-
}
34-
35-
get startIndex() {
36-
return getStartIndex(this.scrollTop, this.rowHeight);
37-
}
38-
39-
get visibleCount() {
40-
return getVisibleCount(this.viewportHeight, this.rowHeight);
24+
return this.config.count * this.rowHeight;
4125
}
4226

4327
get padding() {
44-
return getPadding(
28+
return getSpacers(
4529
this.config.count,
4630
this.config.offset,
4731
this.config.data.length,
4832
this.rowHeight
4933
);
5034
}
5135

52-
checkScroll() {
53-
const { load, start, limit } = shouldLoadMore(
54-
this.startIndex,
55-
this.visibleCount,
56-
this.config.offset,
57-
this.config.data.length,
58-
this.config.count
36+
get targetRange() {
37+
return getRange(
38+
this.scrollTop,
39+
this.viewportHeight,
40+
this.rowHeight,
41+
25
5942
);
60-
61-
if (load) {
62-
const now = Date.now();
63-
64-
const delta = now - this.at;
65-
66-
if (this.timer) clearTimeout(this.timer);
67-
68-
if (delta > this.checkInterval) {
69-
this.onloadrange(start, limit);
70-
this.at = now;
71-
} else {
72-
this.timer = setTimeout(() => {
73-
this.onloadrange(start, limit);
74-
this.at = Date.now();
75-
}, this.checkInterval - delta);
76-
}
77-
}
7843
}
7944

8045
reset() {

src/lib/utilities/sort.test.ts

Lines changed: 1 addition & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ import { describe, it, expect } from "vitest";
33
import {
44
getSortValue,
55
compareItems,
6-
sortItems,
7-
createCacheState,
8-
shouldInvalidateCache,
9-
type CacheState
6+
sortItems
107
} from "./sort";
118

129
import type { Store } from "$lib/types/store";
@@ -144,72 +141,3 @@ describe("sortItems", () => {
144141
expect(stores[0].n).toBe(original[0].n);
145142
});
146143
});
147-
148-
describe("createCacheState", () => {
149-
it("creates empty cache state", () => {
150-
const cache = createCacheState();
151-
expect(cache.results).toEqual([]);
152-
expect(cache.query).toBeNull();
153-
expect(cache.sortKey).toBeNull();
154-
expect(cache.sortDirection).toBeNull();
155-
});
156-
});
157-
158-
describe("shouldInvalidateCache", () => {
159-
const emptyCache = createCacheState();
160-
161-
const populatedCache: CacheState = {
162-
results: [createStore({ n: "Amazon" })],
163-
query: "amazon",
164-
sortKey: "info",
165-
sortDirection: "asc"
166-
};
167-
168-
it("returns true when query changes", () => {
169-
expect(shouldInvalidateCache(populatedCache, "walmart", { key: "info", direction: "asc" })).toBe(true);
170-
});
171-
172-
it("returns true when sort key changes", () => {
173-
expect(shouldInvalidateCache(populatedCache, "amazon", { key: "visits", direction: "asc" })).toBe(true);
174-
});
175-
176-
it("returns true when sort direction changes", () => {
177-
expect(shouldInvalidateCache(populatedCache, "amazon", { key: "info", direction: "desc" })).toBe(true);
178-
});
179-
180-
it("returns true when cache is empty but items exist", () => {
181-
expect(shouldInvalidateCache(emptyCache, "", undefined, 100)).toBe(true);
182-
});
183-
184-
it("returns false when nothing changes", () => {
185-
expect(shouldInvalidateCache(populatedCache, "amazon", { key: "info", direction: "asc" })).toBe(false);
186-
});
187-
188-
it("returns true when cache query is null (fresh cache)", () => {
189-
expect(shouldInvalidateCache(emptyCache, "", undefined, 0)).toBe(true);
190-
});
191-
192-
it("handles undefined sort gracefully", () => {
193-
const cacheNoSort: CacheState = {
194-
results: [createStore()],
195-
query: "",
196-
sortKey: null,
197-
sortDirection: null
198-
};
199-
expect(shouldInvalidateCache(cacheNoSort, "", undefined)).toBe(false);
200-
});
201-
202-
it("invalidates when adding sort to unsorted cache", () => {
203-
const cacheNoSort: CacheState = {
204-
results: [createStore()],
205-
query: "",
206-
sortKey: null,
207-
sortDirection: null
208-
};
209-
expect(shouldInvalidateCache(cacheNoSort, "", { key: "info", direction: "asc" })).toBe(true);
210-
});
211-
212-
it("invalidates when removing sort from sorted cache", () => {
213-
expect(shouldInvalidateCache(populatedCache, "amazon", undefined)).toBe(true);
214-
});
215-
});

src/lib/utilities/sort.ts

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,3 @@ export const sortItems = (items: Store[], sort?: SortState): Store[] => {
2929
return sorted;
3030
};
3131

32-
export type CacheState = {
33-
results: Store[];
34-
query: string | null;
35-
sortKey: string | null;
36-
sortDirection: string | null;
37-
};
38-
39-
export const createCacheState = (): CacheState => ({
40-
results: [],
41-
query: null,
42-
sortKey: null,
43-
sortDirection: null
44-
});
45-
46-
export const shouldInvalidateCache = (
47-
cache: CacheState,
48-
query: string,
49-
sort?: SortState,
50-
itemsLength?: number
51-
): boolean => {
52-
const sortKey = sort?.key || null;
53-
const sortDirection = sort?.direction || null;
54-
55-
return (
56-
query !== cache.query ||
57-
sortKey !== cache.sortKey ||
58-
sortDirection !== cache.sortDirection ||
59-
(cache.results.length === 0 && (itemsLength ?? 0) > 0)
60-
);
61-
};

0 commit comments

Comments
 (0)