Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Performance benchmark suite with `BenchmarkRunner`, `measureMultiRun`, `computeStats`, `measureThroughput` utilities
- `npm run benchmark` script for running performance benchmarks
- Baseline metrics for 6 categories across 1K/10K/100K row datasets
- `TextMeasureCache.measureEmHeight()` for font em-height measurement
- `TextMeasureCache.getWrappedLines()` for word-boundary text wrapping with character-level fallback
- `TextMeasureCache.countWrappedLines()` and `measureWrappedHeight()` for wrapped text height computation
- `ColumnDef.wrapText` option to enable word-wrap rendering per column
- Multi-line wrapped text rendering in `CellTextLayer` when `wrapText` is enabled
- E2E Playwright test for word-wrap visual verification (`tests/e2e/word-wrap.test.ts`)
- `LINE_HEIGHT_MULTIPLIER` shared constant (1.2) for consistent line-height across text rendering
- `RowStore` auto/manual height separation: `setAutoHeight()`, `setAutoHeightsBatch()`, `clearAutoHeight()`, `clearAllAutoHeights()`, `isManual()`, `isAuto()`
- `LayoutEngine.setRowHeightsBatch()` for O(n) batch row height updates (vs O(n²) for individual calls)
- `CellTypeRenderer.measureHeight()` optional method for custom cell type height measurement
- `RenderLayer.measureHeights()` optional method for bulk row height measurement by render layers
- `CellTextLayer.measureHeights()` implementation for measuring wrapped text row heights
- `SpreadsheetEngine.setAutoRowHeights()` for batch auto-measured height updates with manual-always-wins priority
- `AutoRowSizeManager` class: orchestrates automatic row height measurement with viewport-first sync strategy and off-screen async measurement via `requestIdleCallback`
- `SpreadsheetEngineConfig.autoRowHeight` option to enable auto row height (`boolean` or `AutoRowSizeConfig` with `batchSize`, `minRowHeight`, `cellPadding`)
- `SpreadsheetEngine.getAutoRowSizeManager()` accessor for the auto row size manager instance
- `AutoRowSizeManager` dirty tracking: `markDirtyRows()`, `markAllDirty()`, `clearDirty()`, `hasDirtyRows`, `isRowDirty()`, `isAllDirty`, `dirtyRowCount`
- `AutoRowSizeManager.startDirtyMeasurement()` for efficient re-measurement of only dirty rows
- Scroll compensation in `setAutoRowHeights()`: adjusts scroll position when row heights change above viewport to prevent visual jumping
- `SpreadsheetEngine.markAutoRowHeightDirty()` and `markAllAutoRowHeightDirty()` public API for triggering dirty re-measurement
- Auto row height integration with `setCell()`: cell value changes mark the row dirty for re-measurement
- Auto row height integration with column resize: resizing a wrap-enabled column triggers full re-measurement
- `ColumnStretchManager` class: distributes available container width across columns with two modes ('all' and 'last')
- `SpreadsheetEngineConfig.stretchColumns` option: `'all'` distributes extra space evenly among stretchable columns, `'last'` gives remaining space to the last visible column
- `LayoutEngine.setColumnWidthsBatch()` for efficient batch column width updates (single recomputation pass)
- Column stretch recalculation on container resize via existing `ResizeObserver`
- Manual column resize exclusion: manually resized columns are excluded from stretch distribution in 'all' mode
- Frozen column handling: frozen columns are excluded from stretch distribution
- `DatePickerOverlay` class: pure-DOM calendar widget for date-type cell editing, positioned below target cell
- `CellEditor` interface and `CellEditorRegistry` for extensible cell editing with type-based editor resolution
- `DatePickerEditor` adapter wrapping `DatePickerOverlay` as a `CellEditor` implementation
- `DateTimeEditor` class: combined date+time picker (calendar + hour/minute spin controls) for `datetime` columns, commits ISO `YYYY-MM-DDTHH:mm` format
- `'datetime'` added to `CellType` union type
- `dateTimePicker` section added to `SpreadsheetLocale` interface (hour, minute, now, ariaLabel) with EN and RU translations
- Date picker opens on double-click, F2, or type-to-edit for columns with `type: 'date'`
- Calendar month/year navigation, day grid with keyboard navigation (arrows, Enter, Escape, Tab)
- Date selection commits value in YYYY-MM-DD format via command system (undo/redo supported)
- Grid scroll and outside click close the date picker
- Today button for quick date selection
- ARIA attributes on date picker overlay (role=dialog, aria-label)
- `ContextMenuItem.submenu` optional field for recursive nested submenus
- Submenu chevron indicator (`▸`) on items with submenus
- Submenu opens on hover (200ms delay) or ArrowRight key, closes on ArrowLeft or Escape
- Nested submenus supported recursively to arbitrary depth
- Empty menu prevention: parent items with all invisible submenu children are hidden
- Keyboard navigation within submenus (ArrowUp/Down), Escape closes one level at a time
- Interactive demo page with sidebar layout showcasing all library features
- Demo sidebar sections: Display (theme, stretch, auto row height), Data (1M rows, progressive load, streaming), Views (grouping, pivot), Import/Export (Excel, print), Collaboration
- Demo feature badges showing active engine configuration
- Demo context menu items with Insert and Format submenus for submenu showcase
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ npm run typecheck # TypeScript check
npm run lint # ESLint
npm run test # Unit tests (vitest)
npm run test:e2e # E2E tests (playwright)
npm run benchmark # Performance benchmarks (6 metrics × 3 datasets)
```

## Project Structure
Expand Down
52 changes: 48 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ Official wrappers for **React**, **Vue 3**, and **Angular**. Embeddable widget b

| Category | Features |
|----------|----------|
| **Rendering** | Canvas 2D multi-layer pipeline, 100K+ rows at 60fps, progressive loading |
| **Editing** | Inline editor, undo/redo (100 steps), clipboard (TSV + HTML), autofill |
| **Data** | Sort (multi-column), filter (14 operators), frozen panes, merged cells |
| **Plugins** | Formulas, conditional formatting, collaboration (OT), Excel I/O, context menu |
| **Rendering** | Canvas 2D multi-layer pipeline, 100K+ rows at 60fps, progressive loading, auto row height with text wrapping |
| **Editing** | Inline editor, date picker, datetime picker, custom cell editors via CellEditorRegistry, undo/redo (100 steps), clipboard (TSV + HTML), autofill |
| **Layout** | Column stretch (`'all'` / `'last'`), frozen panes, auto row sizing, container resize observation |
| **Data** | Sort (multi-column), filter (14 operators), merged cells |
| **Plugins** | Formulas, conditional formatting, collaboration (OT), Excel I/O, context menu with submenus |
| **Theming** | Light/dark built-in, fully customizable via `WitTheme` |
| **Localization** | Built-in English and Russian locales, custom locale packs, runtime switching |
| **Accessibility** | WCAG 2.1 AA: role=grid, aria-live, keyboard-only, print support |
| **Frameworks** | React, Vue 3, Angular, vanilla JS widget (<36KB gzip) |

Expand Down Expand Up @@ -96,6 +98,48 @@ Full documentation with interactive demos at **[spreadsheet.witqq.dev](https://s
- [API Reference](https://spreadsheet.witqq.dev/api/wit-engine/) — WitEngine, types, cell types
- [Migration from Handsontable](https://spreadsheet.witqq.dev/guides/migration-from-handsontable/) — Side-by-side API mapping

## 🌐 Localization

Built-in English and Russian locale packs. Create custom locales for any language:

```tsx
import { WitTable } from '@witqq/spreadsheet-react';
import { ruLocale } from '@witqq/spreadsheet';

<WitTable columns={columns} data={data} locale={ruLocale} />
```

Custom partial locales are merged over English defaults:

```ts
import { resolveLocale } from '@witqq/spreadsheet';

const myLocale = resolveLocale({
contextMenu: { cut: 'Cortar', copy: 'Copiar', paste: 'Pegar' },
formatLocale: 'es-ES',
});
```

Locale covers: context menu labels, date picker, filter panel, accessibility announcements, aggregate labels, print notices, and number/date formatting.

## 🔌 Custom Cell Editors

Register custom overlay editors for specific column types via `CellEditorRegistry`:

```ts
import type { CellEditor } from '@witqq/spreadsheet';

class ColorPickerEditor implements CellEditor {
readonly id = 'color-picker';
// ... implement open(), close(), setTheme(), setLocale(), destroy()
}

const engine = new SpreadsheetEngine({ columns, data });
engine.registerCellEditor(new ColorPickerEditor(), 'color');
```

Built-in editors: `DatePickerEditor` (registered for `type: 'date'`), `DateTimeEditor` (registered for `type: 'datetime'` — calendar + hour/minute controls, commits ISO `YYYY-MM-DDTHH:mm`), `InlineEditor` (textarea fallback). The registry resolves editors by priority — higher priority wins when multiple editors match.

## 🛠 Development

```bash
Expand Down
Loading