npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@minht11/solid-virtual-container

v0.2.1

Published

Virtual list/grid for Solid-js.

Downloads

616

Readme

Virtual container for Solid-js

Efficient, single direction virtual list/grid for Solid-js

Features

  • Support for grid/list modes.
  • Only render visible items, no matter how big is your list.
  • Keyboard navigation and focus management out of the box.
  • Option to change items size based on available space.

Demo

Usage

npm install @minht11/solid-virtual-container

Create list item component.

const ListItem = (props) => (
  <div
    // Required for items to switch places.
    style={props.style}
    // Used for keyboard navigation and accessibility.
    tabIndex={props.tabIndex}
    role="listitem"
  >
    <div>{props.item}</div>
  </div>
)

Create vertically scrolling virtual list

import { VirtualContainer } from "@minht11/solid-virtual-container"

const App = () => {
  const items = [0, 1, 2, 3]
  let scrollTargetElement!: HTMLDivElement
  return (
    <div style={{ overflow: 'auto' }} ref={scrollTargetElement}>
      <VirtualContainer
        items={items}
        scrollTarget={scrollTargetElement}
        // Define size you wish your list items to take.
        itemSize={{ height: 50 }}
      >
        {ListItem}
      </VirtualContainer>
    </div>
  )
}

or a virtual grid

const App = () => {
  const items = [0, 1, 2, 3]
  let scrollTargetElement!: HTMLDivElement
  return (
    <div style={{ overflow: 'auto' }} ref={scrollTargetElement}>
      <VirtualContainer
        items={items}
        scrollTarget={scrollTargetElement}
        itemSize={{ height: 50, width: 50 }}
        // Calculate how many columns to show.
        crossAxisCount={(measurements) => (
          Math.floor(
            measurements.container.cross / measurements.itemSize.cross
          )
        )}
      >
        {ListItem}
      </VirtualContainer>
    </div>
  )
}

Api

ScrollTargetContext

If you you do not have an immediate access to the VirtualContainer, or do not want to pass props several components deep you can use context api.

const App = () => {
  const items = [0, 1, 2, 3]
  let scrollTargetElement!: HTMLDivElement
  return (
    <div ref={scrollTargetElement}>
      <ScrollTargetContext.Provider value={{ scrollTarget: scrollTargetElement }}>
        ...
        <VirtualContainer ... />
        ...
      </ScrollTargetContext.Provider>
    </div>
  )
}

Virtual container options

interface VirtualContainer<T> {
  // your list data array. 
  items: readonly T[]
  // Define elements size.
  // All elements will use same size.
  itemSize: VirtualItemSize
  // Scrolling element, if context api is used this is not needed,
  // however you must use one or the other.
  scrollTarget?: HTMLElement
  // Scroll direction. Default is vertical.
  direction?: 'vertical' | 'horizontal'
  // Number of elements to render below and above currently visible items,
  // if not provided an optimal amount will be automatically picked.
  overscan?: number
  // Container className, if at all possible ignore this option,
  // because direct styling can break virtualizing, instead wrap
  // element in another div and style that.
  className?: string
  role?: JSX.HTMLAttributes<HTMLDivElement>['role']
  // Function which determines how many columns in vertical mode
  // or rows in horizontal to show. Default is 1.
  crossAxisCount?: (
    measurements: CrossAxisCountOptions,
    // The same as items.length
    itemsCount: number,
  ) => number
  // List item render function.
  children: (props: VirtualItemProps<T>) => JSX.Element
}

If direction is vertical main axis is vertical. If direction is horizontal main axis is horizontal.

interface Axis {
  // Main scrolling direction axis.
  main: number
  // Opposite axis to main.
  cross: number
}

Parameter object used in VirtualContainer.crossAxisCount function.

interface CrossAxisCountOptions {
  // Scrolling element dimensions.
  target: Axis
  // Container element dimensions.
  container: Axis
  // List element dimensions.
  itemSize: Axis
}

Item size

// You can use static object to define item size
interface VirtualItemSizeStatic {
  width?: number
  height?: number
}

// or use a function to calculate it when layout changes.
type VirtualItemSizeDynamic = (
  crossAxisContentSize: number,
  // Scroll direction.
  isHorizontal: boolean,
) => VirtualItemSizeStatic

Dynamic size is useful when you want your grid items to fill all available space

// One possible example
const calculateItemSize = (crossAxisSize: number) => {
  // Choose minimum size depending on the available space.
  const minWidth = crossAxisSize > 560 ? 180 : 140

  const count = Math.floor(crossAxisSize / minWidth)
  const width = Math.floor(crossAxisSize / count)

  return {
    width,
    height: width + 48
  }
}

<VirtualContent itemSize={calculateItemSize}></VirtualContent>

Limitations

Different individual item sizes and scrolling with both directions at the same time are not and likely will never be supported by this package.

Page CSP must allow inline style sheets.