Skip to content

KeenMate/web-grid

Repository files navigation

@keenmate/web-grid

A feature-rich, framework-agnostic data grid web component built with TypeScript. Sorting, filtering, pagination, inline editing (8 editor types), cell range selection, clipboard support, row toolbar, context menus, frozen columns, column reorder/resize, fill handle, virtual scroll, dark mode, and full CSS variable theming — all in a Shadow DOM encapsulated <web-grid> element.

What's New in v1.0.3

  • onrowfocus fixes: No longer fires during cell range selection (drag/shift+click). Mouse-triggered row focus now defers to click (mouseup) instead of mousedown; keyboard navigation still fires immediately.
  • Cell selection visual fix: Focused cell outline no longer persists during cell range drag.
  • Toolbar fixes: Tooltip hides when toolbar moves/closes. Selections cleared on toolbar action click. triggerElement in ontoolbarclick detail stays alive (no longer detached by synchronous re-render).
  • Z-index layer system: All z-index values now use CSS custom properties (--wg-z-header, --wg-z-frozen, etc.). Fixes cell selection bleeding through sticky header and frozen header stacking.
  • Logging system: loglevel-based logging with 4 categories (GRID:INIT, GRID:DATA, GRID:UI, GRID:INTERACTION). Enable via window.components['web-grid'].logging.enableLogging().
  • Runtime API: window.components['web-grid'] with version(), config, and logging — matching web-multiselect and web-daterangepicker.

v1.0.2

  • Tooltip positioning in transformed containers: Fixed tooltips appearing at grid's top-left when ancestor has CSS transform. Switched to position: absolute with :host as positioning context.
  • HTML tooltips: New isTooltipHtml column option for rich tooltip content.
  • ontoolbarclick detail: Now includes event (MouseEvent) and triggerElement (HTMLElement) for anchoring popovers to toolbar buttons inside shadow DOM.
  • Tooltip show delay: Reduced default from 400ms to 200ms.

Installation

npm install @keenmate/web-grid

Quick Start

ES Module (recommended)

<script type="module">
  import '@keenmate/web-grid'
</script>

<web-grid id="grid"></web-grid>

<script type="module">
  const grid = document.getElementById('grid')
  grid.items = [
    { id: 1, name: 'Alice', age: 28 },
    { id: 2, name: 'Bob', age: 34 }
  ]
  grid.columns = [
    { field: 'id', title: 'ID', width: '60px' },
    { field: 'name', title: 'Name' },
    { field: 'age', title: 'Age' }
  ]
  grid.sortMode = 'multi'  // Enable multi-column sorting
</script>

UMD (Script Tag)

<script src="https://unpkg.com/@keenmate/web-grid"></script>
<web-grid id="grid"></web-grid>

Features

  • Sorting — Single or multi-column with visual indicators
  • Filtering — Per-column text input filters
  • Pagination — Client-side or server-side, configurable layout and labels
  • Inline Editing — 8 editor types: text, number, checkbox, select, combobox, autocomplete, date, custom
  • Grid Modesread-only, excel, input-matrix — each sets sensible defaults
  • Keyboard Navigation — Spreadsheet-like Arrow/Tab/Home/End/PageUp/PageDown navigation
  • Cell Range Selection — Click+drag or Shift+click to select rectangular ranges
  • Clipboard — Copy/paste TSV data (Excel-compatible), per-column transform callbacks
  • Row Toolbar — Floating, inline, or cell-specific action buttons
  • Context Menu — Right-click menu for cells and column headers (with predefined actions)
  • Keyboard Shortcuts — Per-row and per-range custom shortcuts with help overlay
  • Column Reordering — Drag-to-reorder with optional localStorage persistence
  • Column Resizing — Drag column borders with min/max constraints and persistence
  • Frozen Columns — Stick columns to the left during horizontal scroll
  • Fill Handle — Excel-like autofill by dragging a cell's corner
  • Row Locking — Optimistic locking with external lock management (WebSocket-ready)
  • Row IdentificationidValueMember/idValueCallback for updateRowById, replaceRowById
  • Virtual Scroll — Render only visible rows for large datasets
  • Infinite Scroll — Load-more pattern triggered near bottom of scroll
  • Dark Mode — Auto-detects OS/attribute/class preferences
  • CSS Variable Theming — 120+ --wg-* variables with --base-* fallback for cross-component themes
  • Shadow DOM — Encapsulated styles, no CSS leakage
  • Summary Bar — Configurable summary content at any corner position
  • ValidationbeforeCommitCallback with tooltip display (custom HTML supported)
  • Row Focus Trackingonrowfocus for master/detail patterns
  • i18n — All UI labels customizable via labels property
  • TypeScript — Full type definitions exported

Architecture

WebGrid uses an action pipeline pattern for user interactions. DOM events are captured by the ActionPipelineAdapter, which translates them into typed action objects (34 action types). These actions flow through a pipeline that dispatches them to specialized executors (16 executors covering focus, editing, navigation, selection, clipboard, dropdown, fill-handle, etc.). Executors can emit child actions for composability — e.g., committing an edit can trigger a focus-move.

DOM Event → Adapter → Action → Pipeline → Executor → State Change → Render

This architecture separates mode detection (the adapter decides what action a click means based on grid mode, edit trigger, cell state) from business logic (executors handle how to focus, edit, select). It also allows incremental migration from legacy event handlers.

Properties

Core Data

Property Type Default Description
items T[] [] Data array to display
columns Column<T>[] [] Column definitions (see Column Definition)

Display

Property Type Default Description
isFilterable boolean false Show per-column filter inputs
isStriped boolean false Alternate row background colors
isHoverable boolean false Highlight row on hover
isRowNumbersVisible boolean false Show row number column
isStickyRowNumbers boolean false Freeze row number column during horizontal scroll
freezeColumns number 0 Freeze first N columns (applied after visual reorder from isFrozen)
mode GridMode Grid mode: 'read-only', 'excel', 'input-matrix' (see Grid Modes)
focusedRowIndex number | null null Currently focused row index (readable/writable)

Editing

Property Type Default Description
isEditable boolean false Enable cell editing
editTrigger EditTrigger 'dblclick' How editing starts: 'click', 'dblclick', 'button', 'always', 'navigate'
editStartSelection EditStartSelection 'selectAll' Cursor position when entering edit via navigate: 'mousePosition', 'selectAll', 'cursorAtStart', 'cursorAtEnd'
dropdownToggleVisibility ToggleVisibility 'on-focus' When to show dropdown toggle button: 'always', 'on-focus'
shouldShowDropdownOnFocus boolean false Auto-open dropdown when cell is focused
shouldOpenDropdownOnEnter boolean false Enter key opens dropdown (true) or moves to next row (false)
isCheckboxAlwaysEditable boolean false Checkboxes are always clickable, even in navigate mode

Sorting

Property Type Default Description
sort SortState[] [] Current sort state (set for initial/server-side sort)
sortMode SortMode 'none' Sort mode: 'none' (disabled), 'single', 'multi'

Pagination

Property Type Default Description
isPageable boolean false Enable pagination
pageSize number 10 Rows per page
pageSizes number[] Available page sizes for selector (e.g., [10, 25, 50, 100])
paginationMode 'client' | 'server' 'client' 'client' = grid slices items; 'server' = items are already current page
currentPage number 1 Current page (1-based)
totalItems number | null null Total item count for server-side pagination
showPagination boolean | 'auto' 'auto' true = always show, false = never, 'auto' = hide when ≤1 page
paginationPosition string 'bottom-center' Position(s): 'bottom-center', 'top-right|bottom-right' for multiple
paginationLabelsCallback PaginationLabelsCallback Callback to customize/translate pagination text
paginationLayout string Element order: 'pageSize|previous|pageInfo|next' or 'first|previous|pageInfo|next|last'

Row Toolbar

Property Type Default Description
isRowToolbarVisible boolean false Show row action toolbar
rowToolbar RowToolbarConfig<T>[] [] Toolbar items (predefined strings or custom objects)
toolbarTrigger 'hover' | 'click' | 'button' 'hover' How to show toolbar
toolbarPosition ToolbarPosition 'auto' Preferred position: 'auto', 'left', 'right', 'top', 'inline'
toolbarVerticalAlign 'top' | 'center' | 'bottom' 'bottom' Vertical alignment for left/right position
toolbarHorizontalAlign 'start' | 'center' | 'end' | 'cursor' 'center' Horizontal alignment for top position
toolbarColumn string | number Column to pin toolbar over (field name or index) for 'top' position
toolbarFollowsCursor boolean false Toolbar follows mouse cursor horizontally
cellToolbar (row, rowIndex, field, colIndex) => RowToolbarConfig[] | undefined Cell-specific toolbar items
cellToolbarOffset number | string 0.2 Horizontal offset: 01 as fraction of cell width, or CSS length (e.g., '2rem')
toolbarBtnMinWidth string Min-width for toolbar buttons (CSS value). Overrides --wg-toolbar-btn-min-width
inlineActionsTitle string 'Actions' Header title for inline actions column (when toolbarPosition='inline'). Set to '' for no title

Deprecated aliases: toolbarAlign → use toolbarVerticalAlign; toolbarTopPosition → use toolbarHorizontalAlign.

Context Menu

Property Type Default Description
contextMenu ContextMenuItem<T>[] Cell/row context menu items
contextMenuXOffset number 0 Horizontal offset from click position
contextMenuYOffset number 4 Vertical offset from click position
headerContextMenu HeaderMenuConfig<T>[] Header context menu items (predefined strings or custom objects)

Cell Selection

Property Type Default Description
cellSelectionMode CellSelectionMode 'click' How to select cell ranges: 'disabled', 'click', 'shift'
shouldCopyWithHeaders boolean false Include column headers when copying cell selection to clipboard

Keyboard Shortcuts

Property Type Default Description
rowShortcuts RowShortcut<T>[] Row-level shortcut definitions
rangeShortcuts RangeShortcut<T>[] Multi-row/range shortcut definitions
isShortcutsHelpVisible boolean false Show keyboard shortcuts help icon
shortcutsHelpPosition 'top-right' | 'top-left' 'top-right' Help icon position
shortcutsHelpContentCallback () => string Custom HTML content to display alongside shortcuts list

Column Management

Property Type Default Description
gridName string | null null Unique name for localStorage persistence
shouldPersistColumnWidths boolean false Persist column widths to localStorage (requires gridName)
isColumnReorderAllowed boolean false Enable drag-to-reorder columns
shouldPersistColumnOrder boolean false Persist column order to localStorage (requires gridName)

Fill Handle

Property Type Default Description
fillDragCallback (detail: FillDragDetail) => boolean | void Called when fill handle is dragged. Return false to cancel.

Row Identification

Property Type Default Description
idValueMember keyof T Property name containing the row's unique ID
idValueCallback (row: T) => unknown Callback returning the row's unique ID

Row Locking

Property Type Default Description
rowLocking RowLockingOptions<T> Row locking configuration (see RowLockingOptions)

RowLockingOptions

Field Type Description
lockedMember keyof T Property with boolean lock state
lockInfoMember keyof T Property with RowLockInfo object
isLockedCallback (row, rowIndex) => boolean Callback-based lock check
getLockInfoCallback (row, rowIndex) => RowLockInfo | null Callback-based lock info
lockedEditBehavior 'block' | 'allow' | 'callback' Edit behavior for locked rows (default: 'block')
canEditLockedCallback (row, lockInfo) => boolean Per-row edit decision (when 'callback')
lockTooltipCallback (lockInfo, row) => string | null Custom tooltip HTML for locked rows

Scroll

Property Type Default Description
isScrollable boolean false Enable scroll container with max-height
scrollMaxHeight string '100vh' Max-height when isScrollable is true
tableBorderOnly boolean false Border only around table, not pagination/toolbar

Virtual Scroll

Property Type Default Description
isVirtualScrollEnabled boolean false Enable virtual scroll
virtualScrollThreshold number 100 Auto-enable when items ≥ threshold
virtualScrollRowHeight number 38 Fixed row height in pixels
virtualScrollBuffer number 10 Extra rows rendered above/below viewport

Infinite Scroll

Property Type Default Description
isInfiniteScrollEnabled boolean false Enable infinite scroll
infiniteScrollThreshold number 100 Distance from bottom (px) to trigger load
hasMoreItems boolean true Set to false when no more data

Tooltip

Property Type Default Description
tooltipShowDelay number 200 Delay in ms before showing tooltip
tooltipHideDelay number 100 Delay in ms before hiding tooltip

Summary

Property Type Default Description
summaryPosition string Position(s): 'bottom-left', 'top-right|bottom-right', etc.
summaryContentCallback SummaryContentCallback<T> Callback returning HTML content for the summary bar
isSummaryInline boolean true Share row with pagination when in same area
summaryMetadata unknown Server-provided metadata passed to summaryContentCallback

Styling

Property Type Default Description
customStylesCallback () => string Returns CSS string injected into shadow DOM
rowClassCallback (row, rowIndex) => string | null Dynamic CSS class for rows
labels Partial<GridLabels> Override UI labels for i18n (see i18n)
validationTooltipCallback (context) => string | null Custom HTML for validation error tooltip (grid-level default)
invalidCells CellValidationState[] [] External validation state for cells

Column Definition

The Column<T> interface defines how each column renders, edits, and behaves.

Basic

Field Type Description
field keyof T | string Data field name (required)
title string Column header title
headerInfo string Info tooltip shown next to header (displays ⓘ icon)
width string Column width (e.g., '100px', '20%')
minWidth string Minimum width during resize
maxWidth string Maximum width during resize

Display

Field Type Description
horizontalAlign 'left' | 'center' | 'right' | 'justify' Cell horizontal alignment (default: 'left')
verticalAlign 'top' | 'middle' | 'bottom' Cell vertical alignment (default: 'middle')
headerHorizontalAlign 'left' | 'center' | 'right' | 'justify' Header horizontal alignment (defaults to horizontalAlign)
headerVerticalAlign 'top' | 'middle' | 'bottom' Header vertical alignment (defaults to verticalAlign)
textOverflow 'wrap' | 'ellipsis' Text overflow behavior
maxLines number Maximum visible lines when textOverflow is 'wrap' (CSS line-clamp)
cellClass string Static CSS class applied to all cells in this column
cellClassCallback (value, row) => string | null Dynamic CSS class based on value/row

Content

Field Type Description
formatCallback (value, row) => string Format value for display (text only)
templateCallback (row) => string Custom cell HTML string
renderCallback (row, element) => void Imperative cell rendering (receives the <td> element)

Editing

Field Type Description
isEditable boolean Enable editing for this column
editor EditorType Editor type: 'text', 'number', 'checkbox', 'select', 'combobox', 'autocomplete', 'date', 'custom'
editTrigger EditTrigger Per-column override for edit trigger
editorOptions EditorOptions<T> Editor-specific configuration (see Editor Types)
dropdownToggleVisibility ToggleVisibility Per-column override: 'always' or 'on-focus'
shouldOpenDropdownOnEnter boolean Per-column override: Enter opens dropdown or moves down
cellEditCallback (context: CustomEditorContext<T>) => void Handler for 'custom' editor type
isEditButtonVisible boolean Show an edit button in the cell

Validation

Field Type Description
beforeCommitCallback (context: BeforeCommitContext<T>) => BeforeCommitResult | Promise<...> Validates and optionally transforms value before commit
validateCallback (value, row) => string | null | Promise<...> Deprecated — use beforeCommitCallback instead
validationTooltipCallback (context) => string | null Custom HTML for this column's validation error tooltip

beforeCommitCallback receives { value, oldValue, row, rowIndex, field } and can return:

  • { valid: true } or { valid: true, transformedValue: ... } — accept (optionally transform)
  • { valid: false, message: 'Error text' } — reject with message
  • true / null / undefined — accept
  • false — reject (no message)
  • string — reject with that string as error message

Tooltip

Field Type Description
tooltipMember string Property name in row data containing tooltip text
tooltipCallback (value, row) => string | null Dynamic tooltip (takes priority over tooltipMember)

Clipboard

Field Type Description
beforeCopyCallback (value, row) => string Transform value before copying to clipboard
beforePasteCallback (value, row) => unknown Process pasted value before applying to cell

Layout

Field Type Description
isFrozen boolean Column sticks to left side during horizontal scroll
isResizable boolean Allow column width to be changed by dragging (default: true)
isMovable boolean Allow column to be reordered by dragging (default: true)
isHidden boolean Column is not rendered but kept in array for visibility toggling
isSortable boolean Enable sorting for this column (when grid has sortMode enabled)
isFilterable boolean Enable filtering for this column

Fill

Field Type Description
fillDirection FillDirection Override grid-level fill direction for this column: 'vertical' or 'all'

Editor Types

Type Description
text Text input
number Numeric input with step/min/max
checkbox Boolean toggle
select Dropdown list
combobox Filterable dropdown
autocomplete Async search dropdown
date Date picker
custom Consumer-controlled via cellEditCallback

Text Options

Option Type Default Description
maxLength number Maximum character count
placeholder string Placeholder text
pattern string Input pattern attribute
inputMode 'text' | 'numeric' | 'email' | 'tel' | 'url' Virtual keyboard hint
editStartSelection EditStartSelection 'selectAll' Cursor position when entering edit

Number Options

Option Type Default Description
min number Minimum value
max number Maximum value
step number Step increment
decimalPlaces number Fixed decimal places
allowNegative boolean Allow negative values

Checkbox Options

Option Type Default Description
trueValue unknown true Value to store when checked
falseValue unknown false Value to store when unchecked

Date Options

Option Type Default Description
minDate Date | string Minimum selectable date
maxDate Date | string Maximum selectable date
dateFormat string Display format: 'YYYY-MM-DD', 'DD.MM.YYYY', etc.
outputFormat DateOutputFormat What to store: 'date' (Date object), 'iso' (ISO string), 'timestamp'

Shared Dropdown Options (select, combobox, autocomplete)

These options are shared across select, combobox, and autocomplete editor types.

Providing Options

Option Type Description
options EditorOption[] Static array of options: { value, label, ...extra }
loadOptions (row, field) => Promise<EditorOption[]> Async option loader
optionsLoadTrigger OptionsLoadTrigger When to call loadOptions: 'immediate', 'oneditstart' (default), 'ondropdownopen'

Display Mapping

By default, options use value and label properties. Override with member strings or callback functions:

Member Property Callback Alternative Description
valueMember getValueCallback Property/callback for the option value
displayMember getDisplayCallback Property/callback for display text
searchMember getSearchCallback Property/callback for searchable text (falls back to display)
iconMember getIconCallback Property/callback for icon/emoji
subtitleMember getSubtitleCallback Property/callback for subtitle/description
disabledMember getDisabledCallback Property/callback for disabled state
groupMember getGroupCallback Property/callback for grouping options

Rendering & Behavior

Option Type Description
renderOptionCallback (option, context) => string Custom HTML for each option. context has { index, isHighlighted, isSelected, isDisabled }
onselect (option, row) => void Fires when an option is selected
allowEmpty boolean Allow null/empty selection
emptyLabel string Label for empty option (default: '-- Select --')
noOptionsText string Override "No options" message
searchingText string Override "Searching..." message
dropdownMinWidth string Minimum width for dropdown (e.g., '300px')
placeholder string Placeholder text (combobox/autocomplete)

Autocomplete-Specific Options

Option Type Default Description
searchCallback (query, row, signal?) => Promise<EditorOption[]> Async search function (receives AbortSignal for cancellation)
initialOptions EditorOption[] Options shown before user starts typing
minSearchLength number 1 Minimum characters before triggering search
debounceMs number 300 Debounce delay for search calls
multiple boolean false Allow multiple selections
maxSelections number Maximum items when multiple is true

Custom Editor

Set editor: 'custom' and provide a cellEditCallback on the column:

{
  field: 'color',
  editor: 'custom',
  cellEditCallback: ({ value, row, rowIndex, field, commit, cancel }) => {
    // Open your own UI (modal, popover, etc.)
    // Call commit(newValue) to save, cancel() to discard
  }
}

Grid Modes

The mode property sets sensible defaults for common use cases:

Mode isEditable editTrigger cellSelectionMode dropdownToggleVisibility shouldShowDropdownOnFocus
'read-only' false 'click' 'on-focus' false
'excel' true 'navigate' 'click' 'always' false
'input-matrix' true 'always' 'shift' 'always' true
  • read-only — No editing. Click to select cells. Useful for display grids with copy support.
  • excel — Navigate cells with arrows, type to start editing, Escape to cancel. Click+drag for cell selection.
  • input-matrix — All cells are always in edit mode. Shift+click for cell selection. Tab to navigate between fields.

Setting mode applies these defaults, but you can override individual properties afterward.

Row Toolbar

Basic Setup

grid.isRowToolbarVisible = true
grid.rowToolbar = ['add', 'delete', 'duplicate', 'moveUp', 'moveDown']

Predefined Items

String Action
'add' Insert a new row after the current row
'delete' Delete the current row
'duplicate' Duplicate the current row
'moveUp' Move row up
'moveDown' Move row down

Custom Items

grid.rowToolbar = [
  'add',
  {
    id: 'edit',
    icon: '✏️',
    title: 'Edit Record',
    label: 'Edit',          // Optional text label next to icon
    row: 1,                  // Row in multi-row toolbar (1 = closest to grid row)
    group: 1,                // Group number for visual dividers
    danger: false,
    disabled: (row, rowIndex) => row.locked,
    hidden: (row, rowIndex) => row.archived,
    tooltip: { description: 'Open editor', shortcut: 'Enter' },
    onclick: ({ row, rowIndex }) => { /* ... */ }
  }
]

Positioning

Property Values Description
toolbarPosition 'auto', 'left', 'right', 'top', 'inline' Where the toolbar appears relative to the row
toolbarVerticalAlign 'top', 'center', 'bottom' Vertical alignment for left/right positions
toolbarHorizontalAlign 'start', 'center', 'end', 'cursor' Horizontal alignment for top position
toolbarFollowsCursor boolean Toolbar tracks mouse position horizontally
toolbarColumn string | number Pin toolbar above a specific column (for top position)

Cell-Specific Toolbar

grid.cellToolbar = (row, rowIndex, field, colIndex) => {
  if (field === 'status') {
    return [
      { id: 'approve', icon: '✅', title: 'Approve', onclick: ({ row }) => approve(row) }
    ]
  }
  return undefined  // No cell-specific toolbar
}

Context Menu

Cell Context Menu

grid.contextMenu = [
  {
    id: 'view',
    label: 'View Details',                         // or (context) => `View ${context.row.name}`
    icon: '👁️',                                    // or (context) => context.row.active ? '🟢' : '🔴'
    shortcut: 'Enter',                             // Display-only shortcut hint
    visible: (context) => true,                    // or static boolean
    disabled: (context) => context.row.locked,     // or static boolean
    danger: false,
    dividerBefore: false,
    onclick: (context) => { /* context: { row, rowIndex, colIndex, column, cellValue } */ }
  }
]

Header Context Menu

grid.headerContextMenu = [
  'sortAsc',                    // Predefined: sort ascending
  'sortDesc',                   // Predefined: sort descending
  'clearSort',                  // Predefined: clear sort
  'hideColumn',                 // Predefined: hide this column
  'freezeColumn',               // Predefined: freeze/unfreeze column
  'unfreezeColumn',
  'columnVisibility',           // Predefined: submenu to toggle column visibility
  {
    id: 'custom',
    label: 'Custom Action',
    icon: '⚙️',
    children: [...],             // Static submenu items
    submenu: (context) => [...], // Dynamic submenu items
    onclick: (context) => { /* context: { column, field, columnIndex, sortDirection, isFrozen, allColumns, labels } */ }
  }
]

Callbacks

These are callback properties set on the grid element, not DOM events. Use the naming convention: on* callbacks are fire-and-forget notifications; *Callback properties return a value that affects behavior (see column-level callbacks above).

Callback Signature Description
onrowchange (detail: RowChangeDetail<T>) => void Cell value changed. Detail includes row, draftRow, rowIndex, field, oldValue, newValue, isValid, validationError
onroweditstart (detail: { row, rowIndex, field }) => void Editing started on a cell
onroweditcancel (detail: { row, rowIndex, field }) => void Edit was cancelled (Escape)
onvalidationerror (detail: { row, rowIndex, field, error }) => void Validation failed on commit
ontoolbarclick (detail: ToolbarClickDetail<T>) => void Toolbar button clicked. Detail: { item, rowIndex, row, event, triggerElement }
onrowaction (detail: { action, rowIndex, row }) => void Legacy — use ontoolbarclick
oncontextmenuopen (context: ContextMenuContext<T>) => void Cell context menu opened
onheadercontextmenuopen (context: HeaderMenuContext<T>) => void Header context menu opened
ondatarequest (detail: DataRequestDetail) => void Sort/page/pageSize changed. Detail: { sort, page, pageSize, trigger, mode, skip }
onrowdelete (detail: { rowIndex, row }) => void Ctrl+Delete pressed on a row. Also dispatched as a DOM CustomEvent
onrowfocus (detail: RowFocusDetail<T>) => void Different row was focused. Detail: { rowIndex, row, previousRowIndex }
onrowlockchange (detail: RowLockChangeDetail<T>) => void Row lock state changed
oncolumnresize (detail: ColumnResizeDetail) => void Column resized. Detail: { field, oldWidth, newWidth, allWidths }
oncolumnreorder (detail: ColumnReorderDetail) => void Column reordered. Detail: { field, fromIndex, toIndex, allOrder }
oncellselectionchange (detail: CellSelectionChangeDetail) => void Cell selection changed. Detail: { range, cellCount }

Note: Only rowdelete is also dispatched as a DOM CustomEvent. All other callbacks are property-based only.

Public Methods

Focus & Editing

Method Description
focusCell(rowIndex, colIndex) Programmatically focus a cell
startEditing(rowIndex, colIndex) Programmatically start editing a cell
openCustomEditor(rowIndex, colIndex) Open the custom editor for a cell with editor: 'custom'

Draft Management

Method Returns Description
getRowDraft(rowIndex) T | undefined Get the draft (uncommitted changes) for a row
hasRowDraft(rowIndex) boolean Check if a row has uncommitted changes
discardRowDraft(rowIndex) void Discard all draft changes for a row
getDraftRowIndices() number[] Get indices of all rows with drafts
discardAllDrafts() void Discard all draft changes across all rows

Validation

Method Returns Description
isCellInvalid(rowIndex, field) boolean Check if a cell has a validation error
getCellValidationError(rowIndex, field) string | null Get the validation error message for a cell
canEditCell(rowIndex, field) boolean Check if a cell can be edited (considers row locking, column editability)

Row Identification

Method Returns Description
getRowId(row) unknown | undefined Get the ID of a row using idValueMember/idValueCallback
findRowById(id) { row, index } | null Find a row by its ID

Row Updates

Method Returns Description
updateRowById(id, newData) boolean Merge partial data into a row (for WebSocket/live updates)
replaceRowById(id, newRow) boolean Replace an entire row by ID

Row Locking

Method Returns Description
isRowLocked(rowOrId) boolean Check if a row is locked
getRowLockInfo(rowOrId) RowLockInfo | null Get lock information for a row
lockRowById(id, lockerInfo?) boolean Lock a row externally (e.g., from WebSocket message)
unlockRowById(id) boolean Unlock an externally locked row
getExternalLocks() Map<unknown, RowLockInfo> Get all external locks
clearExternalLocks() void Remove all external locks

Row Selection

Method Returns Description
selectRow(rowIndex, mode?) void Select a row. Mode: 'replace' (default), 'toggle', 'range'
selectRowRange(fromIndex, toIndex) void Select a range of rows
clearSelection() void Clear row selection
isRowSelected(rowIndex) boolean Check if a row is selected
getSelectedRowsData() T[] Get data for all selected rows
copySelectedRowsToClipboard() Promise<boolean> Copy selected rows as TSV to clipboard

Cell Selection

Method Returns Description
selectCellRange(range) void Select a cell range programmatically
clearCellSelection() void Clear cell selection
getSelectedCells() Array<{ row, rowIndex, colIndex, field, value }> Get data for all selected cells
copyCellSelectionToClipboard() Promise<boolean> Copy selected cells as TSV to clipboard

Column Width

Method Returns Description
setColumnWidth(field, width) void Set width of a single column
setColumnWidths(widths) void Set widths for multiple columns. widths: ColumnWidthState[]
getColumnWidthsState() ColumnWidthState[] Get current widths of all columns

Column Order

Method Returns Description
setColumnOrder(order) void Set column order. order: ColumnOrderState[]
getColumnOrderState() ColumnOrderState[] Get current column order

Styling

CSS Variable Architecture

WebGrid uses a two-level CSS variable system:

web-grid {
  /* Override component variables directly */
  --wg-accent-color: #10b981;
  --wg-header-bg: #f5f5f5;
}

/* Or set base variables for all KeenMate components */
:root {
  --base-accent-color: #10b981;
  --base-layer-1: #ffffff;
}

Each --wg-* variable falls back to a --base-* variable, then to a hardcoded default:

:host {
  --wg-accent-color: var(--base-accent-color, #0078d4);
}

This means:

  1. Setting --base-accent-color on :root themes all KeenMate components at once
  2. Setting --wg-accent-color on web-grid overrides just the grid
  3. Without either, the component uses its built-in defaults

Key Variables

Variable Description
--wg-accent-color Primary accent color
--wg-text-color-1 Primary text color
--wg-surface-1 Background color
--wg-surface-2 Alternate row/header background
--wg-border-color Border color

Component Variables Manifest

A machine-readable manifest documenting all CSS variables is included in the package:

import manifest from '@keenmate/web-grid/manifest'

console.log(manifest.prefix)              // "wg"
console.log(manifest.baseVariables)       // 37 --base-* variables consumed
console.log(manifest.componentVariables)  // 178 --wg-* variables exposed

The manifest follows the component-variables schema and contains:

  • baseVariables — Theme variables (--base-*) the component consumes from @keenmate/theme-designer
  • componentVariables — Component-specific variables (--wg-*) with category and usage descriptions

Dark Mode

Dark mode is triggered automatically by:

  • OS preference: @media (prefers-color-scheme: dark)
  • Attribute: data-theme="dark" on any ancestor or host element
  • Bootstrap: data-bs-theme="dark" on any ancestor or host element
  • Class: .dark on any ancestor (Tailwind CSS)

To force light mode when the OS is in dark mode, set one of:

  • Attribute: data-theme="light" on any ancestor or host element
  • Bootstrap: data-bs-theme="light" on any ancestor or host element
  • Class: .light on any ancestor (Tailwind CSS)

This is useful when your app manages its own theme (e.g. via a toggle) and needs to override the OS preference. The light mode selectors restore the --base-* fallback chain so theme-designer values are respected.

Ancestor detection uses :host-context() to cross shadow DOM boundaries (Chrome 88+, Firefox 128+, Safari 15.4+).

Dynamic Cell & Row Styling

// Per-cell styling via column callback
grid.columns = [{
  field: 'salary',
  cellClass: 'salary-cell',                                    // Static class
  cellClassCallback: (value, row) => value > 90000 ? 'high-value' : null  // Dynamic class
}]

// Per-row styling
grid.rowClassCallback = (row, index) => row.status === 'inactive' ? 'row-inactive' : null

// Inject custom CSS into shadow DOM
grid.customStylesCallback = () => `
  .high-value { background: #d1fae5 !important; }
  .row-inactive { opacity: 0.5; }
`

i18n

All UI labels can be customized via the labels property:

grid.labels = {
  rowActions: 'Actions',
  keyboardShortcuts: 'Keyboard shortcuts',
  paginationFirst: 'First',
  paginationPrevious: 'Previous',
  paginationNext: 'Next',
  paginationLast: 'Last',
  paginationPageInfo: 'Page {current} of {total}',
  paginationItemCount: '{count} items',
  paginationPerPage: 'per page',
  dropdownNoOptions: 'No options',
  dropdownSearching: 'Searching...',
  contextMenu: {
    sortAsc: 'Sort Ascending',
    sortDesc: 'Sort Descending',
    clearSort: 'Clear Sort',
    hideColumn: 'Hide Column',
    freezeColumn: 'Freeze Column',
    unfreezeColumn: 'Unfreeze Column',
    columnVisibility: 'Column Visibility',
    showAll: 'Show all'
  }
}

Runtime API

The component registers itself at window.components['web-grid'] for runtime introspection:

// Check version
window.components['web-grid'].version()  // "1.0.3"

// Package info
window.components['web-grid'].config  // { name, version, author, license, repository, homepage }

// Logging (silent by default)
window.components['web-grid'].logging.enableLogging()         // Enable all at debug level
window.components['web-grid'].logging.setLogLevel('info')     // Set level: trace|debug|info|warn|error|silent
window.components['web-grid'].logging.disableLogging()        // Back to silent
window.components['web-grid'].logging.setCategoryLevel('GRID:UI', 'debug')  // Per-category
window.components['web-grid'].logging.getCategories()         // ['GRID:INIT', 'GRID:DATA', 'GRID:UI', 'GRID:INTERACTION']

Browser Support

  • Chrome/Edge 88+
  • Firefox 78+
  • Safari 14+

License

MIT

About

Framework-agnostic data grid web component with sorting, filtering, inline editing, keyboard navigation, context menus, and row toolbars

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Contributors