npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@polycyphers/spreadsheet-angular

v1.0.5

Published

Angular wrapper around @polycyphers/spreadsheet-core — virtual-scrolled spreadsheet with Excel I/O, formula engine, sorting, filtering, and selection.

Readme

@polycyphers/spreadsheet-angular

Angular (21+) wrapper exposing the same spreadsheet engine as @polycyphers/spreadsheet — standalone components, signal-driven, OnPush, with 50+ inputs and 20+ outputs.

Commercial. Production use requires a license — unlicensed instances render a visible watermark. A free 30-day trial is available, email-gated, no credit card. Details + pricing at packages.polycyphers.com/spreadsheet.

Install

npm install @polycyphers/spreadsheet-angular

What's included

React parity (from @polycyphers/spreadsheet)

  • Main component ks-spreadsheet (SpreadsheetComponent) — standalone, OnPush, signal-driven. ~50 inputs + ~20 outputs.
  • Sub-components (all standalone, exported for independent use): ks-sheet-tabs, ks-formula-bar, ks-status-bar, ks-filter-dropdown, ks-context-menu, ks-long-text-dialog, ks-pagination, ks-license-watermark, and four cell editors: ks-cell-text-editor, ks-cell-select-editor, ks-cell-checkbox-editor, ks-overflow-cell-content.
  • Virtual scroll via core VirtualScrollEngine.
  • Sorting + filtering — click headers to sort; filter icon opens a per-column dropdown.
  • Column resize — drag the right edge of any header.
  • Inline editing — double-click or F2; text/number/date/select/checkbox editors; Tab/Enter/Arrow navigation; Escape cancels.
  • Formula bar — displays current A1 reference; edits flow back to the cell.
  • ClipboardCtrl+C / Ctrl+V (tab-delimited; respects column types; batched into undo history).
  • Undo / redoCtrl+Z / Ctrl+Y (+ Ctrl+Shift+Z).
  • Selection range with live status-bar metrics (count / sum / avg / min / max).
  • Status bar with density toggle + accent color picker + help tooltip.
  • Sheet tabs with badges, icons, disabled state.
  • Long-text dialog opened via longTextOverflow affordance on columns.
  • Context menu — right-click with configurable items (static array or (ctx) => items).
  • Cell formattingformat descriptor on columns: text, number, currency, percentage, date.
  • Row templates via TemplateRef inputstoolbarLeft, slotBelowHeader, footer, plus summaryRow render function.
  • Features container — inline row creation, hour validation, cost code dialog, calculated fields, dirty tracking, dynamic columns, multi-row selection, position summary, context menu.
  • Plugin systemCorePlugin<T> (identical contract to React). Built-in: createPointagePlugin, createTasksPlugin. Helpers: createPlugin, composePlugins.
  • Excel I/OexportToExcel, importFromExcel, exportToCSV on the component (ExcelJS is a peer dep, lazy-loaded).
  • Pinningpinning: { columns, rows } input with CSS-sticky rendering, or per-column pinned: true flag.
  • Merged cellsmergedCells input accepted by core MergeCalculator (rendering path visible only if cells are anchors).
  • Modeview / edit / create with editability gating.
  • License systemsetLicense(key), isLicensed(), KARAMENT_INTERNAL_KEY, LicenseWatermarkComponent (dormant, enforcement flag off).
  • Imperative API on the componentexportToExcel, importFromExcel, exportToCSV, undo, redo, resetColumnWidths, getData, scrollToRow, selectCell, selectRange, clearSelection, focus, getFormulaEngine. Access via @ViewChild(SpreadsheetComponent).

TOMWEB-specific (not in React package)

  • densityMode (comfortable | compact) + accentColor (blue | purple | teal | amber | rose) with runtime toggle in status bar.
  • customActions — toolbar buttons above the formula bar.
  • dataService input — auto-save pattern; debounced saveCell(change) calls on every cell edit.
  • enablePagination + rowsPerPage — page-based rendering alongside virtual scroll.
  • formulaDialogRequest output — fired when a row asks for the formula builder dialog.
  • pasteDetected output — raw ClipboardEvent for custom paste handling.
  • metricsUpdate output — fires whenever the selection metrics change.
  • rowReorder output — ready for drag-drop reorder wiring.
  • TENDER_FUNCTIONS (SELLING_PRICE, ONP_PERCENT, PROFIT_UP_WEIGHT, PROFIT_WEIGHT, TOTAL_SELLING) — importable or auto-registered via [registerTenderFunctions]="true".
  • CellIdAdapter — converts {sheetId}-{rowIndex}-{columnKey}'SheetName'!A1.
  • TOMWEB_CELL_ACCESSOR — pre-built accessor for the { id, cells: { [key]: { value, … } } } row shape. Pass as [cellAccessor].
  • Config presetsTENDER_RESOURCES_PRESET, TENDER_MATERIALS_PRESET, STANDARD_FORMULAS_PRESET, BOQ_EDITOR_PRESET.
  • DI tokensSPREADSHEET_CELL_ACCESSOR, SPREADSHEET_DATA_SERVICE, SPREADSHEET_FEATURE_DEFAULTS (for app-wide defaults).
  • Dirty trackingfeatures.dirtyTracking: { enabled, onDirtyChange }; dirty row ids flow to status bar + row styling.
  • Calculated fieldsfeatures.calculatedFields: [{ targetColumn, calculate, dependencies, format }].

ExcelJS is optional — only required if you call exportToExcel / importFromExcel.

Usage

Basic (flat row shape — matches React package default)

import { Component } from '@angular/core';
import {
  SpreadsheetComponent,
  SpreadsheetColumn,
  SpreadsheetChange
} from '@polycyphers/spreadsheet-angular';

interface Item { code: string; description: string; qty: number; unit: string; }

@Component({
  selector: 'app-boq',
  standalone: true,
  imports: [SpreadsheetComponent],
  template: `
    <ks-spreadsheet
      [rows]="items"
      [columns]="columns"
      [showStatusBar]="true"
      (cellChange)="onCellChange($event)"
    />
  `
})
export class BoqPage {
  items: Item[] = [{ code: '01.01', description: 'Concrete', qty: 100, unit: 'm³' }];
  columns: SpreadsheetColumn<Item>[] = [
    { key: 'code', label: 'Code', width: 100, editable: true },
    { key: 'description', label: 'Description', width: 300, editable: true },
    { key: 'qty', label: 'Qty', width: 80, type: 'number', editable: true, align: 'right' },
    { key: 'unit', label: 'Unit', width: 80, editable: false }
  ];
  onCellChange(e: SpreadsheetChange<Item>) { /* ... */ }
}

TOMWEB row shape (migrating from app-sam-table-spreadsheet)

import { TOMWEB_CELL_ACCESSOR, TENDER_RESOURCES_PRESET } from '@polycyphers/spreadsheet-angular';

@Component({ /* ... */ })
export class TenderManpowerPage {
  columns = [
    { key: 'ref', label: 'Ref', width: 100, editable: true },
    { key: 'name', label: 'Name', width: 280, editable: true },
    { key: 'rate', label: 'Hourly Rate', width: 120, type: 'number',
      format: { type: 'currency', currency: 'USD', decimals: 2 },
      align: 'right', editable: true }
  ];

  rows: any[] = [
    { id: 1, cells: { ref: { value: 'MP-001' }, name: { value: 'Foreman' }, rate: { value: 35 } } }
  ];
}
<ks-spreadsheet
  [rows]="rows"
  [columns]="columns"
  [cellAccessor]="TOMWEB_CELL_ACCESSOR"
  [densityMode]="'compact'"
  [accentColor]="'blue'"
  [enablePagination]="true"
  [rowsPerPage]="50"
  [registerTenderFunctions]="true"
  (cellChange)="onCellEdit($event)"
  (rowReorder)="onRowReorder($event)"
  (formulaDialogRequest)="openFormulaBuilder($event)" />

Auto-save via dataService

import { SpreadsheetDataService } from '@polycyphers/spreadsheet-angular';

class TenderSaveService implements SpreadsheetDataService<Item> {
  debounceMs = 500;
  async saveCell(change) { await fetch('/api/cell', { method: 'PUT', body: JSON.stringify(change) }); }
}
<ks-spreadsheet [dataService]="saveService" ... />

Tender formula functions

// Option A — auto-register via input
<ks-spreadsheet [registerTenderFunctions]="true" ... />

// Option B — import & register on your own engine
import { registerTenderFunctions, FormulaEngine } from '@polycyphers/spreadsheet-angular';
const engine = new FormulaEngine();
registerTenderFunctions(engine);
engine.setCellValue('C1', '=SELLING_PRICE(A1, B1)');

CellIdAdapter (TOMWEB id ↔ A1)

import { CellIdAdapter } from '@polycyphers/spreadsheet-angular';

const adapter = new CellIdAdapter({
  resolveSheet(ref) {
    const sheet = sheets.find((s) => typeof ref === 'number' ? s.id === ref : s.name === ref);
    if (!sheet) return null;
    return {
      sheetId: sheet.id,
      sheetName: sheet.name,
      columnIndex: (key) => sheet.columns.findIndex((c) => c.key === key),
      columnKey: (idx) => sheet.columns[idx]?.key ?? null
    };
  },
  activeSheetId: () => activeSheet.id
});

adapter.toA1('5-10-qty');  // "'Trade A'!D11"
adapter.fromA1(`'Trade A'!D11`); // "5-10-qty"

Build

npm run build        # ng-packagr → dist/
npm run type-check

Migration checklist (TOMWEB sam-table-spreadsheet → this package)

  1. Replace imports: SamTableSpreadsheetComponentSpreadsheetComponent.
  2. Map [sheets] → render one ks-spreadsheet per sheet OR manage active sheet via sheetTabs + activeSheetTab.
  3. Pass [cellAccessor]="TOMWEB_CELL_ACCESSOR" so the component reads row.cells[key].value.
  4. Rename event bindings: (cellEdit)(cellChange), (rowAction)(rowChange), (sheetChange) shape unchanged.
  5. Replace [config] preset object with discrete inputs ([enablePagination], [rowsPerPage], [densityMode], etc.) OR use the exported *_PRESET constants and spread them.
  6. Set [registerTenderFunctions]="true" if your sheets use SELLING_PRICE / ONP_PERCENT / etc.
  7. Wire (formulaDialogRequest) if you were using the formula builder dialog.
  8. Wire (metricsUpdate) if you were using status-bar metrics outside the component.
  9. If you relied on dataService for auto-save, pass it via [dataService] (same interface).
  10. Test — the core engine is shared with React, so formulas, undo/redo, virtual scroll, clipboard, and selection metrics behave identically.

Architecture

┌────────────────────────────────────────────────────────┐
│  @polycyphers/spreadsheet         (React,   v1.0.11)      │
│  @polycyphers/spreadsheet-angular (Angular, v0.1.0, NEW)  │
│  (future) @polycyphers/spreadsheet-vue                    │
└────────────────────┬───────────────────────────────────┘
                     │  depends on (peer)
                     ▼
┌────────────────────────────────────────────────────────┐
│  @polycyphers/spreadsheet-core                            │
│  Pure TS — no framework, no DOM deps                   │
│  - FormulaEngine (68 built-ins + custom)               │
│  - VirtualScrollEngine / SortEngine / FilterEngine     │
│  - SelectionManager / ClipboardManager / UndoRedo      │
│  - PinningCalculator / MergeCalculator / StatusBar     │
│  - PluginManager                                        │
│  - ExcelIO (optional ExcelJS peer dep)                 │
└────────────────────────────────────────────────────────┘

Known limitations (v0.1.0)

  • Full pinning split layout — columns with pinned: true or within pinning.columns use CSS position: sticky rather than a true split-pane render. Works for up to a few pinned columns.
  • Merged cell renderingmergedCells is accepted and fed to the core MergeCalculator, but visual rowspan/colspan merging isn't implemented yet (non-anchor cells still render).
  • Row-height resize drag handle — not yet exposed; per-row height is accepted but not drag-resizable.
  • Row drag-drop reorder UI — the rowReorder output is wired, but no drag-handle is rendered yet.
  • Multi-range (Ctrl+Click) selection — single range only; non-contiguous selection not supported.
  • Inline row creation UIfeatures.inlineRowCreation config is consumed by applyInlineRowCreation() helper, but the built-in "+ new row" button isn't rendered yet.
  • Cost code dialog — config type is defined; consumer wires the dialog via formulaDialogRequest or a custom TemplateRef.

These are UI-level only — all logic is already in core and available to wire up.