Skip to content

Conversation

@lygaret
Copy link

@lygaret lygaret commented Dec 1, 2025

I'd like to be able to use a virtual list with an <ol> element, since that's semantically correct for my application.

Currently, in order to correctly set the start attribute, I'm currently dividing viewTop by rowHeight, which is just how it's defined, and lets me get at the value correctly, but it's a very ugly hack to get the value which is just hidden under the lambda.

In use, this looks like:

  const [virtual, virtualOnScroll] = createVirtualList({
    items: entries,
    overscanCount: 5,
    rootHeight,
    rowHeight
  })

  return (
    <div class={styles['list-wrapper']} onScroll={virtualOnScroll}>
      <ol start={virtual().firstIndex} style={{top: `${virtual().viewerTop}px`}}>
        <For
          each={virtual().visibleItems}
          fallback={<li class={styles['list-empty-marker']} />}
        >
        /* whatever ... */
      </ol>
    </div>
  )

lastIndex is actually _exclusive_, which would require math, and I don't
feel like it's likely to be as useful as firstIndex
@changeset-bot
Copy link

changeset-bot bot commented Dec 1, 2025

🦋 Changeset detected

Latest commit: 16727c1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@solid-primitives/virtual Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

containerHeight: items.length * rowHeight,
viewerTop: firstIndex * rowHeight,
visibleItems: items.slice(firstIndex, lastIndex) as unknown as T,
firstIndex,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while you're at it, you can also expose lastIndex.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can. I did originally, but removed it because its a little weird semantically: when a list is empty, should it return -1, or undefined?

firstIndex being zero makes sense, kinda, but I didn't know what to do with last's typing, so I skipped it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now, it's more like nextIndex than last because it's an exclusive range.

This comment was marked as duplicate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, too.

@lygaret
Copy link
Author

lygaret commented Dec 2, 2025

@atk I ended up going with lastIndex: number | undefined -- semantically, it's undefined for an empty list, though I'm not allowing firstIndex to be undefined, and returning 0 in that case, maybe that's wrong?

nextIndex also has a weird semantic, but imo nextIndex: undefined means more like "there's no more of the list", which might be a better thing to expose?

let me know your thoughts and I'll move that direction, thank you!

visibleItems: items.slice(firstIndex, lastIndex) as unknown as T,
firstIndex,
lastIndex: lastIndex > 0 ? lastIndex - 1 : undefined,
// -1 because slice is an exclusive range
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be items.length if it is -1?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, I'm not sure what you're asking.

The -1 is an explanation of the reason we're not just passing it in straight, and it's already based on items.length, hence the whole thing ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I read the whole thing in a different order. My bad, I'm currently down with a flu.

@atk
Copy link
Member

atk commented Dec 2, 2025

Now all that is left to do is add a changeset using npx changeset.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants