main.tsx
const itemSize = 56;
const pageSize = 10;
const [limit, setLimit] = createSignal(pageSize);
const [feedbacks, feedbacksResult] = useQuery(query.limit(limit()))..... // reactive query where feedbacks is an Accessor<Obj>[]
let parentRef!: HTMLDivElement;
// tableref returns setter, and observer for width and height
const { ref: tableRef, width, height } = useElementSize<HTMLDivElement>();
// My feedbacks on initial renders can be empty [] but for some reason it is not getting updated without creatememo even though feedbacks() is reactive?
const virtualizer = createMemo(() =>
createVirtualizer({
count: feedbacks().length,
estimateSize: () => itemSize,
overscan: 5,
getItemKey: (index) => feedbacks()[index].id,
getScrollElement: () => parentRef,
}),
);
onCleanup(() => {
virtualizer().scrollToOffset(0);
});
createEffect(() => {
//Successfulluconsoles log 10
console.log("Feedbacks", feedbacks().length);
const virtualItems = virtualizer().getVirtualItems();
//For some odd reason virtaulitems arent in-sync with the feedbacks().length
//eg console log is :
// Feedbacks 10
// virtualItems 10
// Feedbacks 20
// virtualItems 14
console.log("virtualItems", virtualItems.length);
const lastItem = virtualItems[virtualItems.length - 1];
if (!lastItem) {
return;
}
if (lastItem.index >= limit() - pageSize / 4) {
setLimit(limit() + pageSize);
}
});
// component
<section class="h-full w-full space-y-2 pt-3 pr-2">
<FeedbackFilter />
<div>
<Switch>
<Match
when={
feedbacks().length > 0 || feedbacksResult().type !== "complete"
}
>
<div class="flex flex-1 flex-col" ref={tableRef}>
<div
style={{
width: `${width()}px`,
height: `${height() || 500}px`,
overflow: "auto",
}}
ref={parentRef}
>
<div
class="relative"
style={{ height: `${virtualizer().getTotalSize() + 200}px` }}
>
<For each={virtualizer().getVirtualItems()}>
{(virtualItem) => {
const feedback = feedbacks()[virtualItem.index];
return (
<FeedbackList
feedback={feedback}
start={virtualItem.start}
/>
);
}}
</For>
</div>
</div>
</div>
</Match>
<Match
when={
feedbacks().length === 0 && feedbacksResult().type === "complete"
}
>
<div>No Feedbacks Found</div>
</Match>
</Switch>
</div>
</section>
export default function FeedbackList({
feedback,
start = 0,
}: {
feedback: FeedbackProps;
start?: number;
}) {
return (
<Card
class="relative isolate h-[56px] w-full overflow-auto rounded-none border-0 border-b p-0"
style={{
transform: `translateY(${start}px)`,
}}
>
<article class="flex h-14 items-center justify-between p-2">
<CardHeader class="p-0">
<CardTitle class="flex flex-row items-center gap-2">
{/* <div
class="iconify solar--clipboard-text-line-duotone size-5"
aria-hidden="true"
/> */}
<A href={`/${feedback.workspaceId}/${feedback.id}`} class="text-sm">
{feedback.title}
<span class="absolute inset-0 z-10 hover:bg-accent/10" />
</A>
</CardTitle>
</CardHeader>
<CardContent class="p-0">
<div class="flex items-center gap-2">
<Show when={feedback.likesCount && feedback.likesCount > 0}>
<div class="flex items-center gap-1 rounded-lg border px-1">
<div class="iconify solar--heart-angle-linear size-3.5" />
<span>{feedback.likesCount}</span>
</div>
</Show>
<Show when={feedback.commentsCount && feedback.commentsCount > 0}>
<div class="flex items-center gap-1 rounded-lg border px-1">
<div class="iconify solar--chat-square-bold-duotone size-3.5" />
<span>{feedback.commentsCount}</span>
</div>
</Show>
<div>
{/* <Show when={feedback.createdAt}>
{(createdAt) => (
<time
dateTime={new Date(createdAt()).toISOString()}
class="font-semibold text-xs"
>
{formatDate(createdAt())}
</time>
)}
</Show> */}
</div>
<div>
<Show when={feedback.user}>
{(user) => (
<>
<Avatar>
<AvatarImage src={user().image || ""} />
<AvatarFallback>
{user().name?.charAt(0).toUpperCase()}
</AvatarFallback>
</Avatar>
</>
)}
</Show>
</div>
</div>
</CardContent>
</article>
</Card>
);
}