CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-virtualized

React components for efficiently rendering large lists and tabular data

Pending
Overview
Eval results
Files

utilities.mddocs/

Utility Functions

Helper functions and utilities for customizing virtualization behavior and enhancing performance.

Capabilities

Overscan Index Getters

Functions that calculate which additional (non-visible) items to render for smooth scrolling.

/**
 * Default overscan calculation for standard usage
 * @param params - Overscan calculation parameters
 */
function defaultOverscanIndicesGetter(params: {
  /** Total number of cells */
  cellCount: number,
  /** Number of extra cells to render */
  overscanCellsCount: number,
  /** Scroll direction (-1 for backward, 1 for forward) */
  scrollDirection: number,
  /** Index of first visible cell */
  startIndex: number,
  /** Index of last visible cell */
  stopIndex: number
}): {
  /** Index of first cell to render (including overscan) */
  overscanStartIndex: number,
  /** Index of last cell to render (including overscan) */
  overscanStopIndex: number
};

/**
 * Enhanced overscan calculation for accessibility
 * Renders more cells to improve screen reader performance
 * @param params - Overscan calculation parameters
 */
function accessibilityOverscanIndicesGetter(params: {
  cellCount: number,
  overscanCellsCount: number,
  /** Scroll direction (-1 for backward, 1 for forward) */
  scrollDirection: number,
  startIndex: number,
  stopIndex: number
}): {
  overscanStartIndex: number,
  overscanStopIndex: number
};

Usage Examples:

import React from 'react';
import { 
  List, 
  Grid,
  defaultOverscanIndicesGetter, 
  accessibilityOverscanIndicesGetter 
} from 'react-virtualized';

// List with custom overscan calculation
function CustomOverscanList({ items, highPerformanceMode }) {
  // Custom overscan getter that adjusts based on performance mode
  const customOverscanGetter = (params) => {
    if (highPerformanceMode) {
      // Render fewer extra items for better performance
      return defaultOverscanIndicesGetter({
        ...params,
        overscanCellsCount: Math.min(params.overscanCellsCount, 2)
      });
    } else {
      // Use accessibility mode for better UX
      return accessibilityOverscanIndicesGetter(params);
    }
  };

  const rowRenderer = ({ index, key, style }) => (
    <div key={key} style={style} className="list-item">
      {items[index]}
    </div>
  );

  return (
    <div>
      <div className="performance-indicator">
        Mode: {highPerformanceMode ? 'High Performance' : 'Accessibility'}
      </div>
      <List
        height={400}
        width={300}
        rowCount={items.length}
        rowHeight={50}
        rowRenderer={rowRenderer}
        overscanIndicesGetter={customOverscanGetter}
        overscanRowCount={5}
      />
    </div>
  );
}

// Grid with accessibility-focused overscan
function AccessibleGrid({ data }) {
  const cellRenderer = ({ columnIndex, key, rowIndex, style }) => (
    <div key={key} style={style} className="grid-cell">
      {data[rowIndex][columnIndex]}
    </div>
  );

  return (
    <Grid
      cellRenderer={cellRenderer}
      columnCount={data[0].length}
      columnWidth={100}
      height={400}
      rowCount={data.length}
      rowHeight={50}
      width={500}
      overscanIndicesGetter={accessibilityOverscanIndicesGetter}
      overscanColumnCount={3}
      overscanRowCount={3}
    />
  );
}

Cell Range Renderer

Controls how cells are rendered within the visible and overscan ranges.

/**
 * Default cell range renderer
 * @param params - Cell range rendering parameters
 */
function defaultCellRangeRenderer(params: {
  /** Cache of rendered cells */
  cellCache: object,
  /** Cell rendering function */
  cellRenderer: (params: {columnIndex: number, key: string, rowIndex: number, style: object}) => React.Node,
  /** First visible column index */
  columnStartIndex: number,
  /** Last visible column index */
  columnStopIndex: number,
  /** Deferred measurement cache */
  deferredMeasurementCache?: object,
  /** Horizontal scroll adjustment */
  horizontalOffsetAdjustment: number,
  /** Whether scrolling is in progress */
  isScrolling: boolean,
  /** Parent component reference */
  parent: object,
  /** First visible row index */
  rowStartIndex: number,
  /** Last visible row index */
  rowStopIndex: number,
  /** Cache of computed styles */
  styleCache: object,
  /** Vertical scroll adjustment */
  verticalOffsetAdjustment: number,
  /** Map of visible column indices */
  visibleColumnIndices: object,
  /** Map of visible row indices */
  visibleRowIndices: object
}): React.Node[];

Usage Examples:

import React from 'react';
import { Grid, defaultCellRangeRenderer } from 'react-virtualized';

// Grid with custom cell range renderer for performance optimization
function OptimizedGrid({ data, enableDebugMode }) {
  const customCellRangeRenderer = (params) => {
    if (enableDebugMode) {
      // Add debug information to rendered cells
      console.log('Rendering cells:', {
        columns: `${params.columnStartIndex}-${params.columnStopIndex}`,
        rows: `${params.rowStartIndex}-${params.rowStopIndex}`,
        isScrolling: params.isScrolling
      });
    }

    // Use default renderer but with custom caching strategy
    const cells = defaultCellRangeRenderer(params);
    
    if (enableDebugMode) {
      // Add debug borders to cells during scrolling
      return cells.map(cell => 
        React.cloneElement(cell, {
          ...cell.props,
          style: {
            ...cell.props.style,
            border: params.isScrolling ? '1px solid red' : '1px solid #ddd'
          }
        })
      );
    }

    return cells;
  };

  const cellRenderer = ({ columnIndex, key, rowIndex, style }) => (
    <div key={key} style={style} className="grid-cell">
      {data[rowIndex][columnIndex]}
    </div>
  );

  return (
    <Grid
      cellRenderer={cellRenderer}
      cellRangeRenderer={customCellRangeRenderer}
      columnCount={data[0].length}
      columnWidth={120}
      height={400}
      rowCount={data.length}
      rowHeight={50}
      width={600}
    />
  );
}

Multi-Column Sort Utility

Advanced sorting utility for tables with multiple column sorting capabilities.

/**
 * Multi-column sort utility for tables
 * @param sortFunction - Function to handle sort events
 * @param options - Configuration options
 */
function createTableMultiSort(
  sortFunction: (params: {sortBy: string, sortDirection: string}) => void,
  options?: {
    /** Default columns to sort by */
    defaultSortBy?: Array<string>,
    /** Default sort directions for columns */
    defaultSortDirection?: {[key: string]: string}
  }
): (params: {event: Event, sortBy: string, sortDirection: string}) => void;

Usage Examples:

import React, { useState } from 'react';
import { Table, Column, createTableMultiSort, SortDirection } from 'react-virtualized';

// Advanced table with multi-column sorting
function MultiSortTable({ employees }) {
  const [sortState, setSortState] = useState({
    sortBy: ['department', 'name'],
    sortDirection: { department: 'ASC', name: 'ASC' }
  });
  const [sortedData, setSortedData] = useState(employees);

  const performMultiSort = (data, sortBy, sortDirection) => {
    return [...data].sort((a, b) => {
      for (const column of sortBy) {
        const aVal = a[column];
        const bVal = b[column];
        const direction = sortDirection[column];
        
        let result = 0;
        if (aVal < bVal) result = -1;
        else if (aVal > bVal) result = 1;
        
        if (result !== 0) {
          return direction === SortDirection.DESC ? -result : result;
        }
      }
      return 0;
    });
  };

  const multiSortHandler = createTableMultiSort(
    ({ sortBy, sortDirection }) => {
      // Handle multi-column sort
      const newSortBy = [sortBy];
      const newSortDirection = { [sortBy]: sortDirection };
      
      // Add existing sorts for multi-column sorting
      sortState.sortBy.forEach(existingSort => {
        if (existingSort !== sortBy) {
          newSortBy.push(existingSort);
          newSortDirection[existingSort] = sortState.sortDirection[existingSort];
        }
      });

      const newSortState = {
        sortBy: newSortBy.slice(0, 3), // Limit to 3 columns
        sortDirection: newSortDirection
      };

      setSortState(newSortState);
      setSortedData(performMultiSort(employees, newSortState.sortBy, newSortState.sortDirection));
    },
    {
      defaultSortBy: ['department', 'name'],
      defaultSortDirection: { department: 'ASC', name: 'ASC' }
    }
  );

  const rowGetter = ({ index }) => sortedData[index];

  // Custom header renderer showing sort priority
  const multiSortHeaderRenderer = ({ label, dataKey, sortBy, sortDirection }) => {
    const sortIndex = sortState.sortBy.indexOf(dataKey);
    const isSorted = sortIndex !== -1;
    
    return (
      <div className="multi-sort-header">
        <span>{label}</span>
        {isSorted && (
          <div className="sort-info">
            <span className="sort-priority">{sortIndex + 1}</span>
            <span className="sort-direction">
              {sortState.sortDirection[dataKey] === 'ASC' ? '▲' : '▼'}
            </span>
          </div>
        )}
      </div>
    );
  };

  return (
    <div>
      <div className="sort-info-panel">
        <h4>Current Sort:</h4>
        <ul>
          {sortState.sortBy.map((column, index) => (
            <li key={column}>
              {index + 1}. {column} ({sortState.sortDirection[column]})
            </li>
          ))}
        </ul>
      </div>
      
      <Table
        width={800}
        height={400}
        headerHeight={60}
        rowHeight={40}
        rowCount={sortedData.length}
        rowGetter={rowGetter}
        onHeaderClick={multiSortHandler}
      >
        <Column
          label="Department"
          dataKey="department"
          width={150}
          headerRenderer={multiSortHeaderRenderer}
        />
        <Column
          label="Name"
          dataKey="name"
          width={200}
          headerRenderer={multiSortHeaderRenderer}
        />
        <Column
          label="Position"
          dataKey="position"
          width={180}
          headerRenderer={multiSortHeaderRenderer}
        />
        <Column
          label="Salary"
          dataKey="salary"
          width={120}
          headerRenderer={multiSortHeaderRenderer}
          cellRenderer={({ cellData }) => `$${cellData.toLocaleString()}`}
        />
        <Column
          label="Start Date"
          dataKey="startDate"
          width={120}
          headerRenderer={multiSortHeaderRenderer}
          cellRenderer={({ cellData }) => new Date(cellData).toLocaleDateString()}
        />
      </Table>
    </div>
  );
}

Masonry Cell Positioner

Utility for creating position managers for masonry layouts.

/**
 * Creates a cell positioner for masonry layouts
 * @param params - Positioner configuration
 */
function createMasonryCellPositioner(params: {
  /** Cache for cell measurements */
  cellMeasurerCache: CellMeasurerCache,
  /** Number of columns in the masonry */
  columnCount: number,
  /** Width of each column */
  columnWidth: number,
  /** Space between items */
  spacer?: number
}): {
  /** Reset the positioner state */
  reset: (params: {columnCount: number, columnWidth: number, spacer?: number}) => void;
};

Usage Examples:

import React, { useMemo, useCallback } from 'react';
import { 
  Masonry, 
  CellMeasurerCache, 
  createMasonryCellPositioner,
  AutoSizer 
} from 'react-virtualized';

// Responsive masonry with dynamic positioner
function ResponsiveMasonryGrid({ items, containerWidth }) {
  const cache = useMemo(() => new CellMeasurerCache({
    defaultHeight: 200,
    fixedWidth: true
  }), []);

  // Calculate columns based on container width
  const columnCount = Math.max(1, Math.floor(containerWidth / 250));
  const columnWidth = Math.floor((containerWidth - (columnCount + 1) * 10) / columnCount);

  const cellPositioner = useMemo(() => {
    return createMasonryCellPositioner({
      cellMeasurerCache: cache,
      columnCount,
      columnWidth,
      spacer: 10
    });
  }, [cache, columnCount, columnWidth]);

  // Reset positioner when layout changes
  const resetPositioner = useCallback(() => {
    cellPositioner.reset({
      columnCount,
      columnWidth,
      spacer: 10
    });
  }, [cellPositioner, columnCount, columnWidth]);

  // Reset when dimensions change
  React.useEffect(() => {
    resetPositioner();
  }, [resetPositioner]);

  const cellRenderer = ({ index, key, parent, style }) => (
    <CellMeasurer
      cache={cache}
      index={index}
      key={key}
      parent={parent}
    >
      <div style={style} className="masonry-item">
        <img
          src={items[index].imageUrl}
          alt={items[index].title}
          style={{ width: '100%', height: 'auto' }}
          onLoad={() => cache.clear(index, 0)}
        />
        <div className="item-content">
          <h3>{items[index].title}</h3>
          <p>{items[index].description}</p>
        </div>
      </div>
    </CellMeasurer>
  );

  return (
    <div style={{ height: 600, width: '100%' }}>
      <AutoSizer>
        {({ height, width }) => (
          <Masonry
            cellCount={items.length}
            cellMeasurerCache={cache}
            cellPositioner={cellPositioner}
            cellRenderer={cellRenderer}
            height={height}
            width={width}
          />
        )}
      </AutoSizer>
    </div>
  );
}

// Masonry with custom spacing and layout
function CustomMasonry({ photos, spacing = 15 }) {
  const cache = useMemo(() => new CellMeasurerCache({
    defaultHeight: 300,
    fixedWidth: true
  }), []);

  const cellPositioner = useMemo(() => {
    return createMasonryCellPositioner({
      cellMeasurerCache: cache,
      columnCount: 4,
      columnWidth: 200,
      spacer: spacing
    });
  }, [cache, spacing]);

  const cellRenderer = ({ index, key, parent, style }) => {
    const photo = photos[index];
    
    return (
      <CellMeasurer
        cache={cache}
        index={index}
        key={key}
        parent={parent}
      >
        {({ measure, registerChild }) => (
          <div
            ref={registerChild}
            style={{
              ...style,
              borderRadius: '8px',
              overflow: 'hidden',
              boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
            }}
            className="photo-item"
          >
            <img
              src={photo.url}
              alt={photo.title}
              onLoad={measure}
              style={{ width: '100%', height: 'auto', display: 'block' }}
            />
            <div style={{ padding: 12, backgroundColor: 'white' }}>
              <h4 style={{ margin: '0 0 8px 0' }}>{photo.title}</h4>
              <p style={{ margin: 0, color: '#666', fontSize: '14px' }}>
                {photo.description}
              </p>
            </div>
          </div>
        )}
      </CellMeasurer>
    );
  };

  return (
    <div style={{ height: 600, width: 850 }}>
      <Masonry
        cellCount={photos.length}
        cellMeasurerCache={cache}
        cellPositioner={cellPositioner}
        cellRenderer={cellRenderer}
        height={600}
        width={850}
      />
    </div>
  );
}

Constants and Configuration

/** Default timeout for scrolling reset (in milliseconds) */
const DEFAULT_SCROLLING_RESET_TIME_INTERVAL: 150;

/** Timeout for window scroll detection */
const IS_SCROLLING_TIMEOUT: 150;

/** Scroll direction constants for internal use */
const SCROLL_DIRECTION_BACKWARD: 'backward';
const SCROLL_DIRECTION_FORWARD: 'forward';

Install with Tessl CLI

npx tessl i tessl/npm-react-virtualized

docs

core-components.md

dynamic-content.md

index.md

layout-components.md

navigation-components.md

specialized-layouts.md

table-components.md

utilities.md

tile.json