# Spreadsheet

> Build a spreadsheet with real-time formula evaluation — a great exercise to practice custom state management, two-way cell bindings, and reusable table components.

- Difficulty: advanced
- Tags: useState, useEffect, customHook
- Estimated time: 90 minutes
- Tests: 15
- Requires subscription: yes

## Overview

Build a spreadsheet with real-time formula evaluation — a great exercise to practice custom state management, two-way cell bindings, and reusable table components.


## Requirements

- Render a spreadsheet with the title, column headers A through E, and rows numbered 1 through 10.
- Each cell must display its computed value inside an `<output>` element with an `<input>` overlaid for editing.
- Clicking a cell lets the user edit its value; pressing Enter or blurring the input commits the change.
- Formulas starting with `=` must be evaluated:
  - Simple arithmetic: `=1+1` should display `2`.
  - Cell references: if A1 is `5`, then `=A1+3` in another cell should display `8`.
- Clicking a column header toggles selection of that column, giving it the `bg-blue-300` class. Clicking it again deselects it.
- Clicking a row number toggles selection of that row with the same `bg-blue-300` class.
- Selecting a column deselects any selected row, and selecting a row deselects any selected column.
- Pressing Backspace with a column or row selected clears all cells in that column or row.
- Clicking anywhere outside the table deselects any selected column or row.
- A formula bar (labeled `fx`) above the table must mirror the value of the cell currently being edited.
- Cells must start empty (no default values).
- Keep the `data-column` and `data-row` attributes on cells and headers — the tests depend on them.


## Notes

- The starter provides the visual skeleton across `App.tsx`, `Spreadsheet.tsx`, `Cell.tsx`, and `useSpreadsheet.ts` with helper constants and types. Add state and handlers — don't rewrite the markup from scratch.
- Use `useState` for cells, selected column, and selected row. A `useReducer` is not needed for 3 operations.
- Each cell has two overlapping layers: a visible `<output>` (computed value) and an invisible `<input>` (raw formula). Toggle the input with `opacity-0` / `opacity-100` instead of conditional rendering so the element stays mounted.
- Evaluate formulas with `eval`: generate JS `const` declarations from all cell values (e.g., `const A1 = 5;`), wrap them in an IIFE, and evaluate the formula. Re-evaluate **every** cell on any change — cells can reference each other.
- Treat empty cells as `0` when generating formula constants so other cells referencing them don't throw errors.
- Use `useEffect` + `useRef` to keep the latest selected column/row available inside stable keyboard and clipboard event listeners without re-attaching them on every render.
- The `data-column` and `data-row` attributes on `<td>`, `<th>`, and `<input>` elements are used by the tests — keep them.
- `useSpreadsheet.ts` already exports `ROWS`, `COLUMNS`, `getColumnLetter`, `times`, and the `CellData` type. Implement the hook that returns `cells`, `selectedColumn`, `selectedRow`, `updateCell`, `clearColumn`, and `clearRow`.


## Tests

01. renders the app title
02. renders the spreadsheet with column headers A-E and rows 1-10
03. renders cells with initial empty values
04. allows editing a cell and displays the new computed value
05. evaluates a simple formula
06. evaluates a formula with cell references
07. re-evaluates formulas when a referenced cell is cleared
08. selects a column when clicking the column header
09. deselects column when clicking the same header again
10. selects a row when clicking the row number
11. deselects row when clicking the same row number again
12. deselects column when selecting a row and vice-versa
13. clears the column when Backspace is pressed with a column selected
14. clears the row when Backspace is pressed with a row selected
15. deselects column when clicking outside the spreadsheet


