or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

buttons-actions.mdcolor-management.mddata-display.mddata-management.mddate-time.mdform-controls.mdfoundation-layout.mdindex.mdinternationalization.mdoverlays-dialogs.mdprogress-status.mdselection-navigation.md
tile.json

data-management.mddocs/

Data Management

Hooks and utilities for managing collections, async data, drag & drop interactions, and state management in React Spectrum applications.

Capabilities

Collection Management

useListData Hook

Hook for managing list data with selection, filtering, sorting, and CRUD operations.

/**
 * Hook for managing list data with selection and CRUD operations
 * @param options - Configuration options for list data management
 * @returns ListData instance with data manipulation methods
 */
function useListData<T>(options: ListOptions<T>): ListData<T>;

interface ListOptions<T> {
  /** Initial list items */
  initialItems?: T[];
  /** Initial selected keys */
  initialSelectedKeys?: Selection;
  /** Function to get unique key from item */
  getKey?: (item: T) => Key;
  /** Initial sort descriptor */
  initialSortDescriptor?: SortDescriptor;
  /** Initial filter text */
  initialFilterText?: string;
}

interface ListData<T> {
  /** Current list items */
  readonly items: T[];
  /** Currently selected keys */
  readonly selectedKeys: Selection;
  /** Current sort descriptor */
  readonly sortDescriptor: SortDescriptor;
  /** Current filter text */
  readonly filterText: string;
  
  /** Set selected keys */
  setSelectedKeys(keys: Selection): void;
  /** Set sort descriptor */
  setSortDescriptor(descriptor: SortDescriptor): void;
  /** Set filter text */
  setFilterText(text: string): void;
  /** Append items to end of list */
  append(...items: T[]): void;
  /** Prepend items to beginning of list */
  prepend(...items: T[]): void;
  /** Insert items at specific index */
  insert(index: number, ...items: T[]): void;
  /** Remove items by keys */
  remove(...keys: Key[]): void;
  /** Remove selected items */
  removeSelectedItems(): void;
  /** Move item to new index */
  move(key: Key, toIndex: number): void;
  /** Update item by key */
  update(key: Key, newItem: T): void;
  /** Get item by key */
  getItem(key: Key): T;
}

useAsyncList Hook

Hook for managing asynchronous list data with loading states and pagination.

/**
 * Hook for managing asynchronous list data with loading and pagination
 * @param options - Configuration options for async list management
 * @returns AsyncListData instance with async data methods
 */
function useAsyncList<T>(options: AsyncListOptions<T>): AsyncListData<T>;

interface AsyncListOptions<T> {
  /** Function to load initial data */
  load?: (params: LoadParams) => Promise<{ items: T[]; cursor?: string }>;
  /** Function to sort data */
  sort?: (params: SortParams<T>) => Promise<{ items: T[] }>;
  /** Initial sort descriptor */
  initialSortDescriptor?: SortDescriptor;
  /** Function to get unique key from item */
  getKey?: (item: T) => Key;
}

interface LoadParams {
  /** Current sort descriptor */
  sortDescriptor?: SortDescriptor;
  /** Pagination cursor */
  cursor?: string;
  /** Filter text */
  filterText?: string;
}

interface SortParams<T> {
  /** Items to sort */
  items: T[];
  /** Sort descriptor */
  sortDescriptor: SortDescriptor;
}

interface AsyncListData<T> {
  /** Current list items */
  readonly items: T[];
  /** Loading state */
  readonly loadingState: LoadingState;
  /** Error state */
  readonly error?: Error;
  /** Current sort descriptor */
  readonly sortDescriptor: SortDescriptor;
  /** Filter text */
  readonly filterText: string;
  
  /** Reload data */
  reload(): void;
  /** Load more data (pagination) */
  loadMore(): void;
  /** Sort data */
  sort(descriptor: SortDescriptor): void;
  /** Set filter text */
  setFilterText(text: string): void;
  /** Insert items */
  insert(index: number, ...items: T[]): void;
  /** Remove items */
  remove(...keys: Key[]): void;
  /** Update item */
  update(key: Key, item: T): void;
  /** Get item by key */
  getItem(key: Key): T;
}

useTreeData Hook

Hook for managing hierarchical tree data with expand/collapse and selection.

/**
 * Hook for managing hierarchical tree data with expand/collapse
 * @param options - Configuration options for tree data management
 * @returns TreeData instance with tree manipulation methods
 */
function useTreeData<T>(options: TreeOptions<T>): TreeData<T>;

interface TreeOptions<T> {
  /** Initial tree items */
  initialItems?: T[];
  /** Initial selected keys */
  initialSelectedKeys?: Selection;
  /** Initial expanded keys */
  initialExpandedKeys?: Set<Key>;
  /** Function to get unique key from item */
  getKey?: (item: T) => Key;
  /** Function to get child items */
  getChildren?: (item: T) => T[];
}

interface TreeData<T> {
  /** Current tree items */
  readonly items: T[];
  /** Currently selected keys */
  readonly selectedKeys: Selection;
  /** Currently expanded keys */
  readonly expandedKeys: Set<Key>;
  
  /** Set selected keys */
  setSelectedKeys(keys: Selection): void;
  /** Set expanded keys */
  setExpandedKeys(keys: Set<Key>): void;
  /** Toggle expansion of key */
  toggleKey(key: Key): void;
  /** Insert items */
  insert(parentKey: Key | null, index: number, ...items: T[]): void;
  /** Insert before item */
  insertBefore(key: Key, ...items: T[]): void;
  /** Insert after item */
  insertAfter(key: Key, ...items: T[]): void;
  /** Append to parent */
  append(parentKey: Key | null, ...items: T[]): void;
  /** Prepend to parent */
  prepend(parentKey: Key | null, ...items: T[]): void;
  /** Remove items */
  remove(...keys: Key[]): void;
  /** Move item */
  move(key: Key, toParentKey: Key | null, index: number): void;
  /** Update item */
  update(key: Key, item: T): void;
  /** Get item by key */
  getItem(key: Key): T;
}

Collection Components

Item and Section

Building blocks for creating collections with proper accessibility and keyboard navigation.

/**
 * Individual item within a collection
 * @param props - Item content and metadata properties
 * @returns JSX element as collection item
 */
function Item<T>(props: ItemProps<T>): JSX.Element;

/**
 * Section grouping for collection items
 * @param props - Section header and item properties
 * @returns JSX element as collection section
 */
function Section<T>(props: SectionProps<T>): JSX.Element;

interface ItemProps<T = any> {
  /** Unique key for the item */
  key?: Key;
  /** Text content for accessibility and filtering */
  textValue?: string;
  /** Item content */
  children: React.ReactNode;
  /** Item value for forms */
  value?: any;
  /** Whether item is disabled */
  isDisabled?: boolean;
}

interface SectionProps<T = any> {
  /** Unique key for the section */
  key?: Key;
  /** Section title/header */
  title?: React.ReactNode;
  /** Collection items within section */
  children: React.ReactElement<ItemProps<T>>[];
}

Collection

Low-level collection abstraction for building custom collection components.

/**
 * Low-level collection abstraction for custom components
 * @param props - Collection configuration properties
 * @returns Collection instance for rendering
 */
function Collection<T>(props: CollectionProps<T>): JSX.Element;

interface CollectionProps<T> {
  /** Collection items or elements */
  children: React.ReactNode;
  /** Items data array */
  items?: Iterable<T>;
  /** Disabled keys */
  disabledKeys?: Iterable<Key>;
}

Drag & Drop

useDragAndDrop Hook

Hook for adding drag and drop capabilities to collection components.

/**
 * Hook for adding drag and drop capabilities to collections
 * @param options - Drag and drop configuration options
 * @returns DragAndDropHooks with event handlers and utilities
 */
function useDragAndDrop(options: DragAndDropOptions): DragAndDropHooks;

interface DragAndDropOptions {
  /** Get drag data for items */
  getItems?: (keys: Set<Key>) => DragItem[];
  /** Accept drop types */
  acceptedDragTypes?: DropType[];
  /** Should accept drop callback */
  shouldAcceptItemDrop?: (target: ItemDropTarget, types: Set<DropType>) => boolean;
  /** Drop event handler */
  onItemDrop?: (e: ItemDropEvent) => void;
  /** Root drop event handler */
  onRootDrop?: (e: RootDropEvent) => void;
  /** Drag start handler */
  onDragStart?: (e: DragStartEvent) => void;
  /** Drag end handler */
  onDragEnd?: (e: DragEndEvent) => void;
  /** Get drop operation */
  getDropOperation?: (target: DropTarget, types: Set<DropType>, allowedOperations: DropOperation[]) => DropOperation;
}

interface DragAndDropHooks {
  /** Drag props for draggable elements */
  dragProps: DragProps;
  /** Drop props for drop targets */
  dropProps: DropProps;
}

interface DragItem {
  /** Text representation */
  "text/plain"?: string;
  /** HTML representation */
  "text/html"?: string;
  /** Custom data */
  [key: string]: any;
}

type DropType = "text/plain" | "text/html" | "text/uri-list" | string;
type DropOperation = "copy" | "move" | "link" | "cancel";

Drag & Drop Constants

/** Directory drag type constant for file system operations */
const DIRECTORY_DRAG_TYPE: string;

Drag & Drop Types

/** Drop item types */
interface DropItem {
  /** Item kind */
  kind: string;
  /** Item types */
  types: Set<string>;
  /** Get text data */
  getText(type: string): Promise<string>;
}

interface FileDropItem extends DropItem {
  kind: "file";
  /** File name */
  name: string;
  /** File type */
  type: string;
  /** File size */
  size: number;
  /** Get file object */
  getFile(): Promise<File>;
}

interface DirectoryDropItem extends DropItem {
  kind: "directory";
  /** Directory name */
  name: string;
  /** Get directory entries */
  getEntries(): AsyncIterable<DropItem>;
}

interface TextDropItem extends DropItem {
  kind: "text";
}

/** Drop target types */
interface ItemDropTarget {
  /** Target type */
  type: "item";
  /** Target key */
  key: Key;
  /** Drop position */
  dropPosition: DropPosition;
}

interface RootDropTarget {
  /** Target type */
  type: "root";
}

type DropTarget = ItemDropTarget | RootDropTarget;
type DropPosition = "before" | "after" | "on";

/** Drag and drop events */
interface DragStartEvent {
  /** Event type */
  type: "dragstart";
  /** Dragged keys */
  keys: Set<Key>;
}

interface DragEndEvent {
  /** Event type */
  type: "dragend";
  /** Drop operation performed */
  dropOperation: DropOperation;
  /** Whether drop was successful */
  isInternal: boolean;
}

interface ItemDropEvent {
  /** Event type */
  type: "drop";
  /** Drop target */
  target: ItemDropTarget;
  /** Dropped items */
  items: DropItem[];
  /** Drop operation */
  dropOperation: DropOperation;
}

interface RootDropEvent {
  /** Event type */
  type: "drop";
  /** Drop target */
  target: RootDropTarget;
  /** Dropped items */
  items: DropItem[];
  /** Drop operation */
  dropOperation: DropOperation;
}

Usage Examples

List Data Management

import { 
  useListData, 
  ListView, 
  Item, 
  ActionBar,
  ActionGroup 
} from "@adobe/react-spectrum";

function TodoList() {
  const list = useListData({
    initialItems: [
      { id: 1, text: "Buy groceries", completed: false },
      { id: 2, text: "Walk the dog", completed: true },
      { id: 3, text: "Write documentation", completed: false }
    ],
    getKey: (item) => item.id
  });

  const handleAction = (key) => {
    if (key === 'toggle') {
      // Toggle completed state for selected items
      list.selectedKeys.forEach(itemKey => {
        const item = list.getItem(itemKey);
        list.update(itemKey, { ...item, completed: !item.completed });
      });
      list.setSelectedKeys(new Set());
    } else if (key === 'delete') {
      list.removeSelectedItems();
    }
  };

  return (
    <div>
      <ListView
        items={list.items}
        selectedKeys={list.selectedKeys}
        onSelectionChange={list.setSelectedKeys}
        selectionMode="multiple"
      >
        {(item) => (
          <Item key={item.id} textValue={item.text}>
            <Text slot="label" style={{ 
              textDecoration: item.completed ? 'line-through' : 'none' 
            }}>
              {item.text}
            </Text>
            <StatusLight variant={item.completed ? 'positive' : 'notice'}>
              {item.completed ? 'Done' : 'Pending'}
            </StatusLight>
          </Item>
        )}
      </ListView>

      <ActionBar
        isOpen={list.selectedKeys.size > 0}
        selectedItemCount={list.selectedKeys.size}
        onAction={handleAction}
        onClearSelection={() => list.setSelectedKeys(new Set())}
      >
        <Item key="toggle">Toggle Complete</Item>
        <Item key="delete">Delete</Item>
      </ActionBar>
    </div>
  );
}

Async Data with Pagination

function UserList() {
  const list = useAsyncList({
    async load({ cursor, sortDescriptor }) {
      const response = await fetch(`/api/users?cursor=${cursor || ''}&sort=${sortDescriptor?.column}&direction=${sortDescriptor?.direction}`);
      const data = await response.json();
      
      return {
        items: data.users,
        cursor: data.nextCursor
      };
    },
    async sort({ items, sortDescriptor }) {
      // Client-side sorting fallback
      return {
        items: items.sort((a, b) => {
          const aVal = a[sortDescriptor.column];
          const bVal = b[sortDescriptor.column];
          return sortDescriptor.direction === 'ascending' 
            ? aVal.localeCompare(bVal)
            : bVal.localeCompare(aVal);
        })
      };
    }
  });

  return (
    <TableView
      items={list.items}
      loadingState={list.loadingState}
      sortDescriptor={list.sortDescriptor}
      onSortChange={list.sort}
      onLoadMore={list.loadMore}
    >
      <TableHeader>
        <Column key="name" allowsSorting>Name</Column>
        <Column key="email" allowsSorting>Email</Column>
        <Column key="role">Role</Column>
      </TableHeader>
      <TableBody>
        {(user) => (
          <Row key={user.id}>
            <Cell>{user.name}</Cell>
            <Cell>{user.email}</Cell>
            <Cell>{user.role}</Cell>
          </Row>
        )}
      </TableBody>
    </TableView>
  );
}

Drag & Drop File Upload

function FileUploader() {
  const { dragProps, dropProps } = useDragAndDrop({
    acceptedDragTypes: ['application/pdf', 'image/*'],
    onRootDrop: async (e) => {
      const files = e.items.filter(item => item.kind === 'file');
      
      for (const fileItem of files) {
        const file = await fileItem.getFile();
        console.log('Uploading:', file.name);
        // Handle file upload
      }
    }
  });

  return (
    <View
      {...dropProps}
      backgroundColor="gray-50"
      borderStyle="dashed"
      borderWidth="thick"
      borderRadius="medium"
      padding="size-400"
      minHeight="size-2400"
    >
      <Flex direction="column" alignItems="center" justifyContent="center" gap="size-200">
        <Icon size="L" src={UploadIcon} />
        <Heading level={3}>Drop files here</Heading>
        <Text>Support for PDF and image files</Text>
      </Flex>
    </View>
  );
}

Types

Common Data Types

/** Collection item key type */
type Key = string | number;

/** Selection state */
type Selection = "all" | Set<Key>;

/** Sort descriptor for ordering data */
interface SortDescriptor {
  /** Column key to sort by */
  column: Key;
  /** Sort direction */
  direction: "ascending" | "descending";
}

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

/** Selection modes for collections */
type SelectionMode = "none" | "single" | "multiple";