GridSheet

repository·master·Indexed 41 days ago

https://github.com/walkframe/gridsheet

A multi-framework library for spreadsheet-like editing and viewing in web applications, supporting React, Preact, Vue, and Svelte. It includes @gridsheet/engine, a headless spreadsheet model and formula resolution engine capable of synchronous evaluation and asynchronous formula resolution (e.g., =AI()), as well as @gridsheet/functions for extended formula support.

Tokens
75.5K
Snippets
243
Records
316
Agent score
51%

What's inside GridSheet

  1. Overview of @gridsheet/engine

    master
    The @gridsheet/engine package is a headless spreadsheet model and formula resolution engine. It is designed to be independent of any UI framework (zero React/DOM dependency), making it suitable for use in CLI tools, VS Code extensions, CI environments, or backend services. It handles parsing, synchronous evaluation, and asynchronous formula resolution (e.g., =AI(...)).
  2. Overview of GridSheet component hierarchy

    master

    GridSheet is composed of several specialized components:

    • GridSheet: The main orchestrator and container.
    • FormulaBar: Displays/edits formulas; includes FunctionGuide for autocomplete.
    • SearchBar: Provides search/replace functionality (replaces FormulaBar when active).
    • Editor: Manages cell input and validation.
    • Tabular: The core rendering engine, containing:
      • CellStateOverlay: Canvas overlay for selection visuals.
      • HeaderCellLeft/Top: Row and column header components.
      • Cell: Individual cell units.
    • StoreObserver: Monitors state to trigger onChange and onSave events.
    • Menus: ContextMenu (right-click), ColumnMenu (header clicks), and RowMenu (row header clicks).
    • Resizer: Handles dragging to resize columns/rows.
    • ScrollHandle: Triggers autoscroll at edges during drag/select.
    • Emitter: Broadcasts keyboard and focus events.
  3. What is GridSheet

    master

    GridSheet is a framework-agnostic programmable spreadsheet framework for web applications. It provides a high-performance grid engine capable of handling millions of cells through virtualized rendering.

    Key features include:

    • Formula Engine: Supports Excel-like formulas, including ARRAYFORMULA spilling, cross-sheet references, and custom functions.
    • Custom Renderers: Allows rendering complex content like charts, badges, avatars, or inputs inside cells using composable policies.
    • Async Functions: Enables fetching live data directly within cells with built-in loading states and result caching.
    • Framework Bindings: Provides first-class support for React, Vue, Svelte, and Preact.
    • Batteries Included: Comes with keyboard shortcuts, sort & filter, undo/redo, clipboard support, and light/dark theming.
  4. Overview of GridSheet packages

    master

    GridSheet provides spreadsheet-like capabilities across multiple web frameworks. Depending on your project's technology stack, you should use the corresponding core package:

    • React: @gridsheet/react-core
    • Preact: @gridsheet/preact-core
    • Vue: @gridsheet/vue-core
    • Svelte: @gridsheet/svelte-core
  5. Key features of @gridsheet/preact-core example

    master

    The @gridsheet/preact-core example demonstrates several advanced GridSheet capabilities:

    • Multiple Sheet Instances: Running two GridSheet instances that share a single hub for synchronization or shared state.
    • Formula Engine: Support for standard spreadsheet formulas including basic arithmetic (e.g., =SUM(A2:B2)) and cross-sheet references (e.g., =SUM(Sheet1!A2:B3)).
    • Labeler Functionality: Interactive controls to enable or disable decimal labelers.
    • Styling & Themes: Support for cell styling and dark mode themes.
    • Interactive Controls: Integration of UI controls to manipulate sheet behavior.
  6. What is @gridsheet/web?

    master

    The @gridsheet/web package serves as the web/DOM rendering layer for GridSheet. It provides the browser-specific helpers required by framework bindings, including:

    • DOM focus and selection management
    • Text input handling
    • Popup positioning
    • Virtualization
    • Style embedding

    It builds on top of the headless @gridsheet/engine and re-exports its full API. While it is a critical layer for framework bindings like @gridsheet/react-core, it is not intended to be used directly by application developers.

  7. Resolve async formulas in virtualized sheets

    master

    Because GridSheet is virtualized, async formulas in off-screen cells do not fire automatically. To ensure all data is materialized (e.g., before saving or exporting), use the following async resolution methods:

    • resolveAll(): Forces all off-screen async formula cells to fire. This is the mechanism behind the eager prop.
    • waitForPending(): Returns a Promise that resolves once all in-flight async computations are complete.
    • hasPendingCells(): Returns true if any cell is currently awaiting an async result.
    const { sheet } = sheetRef.current;
    
    // Force all off-screen async cells to run
    sheet.resolveAll();
    
    // Wait for them to finish
    await sheet.waitForPending();
    
    // Now you can safely read the data
    const matrix = toValueMatrix(sheet);
  8. Configure formula resolution modes

    master

    The resolution option determines how formula results are handled when reading cells via getCell or toXXX functions:

    ValueDescription
    'RESOLVED'(default) Fully evaluates formulas to a scalar. Range references like =C1:F1 are unwrapped to the top-left scalar value.
    'EVALUATED'Evaluates formulas one level deep but preserves range results as a Sheet object. Use this in renderSheet policy hooks to receive the full range.
    'RAW'Returns the formula string with addresses resolved to display form; does not evaluate.
    'SYSTEM'Returns the raw stored value with no evaluation or transformation.

    Example usage:

    // Default: =C1:F1 → scalar value of C1
    sheet.getCell({ y: 1, x: 2 });
    
    // EVALUATED: =C1:F1 → Sheet object spanning C1:F1
    sheet.getCell({ y: 1, x: 2 }, { resolution: 'EVALUATED' });
    
    // RAW: =C1:F1 → '=C1:F1' string
    sheet.getCell({ y: 1, x: 2 }, { resolution: 'RAW' });
    sheet.getCell({ y: 1, x: 2 });
    
    // EVALUATED: =C1:F1 → Sheet object spanning C1:F1
    sheet.getCell({ y: 1, x: 2 }, { resolution: 'EVALUATED' });
    
    // RAW: =C1:F1 → '=C1:F1' string
    sheet.getCell({ y: 1, x: 2 }, { resolution: 'RAW' });
  9. Understand unsaved-state highlights in GridSheet

    master

    The extension uses color coding in the footer and cell backgrounds to indicate pending changes that have not yet been saved to disk:

    • Changed (amber): Indicates cells you have edited manually that are not yet saved.
    • Evaluated (purple): Indicates formula cells whose computed result is not yet in the file. This applies when gridsheet.viewer.saveEvaluated is enabled. A cell also lights up if a cell it references changes or if an AI function result (=CLAUDE() / =CODEX()) arrives.

    All highlights clear once the file is saved via Ctrl+S.

  10. How pending state propagation works in async grids

    master
    When an asynchronous formula is resolving, GridSheet marks the cell as Pending. This state propagates through the dependency graph: any formula that depends on a Pending cell (for example, a =SUM() formula referencing the async cell) will also enter a Pending state. The dependent formulas will wait and display loading animations until all upstream async cells have settled.
  11. Share a `book` across multiple `GridSheet` instances

    master

    To enable cross-sheet communication and formula references, you must share the same book instance (returned by useSpellbook) across multiple GridSheet components. Each GridSheet component should be assigned a unique sheetName to represent a different sheet within that single book.

    const book = useSpellbook({ policies: { sparkline: new Policy({ mixins: [...] }) } });
    
    <GridSheet book={book} sheetName={sheetName1} initialCells={...} />
    <GridSheet book={book} sheetName={sheetName2} initialCells={...} />
  12. How the Registry and Sheet relationship works

    master

    GridSheet's architecture is built on the relationship between a Registry and Sheet instances.

    • Registry: A cross-cutting instance (accessed via a Book) shared across multiple sheets. It acts as a central hub for Functions, Policies, History (undo/redo), Async Cache, and Event Handlers.
    • Sheet: A specific instance corresponding to a rendered <GridSheet> component. It manages its own ID Matrix, Cell Data, System Metadata, and Sheet State.

    Key Pattern: The Book Wrapper A Book is a thin wrapper { registry: Registry } created using useBook() (React hook) or createBook() (imperative). This allows React to trigger re-renders by replacing the Book reference while the underlying Registry maintains a stable identity.

    Note: If you do not provide an explicit book prop to a <GridSheet>, a new Registry is implicitly created for that single sheet. To share state, functions, or history across multiple sheets, you must create one Book and pass it to all relevant <GridSheet> components.

    // Example of sharing a registry across multiple sheets
    const book = useBook();
    
    return (
      <>
        <GridSheet sheetName="Sheet1" book={book} />
        <GridSheet sheetName="Sheet2" book={book} />
      </>
    );