CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-types--shared

Shared TypeScript type definitions for React Spectrum components and hooks, providing common interfaces for DOM interactions, styling, accessibility, internationalization, and component behavior across the React Spectrum ecosystem

Pending
Overview
Eval results
Files

collections.mddocs/

Collections and Data Structures

Generic collection interfaces supporting lists, grids, trees, and other data structures with keyboard navigation, sorting, expansion, loading states, and layout management.

Capabilities

Core Collection Interface

The fundamental collection interface that provides iteration and key-based access to items.

/**
 * A generic interface to access a readonly sequential collection of unique keyed items
 * @template T The type of items in the collection
 */
interface Collection<T> extends Iterable<T> {
  /** The number of items in the collection */
  readonly size: number;

  /** Iterate over all keys in the collection */
  getKeys(): Iterable<Key>;

  /** Get an item by its key */
  getItem(key: Key): T | null;

  /** Get an item by the index of its key */
  at(idx: number): T | null;

  /** Get the key that comes before the given key in the collection */
  getKeyBefore(key: Key): Key | null;

  /** Get the key that comes after the given key in the collection */
  getKeyAfter(key: Key): Key | null;

  /** Get the first key in the collection */
  getFirstKey(): Key | null;

  /** Get the last key in the collection */
  getLastKey(): Key | null;

  /** Iterate over the child items of the given key */
  getChildren?(key: Key): Iterable<T>;

  /** Returns a string representation of the item's contents */
  getTextValue?(key: Key): string;

  /** Filters the collection using the given function */
  filter?(filterFn: (nodeValue: string, node: T) => boolean): Collection<T>;
}

Collection Nodes

Nodes represent individual items within a collection with metadata and hierarchy support.

/**
 * Represents a node in a collection
 * @template T The type of the node's value
 */
interface Node<T> {
  /** The type of item this node represents */
  type: string;
  /** A unique key for the node */
  key: Key;
  /** The object value the node was created from */
  value: T | null;
  /** The level of depth this node is at in the hierarchy */
  level: number;
  /** Whether this item has children, even if not loaded yet */
  hasChildNodes: boolean;
  /**
   * The loaded children of this node
   * @deprecated Use collection.getChildren(node.key) instead
   */
  childNodes: Iterable<Node<T>>;
  /** The rendered contents of this node (e.g. JSX) */
  rendered: ReactNode;
  /** A string value for this node, used for features like typeahead */
  textValue: string;
  /** An accessibility label for this node */
  "aria-label"?: string;
  /** The index of this node within its parent */
  index: number;
  /** A function that should be called to wrap the rendered node */
  wrapper?: (element: ReactElement) => ReactElement;
  /** The key of the parent node */
  parentKey?: Key | null;
  /** The key of the node before this node */
  prevKey?: Key | null;
  /** The key of the node after this node */
  nextKey?: Key | null;
  /** Additional properties specific to a particular node type */
  props?: any;
  /** @private */
  shouldInvalidate?: (context: any) => boolean;
  /** A function that renders this node to a React Element in the DOM */
  render?: (node: Node<any>) => ReactElement;
}

Items and Sections

Building blocks for creating collection content with React elements.

/**
 * Properties for collection items
 * @template T The type of the item data
 */
interface ItemProps<T> extends LinkDOMProps {
  /** Rendered contents of the item or child items */
  children: ReactNode;
  /** Rendered contents of the item if children contains child items */
  title?: ReactNode;
  /** A string representation of the item's contents, used for features like typeahead */
  textValue?: string;
  /** An accessibility label for this item */
  "aria-label"?: string;
  /** A list of child item objects. Used for dynamic collections */
  childItems?: Iterable<T>;
  /** Whether this item has children, even if not loaded yet */
  hasChildItems?: boolean;
}

/**
 * Properties for collection sections
 * @template T The type of the item data
 */
interface SectionProps<T> {
  /** Rendered contents of the section, e.g. a header */
  title?: ReactNode;
  /** An accessibility label for the section */
  "aria-label"?: string;
  /** Static child items or a function to render children */
  children: ItemElement<T> | ItemElement<T>[] | ItemRenderer<T>;
  /** Item objects in the section */
  items?: Iterable<T>;
}

/** React element representing an item */
type ItemElement<T> = ReactElement<ItemProps<T>> | null;

/** Function that renders an item */
type ItemRenderer<T> = (item: T) => ItemElement<T>;

/** React element representing a section */
type SectionElement<T> = ReactElement<SectionProps<T>> | null;

/** Union of collection elements */
type CollectionElement<T> = SectionElement<T> | ItemElement<T>;

/** Children content for collections */
type CollectionChildren<T> = CollectionElement<T> | CollectionElement<T>[] | ((item: T) => CollectionElement<T>);

Collection Base Properties

Base properties for collections with static and dynamic content support.

/**
 * Base properties for collections
 * @template T The type of items in the collection
 */
interface CollectionBase<T> {
  /** The contents of the collection */
  children: CollectionChildren<T>;
  /** Item objects in the collection */
  items?: Iterable<T>;
  /** The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with */
  disabledKeys?: Iterable<Key>;
}

/**
 * Base properties for collection state
 * @template T The type of items in the collection
 * @template C The type of the collection
 */
interface CollectionStateBase<T, C extends Collection<Node<T>> = Collection<Node<T>>> extends Partial<CollectionBase<T>> {
  /** A pre-constructed collection to use instead of building one from items and children */
  collection?: C;
}

Loading States

Support for asynchronous collections with loading indicators.

/** Loading states for collections */
type LoadingState = "loading" | "sorting" | "loadingMore" | "error" | "idle" | "filtering";

/**
 * Properties for collections that support async loading
 */
interface AsyncLoadable {
  /** Whether the items are currently loading */
  isLoading?: boolean;
  /** Handler that is called when more items should be loaded, e.g. while scrolling near the bottom */
  onLoadMore?: () => any;
}

Expandable Collections

Support for hierarchical collections with expand/collapse functionality.

/**
 * Properties for expandable collections (trees)
 */
interface Expandable {
  /** The currently expanded keys in the collection (controlled) */
  expandedKeys?: Iterable<Key>;
  /** The initial expanded keys in the collection (uncontrolled) */
  defaultExpandedKeys?: Iterable<Key>;
  /** Handler that is called when items are expanded or collapsed */
  onExpandedChange?: (keys: Set<Key>) => any;
}

Sortable Collections

Support for collections with sorting functionality.

/** Sort direction options */
type SortDirection = "ascending" | "descending";

/**
 * Sort descriptor defining how to sort a collection
 */
interface SortDescriptor {
  /** The key of the column to sort by */
  column: Key;
  /** The direction to sort by */
  direction: SortDirection;
}

/**
 * Properties for sortable collections
 */
interface Sortable {
  /** The current sorted column and direction */
  sortDescriptor?: SortDescriptor;
  /** Handler that is called when the sorted column or direction changes */
  onSortChange?: (descriptor: SortDescriptor) => any;
}

Keyboard Navigation

Interfaces for custom keyboard navigation within collections.

/**
 * Keyboard navigation delegate for custom key handling
 */
interface KeyboardDelegate {
  /** Returns the key visually below the given one, or null for none */
  getKeyBelow?(key: Key): Key | null;

  /** Returns the key visually above the given one, or null for none */
  getKeyAbove?(key: Key): Key | null;

  /** Returns the key visually to the left of the given one, or null for none */
  getKeyLeftOf?(key: Key): Key | null;

  /** Returns the key visually to the right of the given one, or null for none */
  getKeyRightOf?(key: Key): Key | null;

  /** Returns the key visually one page below the given one, or null for none */
  getKeyPageBelow?(key: Key): Key | null;

  /** Returns the key visually one page above the given one, or null for none */
  getKeyPageAbove?(key: Key): Key | null;

  /** Returns the first key, or null for none */
  getFirstKey?(key?: Key | null, global?: boolean): Key | null;

  /** Returns the last key, or null for none */
  getLastKey?(key?: Key | null, global?: boolean): Key | null;

  /** Returns the next key after fromKey that matches the given search string, or null for none */
  getKeyForSearch?(search: string, fromKey?: Key | null): Key | null;
}

Layout Management

Interfaces for managing collection layout and virtualization.

/**
 * Rectangle type for layout calculations
 */
interface Rect {
  x: number;
  y: number;
  width: number;
  height: number;
}

/**
 * Size type for dimensions
 */
interface Size {
  width: number;
  height: number;
}

/**
 * Layout delegate provides layout information for collection items
 */
interface LayoutDelegate {
  /** Returns a rectangle for the item with the given key */
  getItemRect(key: Key): Rect | null;
  /** Returns the visible rectangle of the collection */
  getVisibleRect(): Rect;
  /** Returns the size of the scrollable content in the collection */
  getContentSize(): Size;
  /** Returns a list of keys between from and to */
  getKeyRange?(from: Key, to: Key): Key[];
}

Usage Examples:

import { 
  Collection, 
  Node, 
  CollectionBase, 
  ItemProps, 
  SectionProps,
  Expandable,
  Sortable,
  AsyncLoadable,
  SortDescriptor,
  Key
} from "@react-types/shared";

// Basic list component with collection support
interface ListProps<T> extends CollectionBase<T>, AsyncLoadable {
  onAction?: (key: Key) => void;
}

function List<T>({ 
  children, 
  items, 
  disabledKeys, 
  isLoading, 
  onLoadMore, 
  onAction 
}: ListProps<T>) {
  // In a real implementation, you'd build a collection from children/items
  // This is a simplified example showing the interface usage
  
  return (
    <div role="list">
      {isLoading && <div>Loading...</div>}
      {/* Render collection items */}
      <div>Collection items would be rendered here</div>
      {onLoadMore && (
        <button onClick={onLoadMore}>Load More</button>
      )}
    </div>
  );
}

// Tree component with expansion support
interface TreeProps<T> extends CollectionBase<T>, Expandable {
  onAction?: (key: Key) => void;
}

function Tree<T>({ 
  children, 
  items, 
  disabledKeys,
  expandedKeys,
  defaultExpandedKeys,
  onExpandedChange,
  onAction 
}: TreeProps<T>) {
  const [expanded, setExpanded] = useState<Set<Key>>(
    new Set(defaultExpandedKeys || [])
  );

  const handleToggleExpanded = (key: Key) => {
    const newExpanded = new Set(expandedKeys || expanded);
    if (newExpanded.has(key)) {
      newExpanded.delete(key);
    } else {
      newExpanded.add(key);
    }
    setExpanded(newExpanded);
    onExpandedChange?.(newExpanded);
  };

  return (
    <div role="tree">
      {/* Tree implementation would render nodes with expand/collapse controls */}
      <div>Tree nodes would be rendered here</div>
    </div>
  );
}

// Table component with sorting support
interface TableProps<T> extends CollectionBase<T>, Sortable {
  columns: Array<{
    key: Key;
    name: string;
    allowsSorting?: boolean;
  }>;
}

function Table<T>({ 
  children, 
  items, 
  columns,
  sortDescriptor,
  onSortChange 
}: TableProps<T>) {
  const handleSort = (column: Key) => {
    const newDescriptor: SortDescriptor = {
      column,
      direction: 
        sortDescriptor?.column === column && sortDescriptor.direction === "ascending"
          ? "descending" 
          : "ascending"
    };
    onSortChange?.(newDescriptor);
  };

  return (
    <table>
      <thead>
        <tr>
          {columns.map(column => (
            <th 
              key={column.key}
              onClick={() => column.allowsSorting && handleSort(column.key)}
              style={{ 
                cursor: column.allowsSorting ? "pointer" : "default"
              }}
            >
              {column.name}
              {sortDescriptor?.column === column.key && (
                <span>{sortDescriptor.direction === "ascending" ? " ↑" : " ↓"}</span>
              )}
            </th>
          ))}
        </tr>
      </thead>
      <tbody>
        {/* Table rows would be rendered here */}
      </tbody>
    </table>
  );
}

// Usage with static content
function StaticListExample() {
  return (
    <List>
      <Item key="1">First item</Item>
      <Item key="2">Second item</Item>
      <Section title="Group A">
        <Item key="3">Third item</Item>
        <Item key="4">Fourth item</Item>
      </Section>
    </List>
  );
}

// Usage with dynamic content
function DynamicListExample() {
  const items = [
    { id: 1, name: "Apple", category: "Fruit" },
    { id: 2, name: "Carrot", category: "Vegetable" },
    { id: 3, name: "Banana", category: "Fruit" }
  ];

  return (
    <List items={items} disabledKeys={[2]}>
      {(item) => (
        <Item key={item.id} textValue={item.name}>
          {item.name} ({item.category})
        </Item>
      )}
    </List>
  );
}

// Tree with expansion
function TreeExample() {
  const treeData = [
    {
      id: 1,
      name: "Documents",
      children: [
        { id: 2, name: "Resume.pdf" },
        { id: 3, name: "Cover Letter.doc" }
      ]
    },
    {
      id: 4,
      name: "Images",
      children: [
        { id: 5, name: "Photo1.jpg" },
        { id: 6, name: "Photo2.png" }
      ]
    }
  ];

  return (
    <Tree 
      items={treeData}
      defaultExpandedKeys={[1]}
      onExpandedChange={(keys) => console.log("Expanded keys:", keys)}
    >
      {(item) => (
        <Item 
          key={item.id} 
          hasChildItems={!!item.children?.length}
          childItems={item.children}
        >
          {item.name}
        </Item>
      )}
    </Tree>
  );
}

// Sortable table
function SortableTableExample() {
  const data = [
    { id: 1, name: "Alice", age: 30, department: "Engineering" },
    { id: 2, name: "Bob", age: 25, department: "Design" },
    { id: 3, name: "Carol", age: 35, department: "Marketing" }
  ];

  const columns = [
    { key: "name", name: "Name", allowsSorting: true },
    { key: "age", name: "Age", allowsSorting: true },
    { key: "department", name: "Department", allowsSorting: false }
  ];

  const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
    column: "name",
    direction: "ascending"
  });

  return (
    <Table
      items={data}
      columns={columns}
      sortDescriptor={sortDescriptor}
      onSortChange={setSortDescriptor}
    >
      {(item) => (
        <Row key={item.id}>
          <Cell>{item.name}</Cell>
          <Cell>{item.age}</Cell>
          <Cell>{item.department}</Cell>
        </Row>
      )}
    </Table>
  );
}

Install with Tessl CLI

npx tessl i tessl/npm-react-types--shared

docs

collections.md

design-tokens.md

dom-aria.md

drag-drop.md

events.md

index.md

input-handling.md

labelable.md

refs.md

selection.md

styling.md

tile.json