React Data Grid

ApexGrid works with React as a web component. The @lit/react package provides a wrapper that bridges Lit events to React's synthetic event system.

Installation

npm install apex-grid @lit/react

@lit/react is the current package name. The older @lit-labs/react was the preview name and is now deprecated.

Create a typed wrapper

Use createComponent from @lit/react to wrap the element. The factory function pattern below preserves the TypeScript generics on the grid's data and columns props.

// apex-grid-wrapper.tsx
import React from 'react'
import { createComponent } from '@lit/react'
import { ApexGrid, ColumnConfiguration } from 'apex-grid'

ApexGrid.register()

export function createApexGrid<T extends object>() {
  return createComponent({
    tagName: 'apex-grid',
    elementClass: ApexGrid<T>,
    react: React,
    events: {
      onRowSelected:      'rowSelected',
      onRowSelecting:     'rowSelecting',
      onCellValueChanged: 'cellValueChanged',
      onFiltered:         'filtered',
      onSorted:           'sorted',
      onPageChanged:      'pageChanged',
    },
  })
}

Usage

Call the factory once per data type at module scope, then use the resulting component like any React component.

// App.tsx
import { createApexGrid } from './apex-grid-wrapper'
import { ColumnConfiguration } from 'apex-grid'

interface User {
  id: number
  name: string
  email: string
}

const UserGrid = createApexGrid<User>()

export default function App() {
  const data: User[] = [
    { id: 1, name: 'Alice', email: 'alice@example.com' },
    { id: 2, name: 'Bob',   email: 'bob@example.com' },
  ]

  const columns: ColumnConfiguration<User>[] = [
    { key: 'id',    headerText: 'ID',    type: 'number' },
    { key: 'name',  headerText: 'Name',  sort: true, filter: true },
    { key: 'email', headerText: 'Email', sort: true },
  ]

  return (
    <UserGrid
      data={data}
      columns={columns}
      onRowSelected={(e) => console.log('selected:', e.detail)}
    />
  )
}

Imperative access

Use a ref to call grid methods directly. Pass the ref to the wrapped component using the standard React ref prop.

import { useRef, useEffect } from 'react'
import { createApexGrid } from './apex-grid-wrapper'
import { ApexGrid, ColumnConfiguration } from 'apex-grid'

interface User {
  id: number
  name: string
  email: string
}

const UserGrid = createApexGrid<User>()

export default function App() {
  const gridRef = useRef<ApexGrid<User>>(null)

  const data: User[] = [
    { id: 1, name: 'Alice', email: 'alice@example.com' },
    { id: 2, name: 'Bob',   email: 'bob@example.com' },
  ]

  const columns: ColumnConfiguration<User>[] = [
    { key: 'id',    headerText: 'ID',    type: 'number' },
    { key: 'name',  headerText: 'Name',  sort: true },
    { key: 'email', headerText: 'Email' },
  ]

  useEffect(() => {
    gridRef.current?.sort({ key: 'name', direction: 'ascending' })
  }, [])

  return (
    <UserGrid
      ref={gridRef}
      data={data}
      columns={columns}
    />
  )
}

Available events

The events map passed to createComponent controls which custom events become React props: add an onX: 'domEventName' entry for each event you want to handle (the example above wires the six most common). ApexGrid dispatches the following events.

Events emitted before an operation (the -ing / -Changing variants) are cancellable: call e.detail's cancellation or e.preventDefault() to stop the operation, and you can modify the expression before it runs.

Suggested propDOM eventCancellableFires
onRowSelectingrowSelectingyesBefore the row selection set changes
onRowSelectedrowSelectedAfter row selection changes
onCellValueChangingcellValueChangingyesBefore a cell value is committed
onCellValueChangedcellValueChangedAfter a cell value is committed
onCellValidationFailedcellValidationFailedA candidate cell value was rejected by validators
onRowEditStartedrowEditStartedA row entered edit mode (row edit mode)
onRowEditEndedrowEditEndedA row left edit mode (committed reports whether it saved)
onSortingsortingyesBefore a sort is applied
onSortedsortedAfter a sort is applied
onFilteringfilteringyesBefore a filter is applied
onFilteredfilteredAfter a filter is applied
onQuickFilterChangingquickFilterChangingyesBefore the quick-filter value is applied
onQuickFilterChangedquickFilterChangedAfter the quick filter is applied
onPageChangingpageChangingyesBefore a page or page-size change
onPageChangedpageChangedAfter a page or page-size change
onColumnPinningcolumnPinningyesBefore a column's pin position changes
onColumnPinnedcolumnPinnedAfter a column's pin position changes
onColumnMovingcolumnMovingyesBefore a column moves
onColumnMovedcolumnMovedAfter a column moves
onRowPinningrowPinningyesBefore a row is pinned, moved between bands, or unpinned
onRowPinnedrowPinnedAfter a row's pin state changes
onRowMovingrowMovingyesBefore a row moves
onRowMovedrowMovedAfter a row moves
onRowExpandingrowExpandingyesBefore the row-expansion set changes
onRowExpandedrowExpandedAfter a row-expansion change
onTreeRowExpandingtreeRowExpandingyesBefore the tree-row expansion set changes
onTreeRowExpandedtreeRowExpandedAfter a tree-row expansion change
onHistoryChangedhistoryChangedAfter the undo / redo stacks change
onStateChangedstateChangedAfter the grid's state changes (debounced)

Each handler receives the CustomEvent; read e.detail for the payload (typed per event, e.g. ApexRowSelectedEvent<T>, SortExpression<T>).