Files
2026-04-08 23:12:46 +02:00

111 lines
3.3 KiB
Markdown

# ngx-pretext-table
Variable-height virtual scrolling for PrimeNG `p-table`, powered by [@chenglou/pretext](https://github.com/chenglou/pretext).
PrimeNG's built-in virtual scroll only supports **fixed row heights** (`virtualScrollItemSize`). This library replaces it with pretext-calculated variable row heights — no DOM measurement, no layout thrashing, O(log n) scroll lookup.
## Install
```bash
npm install ngx-pretext-table @chenglou/pretext
```
## Usage
```typescript
import { Component } from '@angular/core';
import { TableModule } from 'primeng/table';
import {
PretextVirtualScrollDirective,
PretextScrollEvent,
PretextColumnDef,
} from 'ngx-pretext-table';
@Component({
standalone: true,
imports: [TableModule, PretextVirtualScrollDirective],
template: `
<div pretextVirtualScroll
[data]="data"
[columns]="columns"
[font]="'14px system-ui'"
[lineHeight]="20"
[rowPadding]="16"
[scrollHeight]="'500px'"
(visibleRangeChange)="onRange($event)">
<p-table [value]="visibleData" [scrollable]="false">
<ng-template pTemplate="body" let-row let-i="rowIndex">
<tr [style.height.px]="rowHeights[i]">
<td>{{ row.name }}</td>
<td>{{ row.description }}</td>
</tr>
</ng-template>
</p-table>
</div>
`,
})
export class MyComponent {
data = [/* your data */];
// field + available text width (column width minus padding)
columns: PretextColumnDef[] = [
{ field: 'name', width: 184 },
{ field: 'description', width: 384 },
];
visibleData: any[] = [];
rowHeights: number[] = [];
onRange(e: PretextScrollEvent) {
this.visibleData = this.data.slice(e.start, e.end);
this.rowHeights = e.rowHeights;
}
}
```
## API
### `PretextVirtualScrollDirective`
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `data` | `any[]` | required | Full data array |
| `columns` | `PretextColumnDef[]` | required | Column field + text width |
| `font` | `string` | `'14px system-ui'` | CSS font shorthand |
| `lineHeight` | `number` | `20` | Line height in px |
| `rowPadding` | `number` | `16` | Vertical padding per row |
| `minRowHeight` | `number` | `32` | Minimum row height |
| `rowGap` | `number` | `0` | Gap between rows |
| `scrollHeight` | `string` | `'400px'` | Viewport height |
| `bufferRows` | `number` | `5` | Buffer rows above/below |
| Output | Type | Description |
|--------|------|-------------|
| `visibleRangeChange` | `PretextScrollEvent` | Emits when visible range changes |
| Method | Description |
|--------|-------------|
| `scrollToIndex(index, behavior?)` | Scroll to a specific row |
### `PretextRowHeightService`
Low-level service for direct control:
```typescript
const cache = service.prepareRows(data, columns, font);
const heights = service.calculateRowHeights(cache, columns, lineHeight, padding);
const positions = service.buildPositionIndex(heights);
const range = service.findVisibleRange(positions, scrollTop, viewportHeight);
```
## How it works
1. **Prepare** (once) — `@chenglou/pretext` segments text and measures via canvas
2. **Layout** (on resize) — pure arithmetic, ~0.0002ms per cell, no DOM access
3. **Scroll** (continuous) — O(log n) binary search in cumulative position arrays
## License
MIT