# BaseColumnConfig

> _Since v0.1.1_

Base contract for a column configuration.

Defines the fundamental properties all columns share. Extended by ColumnConfig
with additional features like custom renderers and grouping.

#### Example

```typescript
// Basic column with common properties
const columns: BaseColumnConfig<Employee>[] = [
  {
    field: 'name',
    header: 'Full Name',
    sortable: true,
    resizable: true,
  },
  {
    field: 'salary',
    type: 'number',
    width: 120,
    format: (value) => `$${value.toLocaleString()}`,
    sortComparator: (a, b) => a - b,
  },
  {
    field: 'department',
    type: 'select',
    options: [
      { label: 'Engineering', value: 'eng' },
      { label: 'Sales', value: 'sales' },
    ],
  },
];
```

## Properties

| Property | Type | Description |
| -------- | ---- | ----------- |
| `field` | <code>keyof TRow &amp; string</code> | Unique field key referencing property in row objects |
| `header?` | <code>string</code> | Visible header label; defaults to capitalized field |
| `type?` | <code><a href="/grid/api/core/types/columntype/">ColumnType</a></code> | Column data type. |
| `width?` | <code>string &#124; number</code> | Column width in pixels; fixed size (no flexibility) |
| `minWidth?` | <code>number</code> | Minimum column width in pixels (stretch mode only); when set, column uses minmax(minWidth, 1fr) |
| `order?` | <code>number</code> | Initial column display index. <span class="since-badge" title="Introduced in v2.17.0">v2.17.0+</span> |
| `sortable?` | <code>boolean</code> | Whether column can be sorted |
| `resizable?` | <code>boolean</code> | Whether column can be resized by user |
| `sortComparator?` | <code>(a: TValue, b: TValue, rowA: TRow, rowB: TRow) =&gt; number</code> | Optional custom comparator for sorting (a,b) -&gt; number |
| `valueAccessor?` | <code>(ctx: object) =&gt; TValue</code> | Compute the cell's value from the row. When defined, this is the single source of truth used by sorting, filtering, formatting, cell rendering, export, and clipboard — eliminating the need to duplicate value-extraction logic across `sortComparator`, `filterValue`, and per-renderer code. |
| `options?` | <code>object[] &#124; () =&gt; object[]</code> | For select type - available options |
| `format?` | <code>(value: TValue, row: TRow) =&gt; string</code> | Formats the raw cell value into a display string. |
| `utility?` | <code>boolean</code> | Marks this column as a **system / utility column** — a column that exists to support grid behaviour rather than to display user data. |
| `meta?` | <code>Record&lt;string, unknown&gt;</code> | Arbitrary extra metadata for application use. |
| `editable?` | <code>boolean &#124; (row: TRow) =&gt; boolean</code> | Whether the field is editable (enables editors). Requires EditingPlugin. |
| `editor?` | <code><a href="/grid/api/core/types/columneditorspec/">ColumnEditorSpec</a>&lt;TRow, TValue&gt;</code> | Optional custom editor factory or element tag name. Requires EditingPlugin. |
| `multi?` | <code>boolean</code> | For select type - allow multi select. Requires EditingPlugin. |
| `editorParams?` | <code><a href="/grid/plugins/editing/types/editorparams/">EditorParams</a></code> | Configuration parameters for built-in editors. Shape depends on column type (NumberEditorParams, TextEditorParams, DateEditorParams, SelectEditorParams). Requires EditingPlugin. |
| `nullable?` | <code>boolean</code> | Whether this column allows `null` values. Requires EditingPlugin. |
| `filterable?` | <code>boolean</code> | Whether this column can be filtered (only applicable when FilteringPlugin is enabled). |
| `filterParams?` | <code><a href="/grid/plugins/filtering/interfaces/filterparams/">FilterParams</a></code> | Configuration for the filter UI (only applicable when FilteringPlugin is enabled). For number columns: \{ min, max, step \} For date columns: \{ min, max \} (ISO date strings) Falls back to editorParams if not set. |
| `filterValue?` | <code>(value: unknown, row: any) =&gt; unknown</code> | Custom value extractor for filtering. Use this when the cell value is a complex type (e.g., an array of objects) and the filter should operate on derived primitive values instead. |
| `filterType?` | <code><a href="/grid/plugins/filtering/types/filtertype/">FilterType</a></code> | Override the filter panel UI type independently of the column's `type`. |
| `pinned?` | <code><a href="/grid/plugins/pinned-columns/types/pinnedposition/">PinnedPosition</a></code> | Pin column to an edge of the grid. |
| `lockPinning?` | <code>boolean</code> | Prevent the user from unpinning or repinning this column via the header context menu. Programmatic changes are still allowed. Requires PinnedColumnsPlugin. |
| `lockPosition?` | <code>boolean</code> | Prevent this column from being reordered by the user. When `true`, the column cannot be dragged in the header row or rearranged via the visibility panel. Programmatic reordering (e.g. `setColumnOrder()`) is not affected. |
| `checkboxColumn?` | <code>boolean</code> | Marks this column as the selection checkbox column. Set automatically by SelectionPlugin on its synthesized utility column. |
| `cellTooltip?` | <code>string &#124; false &#124; (ctx: <a href="/grid/api/core/interfaces/cellrendercontext/">CellRenderContext</a>&lt;TRow, TValue&gt;) =&gt; string &#124; unknown</code> | Cell tooltip configuration. Requires TooltipPlugin. |
| `headerTooltip?` | <code>string &#124; false &#124; (ctx: <a href="/grid/api/core/interfaces/headerlabelcontext/">HeaderLabelContext</a>&lt;TRow&gt;) =&gt; string &#124; unknown</code> | Header tooltip configuration. Requires TooltipPlugin. |

### Property Details

#### type

Column data type.

Built-in types: `'string'`, `'number'`, `'date'`, `'boolean'`, `'select'`

Custom types (e.g., `'currency'`, `'country'`) can have type-level defaults
via `gridConfig.typeDefaults` or framework adapter registries.

**Default:** `Inferred from first row data`

---

#### order

Initial column display index.

Sets the column's initial position in the grid declaratively. When `order` is set,
the column is repositioned at that index among the initial column array, _before_
any saved column state is applied.

**Precedence** (low → high):
```
data-key order (merge)    <  order attribute  <  columnState / applyColumnState / runtime reorder
/ array order (auto)
```

If the user supplies saved column state (`gridConfig.columnState`, `applyColumnState()`,
or runtime reorder via drag), that state **wins**. Use `resetColumnOrder()` to return
to the `order`-influenced initial order.

```typescript
// Move 'special-field' to initial index 1; other columns stay inferred
<tbw-grid data-src="/url" column-inference="merge">
  <tbw-grid-column field="special-field" order="1"></tbw-grid-column>
</tbw-grid>
```

**See also:** ReorderPlugin.resetColumnOrder() — returns to this order

---

#### valueAccessor

Compute the cell's value from the row. When defined, this is the single
source of truth used by sorting, filtering, formatting, cell rendering,
export, and clipboard — eliminating the need to duplicate value-extraction
logic across `sortComparator`, `filterValue`, and per-renderer code.

**Resolution precedence**:
- **Sort**: `sortComparator` → `valueAccessor` → `row[field]`
- **Filter**: `filterValue` → `valueAccessor` → `row[field]`
- **Render / format / export / copy**: `valueAccessor` → `row[field]`

The accessor is the *default* value source — per-column escape hatches
(`sortComparator`, `filterValue`) still take precedence when set.

Results are memoized per `(row identity, column field)` so accessors are
free to be "slow but correct" (e.g. `array.find(...)`). Immutable row
updates auto-invalidate; in-place mutations are invalidated by the grid's
edit / transaction paths.

Note: a `valueAccessor` without a paired `valueSetter` (planned API)
implies the column is read-only — editors will not commit through it.

```typescript
// Computed value from nested data
{
  field: 'bolDate',
  header: 'BL date',
  valueAccessor: ({ row }) => {
    if (isCargo(row)) {
      return row.movements.find(m => m.operationType === 'LOAD')?.movementDate ?? null;
    }
    return row.movementDate ?? null;
  },
  filterType: 'date',
  // No need for sortComparator or filterValue — they fall back to the accessor.
}
```

---

#### format

Formats the raw cell value into a display string.

Used both for **cell rendering** and the **built-in filter panel**:
- In cells, the formatted value replaces the raw value as text content.
- In the filter panel (set filter), checkbox labels show the formatted value
  instead of the raw value, and search matches against the formatted text.

The `row` parameter is available during cell rendering but is `undefined`
when called from the filter panel (standalone value formatting). Avoid
accessing `row` properties in format functions intended for filter display.

```typescript
// Currency formatter — works in both cells and filter panel
{
  field: 'price',
  format: (value) => `$${Number(value).toFixed(2)}`,
}

// ID-to-name lookup — filter panel shows names, not IDs
{
  field: 'departmentId',
  format: (value) => departmentMap.get(value as string) ?? String(value),
}
```

---

#### utility

Marks this column as a **system / utility column** — a column that exists to
support grid behaviour rather than to display user data.

Built-in plugins set this on the columns they synthesize (selection checkbox,
row-reorder drag handle, master-detail / tree / row-grouping expander). You can
also set it on columns you author yourself when you want them to behave the same
way — for example, a row-action menu column, a status indicator, or any
developer-defined "system" column that should be visible in the grid only.

Setting `utility: true` excludes the column from:

- **Visibility panel** — not listed in the show/hide UI
- **Column reorder** — header drag-drop and visibility-panel drag treat it as locked
  (equivalent to `lockPosition: true`)
- **Print** — hidden by `PrintPlugin` during print
- **Clipboard copy** — skipped by `ClipboardPlugin`
- **Export** (CSV / JSON / Excel) — skipped by `ExportPlugin`
- **Range / row selection** — clicks land on the column but selection ignores it
- **Filter UI** — no filter button rendered, no filter model entry

The column is still rendered in the grid and still receives `cellRenderer` /
`viewRenderer` / `headerRenderer` callbacks — it is "hidden from the system,
visible in the grid".

Convention: name the field with a `__`-prefix (e.g. `__actions`) so it cannot
collide with a real data field.

```ts
{
  field: '__actions',
  header: '',
  width: 80,
  utility: true,         // excluded from print, export, reorder, visibility, etc.
  resizable: false,
  sortable: false,
  filterable: false,
  viewRenderer: ({ row }) => createActionsButton(row),
}
```

---

#### meta

**Do not use `meta` for grid-recognized flags.** Properties like `lockPosition`,
`lockVisible`, `lockPinning`, `pinned`, `utility`, and `checkboxColumn` are first-class
augmented properties on `ColumnConfig` itself. Using `meta.<flag>` for any of them is
deprecated and only kept as a runtime fallback for back-compat.

---

#### editable

Whether the field is editable (enables editors). Requires EditingPlugin.

- `true` — editable for all rows
- `false` / omitted — not editable
- `(row: TRow) => boolean` — conditionally editable per row

When a function is provided it is evaluated each time the grid needs to
determine if a specific cell can enter edit mode (click, keyboard, grid
mode render, tab navigation, etc.). Keep the function fast — it runs on
hot render paths.

```typescript
// Always editable
{ field: 'name', editable: true }

// Conditionally editable
{ field: 'price', editable: (row) => row.status !== 'locked' }
```

---

#### editorParams

```typescript
{ field: 'price', type: 'number', editable: true, editorParams: { min: 0, max: 1000, step: 0.01 } }
```

---

#### nullable

Whether this column allows `null` values. Requires EditingPlugin.

When `true`:
- **Text/number editors**: clearing all content commits `null`.
- **Select editors**: a "(Blank)" option is automatically prepended that
  commits `null`. The label defaults to `"(Blank)"` and can be overridden
  via `SelectEditorParams.emptyLabel`.
- **Date editors**: clearing the date commits `null`.

When `false`:
- **Text editors**: clearing commits `""` (empty string).
- **Number editors**: clearing commits `editorParams.min` if set, otherwise `0`.
- **Select editors**: no blank option is shown, forcing a selection.
- **Date editors**: clearing commits `editorParams.default` if set,
  otherwise today's date. The fallback preserves the original type
  (string → `"YYYY-MM-DD"`, Date → `new Date()`).

When omitted (default), behaviour matches `false` for text/number columns
and no special handling is applied.

Custom editors can read `column.nullable` from the `ColumnEditorContext`
to implement their own nullable behaviour.

**Default:** `false`

```typescript
columns: [
  { field: 'nickname', editable: true, nullable: true },
  { field: 'department', type: 'select', editable: true, nullable: true,
    options: [{ label: 'Engineering', value: 'eng' }, { label: 'Sales', value: 'sales' }] },
  { field: 'price', type: 'number', editable: true, nullable: false,
    editorParams: { min: 0 } }, // clears to 0
  { field: 'startDate', type: 'date', editable: true, nullable: false,
    editorParams: { default: '2024-01-01' } }, // clears to Jan 1 2024
]
```

---

#### filterable

**Default:** `true`

---

#### filterValue

Custom value extractor for filtering. Use this when the cell value is
a complex type (e.g., an array of objects) and the filter should operate
on derived primitive values instead.

The function receives the raw cell value and the full row, and should
return either a single filterable value or an array of filterable values.
When an array is returned, each element becomes an individual entry in
the filter panel's unique values list. During filtering:

- **`notIn`** (set filter): row is hidden if ANY extracted value is in the excluded set
- **`in`** (set filter): row passes if ANY extracted value is in the included set

```typescript
// Array-of-objects column: extract individual names for filtering
{
  field: 'sellers',
  filterValue: (value) =>
    (value as { companyName: string }[])?.map(s => s.companyName) ?? [],
  format: (value) => (value as { companyName: string }[])?.map(s => s.companyName).join(', ') ?? '',
}
```

---

#### filterType

Override the filter panel UI type independently of the column's `type`.

By default the built-in filter panel is chosen based on `column.type`
(e.g. `'number'` → range slider, `'date'` → date pickers). Set
`filterType` when you want a different panel — for example a numeric
column that should show a set (checkbox list) filter instead of a
range slider.

```typescript
// Volume is stored as a number but users want a value-picker
{ field: 'volume', type: 'number', filterType: 'set', filterable: true }
```

---

#### lockPinning

**Default:** `false`

---

#### lockPosition

Prevent this column from being reordered by the user. When `true`, the column
cannot be dragged in the header row or rearranged via the visibility panel.
Programmatic reordering (e.g. `setColumnOrder()`) is not affected.

Requires ReorderPlugin (or VisibilityPlugin for the panel-drag case).

**Default:** `false`

---

#### cellTooltip

Cell tooltip configuration. Requires TooltipPlugin.

- `false` — disable cell tooltips for this column
- `string` — static tooltip text for all cells in this column
- `(ctx) => string | null` — dynamic tooltip from row data; return `null` to suppress

When omitted, the plugin uses the cell's `textContent` on overflow (if `cell` is enabled).

```typescript
// Static tooltip
{ field: 'status', cellTooltip: 'Current status of the record' }

// Dynamic tooltip from row data
{ field: 'name', cellTooltip: (ctx) => `${ctx.row.firstName} ${ctx.row.lastName}\nDept: ${ctx.row.department}` }

// Disable for this column
{ field: 'actions', cellTooltip: false }
```

---

#### headerTooltip

Header tooltip configuration. Requires TooltipPlugin.

- `false` — disable header tooltip for this column
- `string` — static tooltip text
- `(ctx) => string | null` — dynamic tooltip; return `null` to suppress

When omitted, the plugin uses the column `header` text on overflow (if `header` is enabled).

```typescript
// Custom header tooltip with description
{ field: 'revenue', headerTooltip: 'Total revenue in USD (before tax)' }

// Disable for this column
{ field: 'id', headerTooltip: false }
```

---

## See Also

- [`ColumnConfig`](/grid/api/core/interfaces/columnconfig.md) for full column configuration with renderers
- [`ColumnType`](/grid/api/core/types/columntype.md) for type options
