@one-grid-core/angular
v1.0.0
Published
OneGrid — a feature-rich Angular data grid component with tree data, master/detail, editing, selection, pagination, and virtual scrolling.
Maintainers
Readme
OneGrid
A feature-rich Angular data grid component with tree data, master/detail rows, inline editing, row/cell selection, pagination, and virtual scrolling.
Installation
npm install @one-grid-core/angularPeer dependencies (installed alongside your app):
npm install @angular/cdk lucide-angularRequires Angular 17 or newer.
Usage
Everything in the library is standalone, so import the component directly:
import { OneGridComponent } from '@one-grid-core/angular';
@Component({
standalone: true,
imports: [OneGridComponent],
// ...
})
export class MyComponent {}OneGridModule still works and exports the same components, for apps that are
not standalone yet:
import { OneGridModule } from '@one-grid-core/angular';
@NgModule({
imports: [OneGridModule],
})
export class AppModule {}Both paths render identically — the icons the grid draws travel with the components, so nothing extra needs registering either way.
Use the component:
<one-grid
[rowData]="rows"
[columnDefs]="columnDefs"
[defaultColumnDef]="defaultColumnDef"
(onGridReady)="onGridReady($event)"
/>import { OneColumnDef, OneGridReadyEvent } from '@one-grid-core/angular';
export class AppComponent {
rows = [{ make: 'Toyota', model: 'Celica', price: 35000 }];
columnDefs: OneColumnDef<any>[] = [
{ field: 'make' },
{ field: 'model' },
{ field: 'price' },
];
defaultColumnDef: OneColumnDef<any> = { resizable: true, sortable: true };
onGridReady(event: OneGridReadyEvent) {
// event.api gives programmatic access to the grid
}
}Styles
Import the theme in your global styles.scss:
@use "@one-grid-core/angular/scss/one-grid";The theme is self-contained: it renders with neutral defaults on its own, and automatically picks up your Bootstrap design tokens (--bs-* CSS variables) when Bootstrap is present.
Theming
Every visual value is exposed through a layered token system, so you can customize at the level that fits:
1. Runtime CSS custom properties (recommended)
Set --one-grid-* variables anywhere — they inherit like normal CSS variables. No recompile needed, and they can change at runtime (theme switcher, dark mode).
/* Globally */
:root {
--one-grid-primary: #6d28d9;
--one-grid-cell-height: 34px;
}
/* Dark mode */
.theme-dark {
--one-grid-body-bg: #1e1e1e;
--one-grid-header-bg: #262626;
--one-grid-row-odd-bg: #1e1e1e;
--one-grid-row-even-bg: #242424;
--one-grid-text-color: #e5e5e5;
--one-grid-border-color: #3a3a3a;
--one-grid-input-bg: #2a2a2a;
--one-grid-pagination-bg: #1e1e1e;
--one-grid-pagination-btn-color: #e5e5e5;
}
/* A single compact grid instance */
one-grid.compact {
--one-grid-cell-height: 24px;
--one-grid-header-cell-height: 28px;
--one-grid-font-size: 11px;
}2. Compile-time Sass configuration
Every Sass variable is declared !default, so you can configure the module:
@use "@one-grid-core/angular/scss/one-grid" with (
$cell-height: 36px,
$og-primary: #6d28d9,
$prefix: "bs-" // prefix of your design system's CSS variables
);Legacy @import with pre-defined variables also still works.
Available tokens
| Token (--one-grid-*) | Default | Purpose |
| --- | --- | --- |
| header-bg | var(--bs-tertiary-bg, #f8f9fa) | Header background |
| body-bg | var(--bs-card-bg, #fff) | Body background |
| row-odd-bg / row-even-bg | card / body bg | Zebra striping |
| border-color | var(--bs-border-color, #dee2e6) | All borders and separators |
| text-color | var(--bs-body-color, #212529) | Cell and header text |
| font-size | 12px | Grid font size |
| hover-bg | primary at 8% | Row hover |
| selected-bg | primary at 8% | Selected row |
| cell-selected-bg | primary at 16% | Selected cell |
| cell-selected-flash-bg | primary at 60% | Copy-feedback flash |
| header-cell-height | 34px | Header row height |
| cell-height | 30px | Data row height (density) |
| cell-padding | 0 10px | Cell padding |
| cell-item-space | 8px | Spacing between in-cell items |
| cell-line-height | 1.2 | Text line height |
| row-drag-handle-color | var(--bs-secondary-color, #6c757d) | Row drag handle at rest |
| row-drag-indicator-size | 2px | Thickness of the drop indicator line |
| detail-padding | 8px | Inset around a master/detail row's nested grid. It is height the virtual scroller cannot account for; set it to 0 for a flush detail area. |
| action-btn-size | 20px | Row action buttons, checkboxes, icons |
| action-btn-radius | 4px | Small control corner radius |
| border-radius | 0 | Outer grid corner radius |
| input-bg | var(--bs-input-bg, #fff) | Inline cell editor background |
| pagination-bg / pagination-btn-color / pagination-height | card bg / body color / 40px | Pagination bar |
| primary / success / warning / danger / info | Bootstrap accents | Accent colors |
| on-accent | #fff | Text/icons on accent fills |
| primary-ink | var(--tl-accent-ink, #fff) | Ink on the primary fill |
| primary-subtle-bg / success-subtle-bg / danger-subtle-bg / info-subtle-bg | accents at 8–15% | Subtle state fills |
| drop-valid-bg / drop-invalid-bg | success/danger at 10% | Drag-and-drop targets |
| menu-shadow | var(--bs-box-shadow-lg, …) | Cell option menu elevation |
UX guidance when customizing: keep cell-height ≥ 24px so click targets stay usable, keep text/background pairs at a WCAG contrast of at least 4.5:1, and derive hover/selected states from your primary color at low opacity so they remain distinguishable in both light and dark themes.
Column callbacks
Every callback on a column definition comes in one of two scopes, and the params say which.
Cell-scoped — resolved per row, and all handed the same object:
value, data, node, rowIndex, field, columnDef, api, context,
plus whatever the column declared in cellRendererParams (the grid's own fields
win a name collision).
editable · updatable · disable · hideActionButton · rowDrag ·
cellClass · cellStyle · cellRenderer · valueFormatter
Header-scoped — resolved once per column, when the columns are registered.
A header is one cell for the whole column, so there is no row: columnDef,
field, context.
sortable · resizable · dragable · suppressHeaderMenuButton ·
headerClass · headerStyle
valueGetter is its own shape (OneValueGetterParams): data, rowIndex,
node, context.
Features
- Column definitions with value getters, value formatters, cell renderers and validation
- Column visibility (
hide/api.setColumnVisible) and a JSON-safe layout round-trip (api.getColumnStateSnapshot/api.applyColumnState) - Sorting, including multi-column through the sort model
- Quick filter and a programmatic column filter model with thirteen operators,
including negations, inclusive
inRangeandblank/notBlank - Tree data with expand/collapse and auto group columns
- Row transactions (
api.applyTransaction({ add, addToParentId, update, remove })) — append, patch or drop rows by id without rebinding the dataset; selection and expansion survive, tree-aware - Row grouping (
columnDef.rowGroup, nesting in column order) with aggregation (columnDef.aggFunc: sum/min/max/avg/count/first/last or a custom function over the leaves), regroupable at runtime throughapi.setRowGrouping(fields)— any columns, any nesting order,[]to ungroup, without the grid being re-created - Master/detail nested grids
- Inline cell editing with input validation, including a
selecteditor whose options commit typed values - Full-row editing (
editType="fullRow"): every editable cell of the row opens together, Enter or leaving the row commits, Escape abandons - Undo/redo of committed cell edits (
[undoRedoCellEditing],api.undoCellEditing/redoCellEditing, Ctrl+Z / Ctrl+Y in the grid); a paste or a clear is one step however many cells it touched - Cell ranges, by dragging or from the keyboard: Shift with the arrow, Home, End and page keys stretches a range, a plain arrow or Escape collapses it, Ctrl/Cmd+A takes every cell. Drawn as one outline around the range. It survives a click elsewhere on the page, so a host toolbar button can act on it
- Clipboard in the spreadsheet format (tab-separated, quoted where a value
holds a tab or line break): Ctrl+C copies the range or the focused cell,
Ctrl+V pastes from Excel, Sheets or the grid — one value fills a selected
range — and Delete / Backspace clears the range. Pasted values go through
the editor's conversion; read-only cells and values that do not fit the
column are skipped and the count announced. Each changed cell raises
cellValueChangedwithsource: 'paste'or'delete' - Row and cell selection, header checkbox select-all. From the keyboard, Space toggles the focused row and Shift+Space selects from where the row selection started (Shift+Arrow used to extend the rows; it now stretches the cell range)
- Pinned columns (left/right) and pinned rows (top/bottom)
- Row drag & drop, operable by pointer or keyboard
- Row action buttons and per-cell option menus
- Virtual scrolling (Angular CDK)
- Flex column sizing (
columnDef.flex) against the grid's own viewport; hand-resizing a column takes it out of the flex pool - A default column menu behind the header ⋮ button (
[headerMenu]) — sort, pin, hide — with(headerMenuClick)still emitted for hosts that bring their own - A filter row (
[filterRow]) with an input under every column whosefilterableresolves true, feeding the filter model debounced - Client-side pagination (
[clientSidePagination]+[pageSize]): the grid slices the filtered rows and drives its own bar; the default mode still leaves paging to the host - CSV export (
api.getDataAsCsv/api.exportDataAsCsv), through the same value getter/formatter pipeline the cells render with - Host outputs for sort changes (
sortChanged), committed cell edits (cellValueChanged), focus and range changes (cellFocused/cellRangeChanged), and completed column drags and resizes (columnMoved/columnResized) — also available from code throughapi.addEventListener(type, listener), which returns its unsubscribe - A cell API for everything the keyboard and pointer can do:
getFocusedCell/setFocusedCell,ensureRowVisible/ensureColumnVisible(minimal scroll, clear of pinned columns),getCellRange/setCellRange/clearCellRange/getCellRangeValues,startEditingCell/stopEditing(cancel?)/getEditingCells, andgetCellValue(rowId, field, { formatted })/setCellValue— the last refreshes the cell and raisescellValueChangedwithsource: 'api'
Not implemented
Declared in the public types but inert, and documented as such rather than quietly omitted:
columnDef.valueSetter— never invoked, and its signature could not work;api.setCellValue(rowId, field, value)is the write path
Deliberately out of scope, so nobody goes looking:
- Pivoting — row grouping aggregates down a tree; turning values into columns is a different engine.
- A server-side / infinite row model — the host-driven pagination contract plus the sort and filter models is this grid's server story: page, sort and filter server-side and hand the grid each page. A block cache would duplicate that with more machinery.
- Cell/row spanning — incompatible with fixed-height virtual scrolling as built.
License
MIT
