# TypeDefault

> _Since v0.3.0_

Type default configuration for Vue applications.

Inherits every core BaseTypeDefault property (which is itself a
partial `ColumnConfig` — `format`, `width`, `sortable`, `cellClass`,
`filterable`, …) and widens the renderer/editor slots to accept Vue render
functions.

#### Example

```ts
import type { TypeDefault } from '@toolbox-web/grid-vue';
import CountryFlag from './CountryFlag.vue';
import CountrySelect from './CountrySelect.vue';

const countryDefault: TypeDefault<Employee, string> = {
  width: 140,
  renderer: (ctx) => h(CountryFlag, { code: ctx.value }),
  editor: (ctx) => h(CountrySelect, {
    modelValue: ctx.value,
    'onUpdate:modelValue': ctx.commit,
  }),
};
```

## Properties

| Property | Type | Description |
| -------- | ---- | ----------- |
| `format?` | <code>(value: unknown, row: TRow) =&gt; string</code> | Default formatter for all columns of this type. |
| `type?` | <code><a href="/grid/api/core/types/columntype/">ColumnType</a></code> | Column data type. |
| `width?` | <code>string &#124; number</code> | Column width as a CSS grid track size. A number is treated as pixels. |
| `minWidth?` | <code>number</code> | Minimum column width in pixels. Pixels only — the value doubles as the numeric clamp applied during drag-resize (40px when unset). |
| <span id="sortable"></span>`sortable?` | <code>boolean</code> | Whether column can be sorted |
| <span id="resizable"></span>`resizable?` | <code>boolean</code> | Whether column can be resized by user |
| <span id="sortcomparator"></span>`sortComparator?` | <code>(a: any, b: any, rowA: TRow, rowB: TRow) =&gt; number</code> | Optional custom comparator for sorting (a,b) -&gt; number |
| `valueAccessor?` | <code>(ctx: &#123; row: TRow; column: <a href="/grid/vue/api/types/columnconfig/">ColumnConfig</a>&lt;TRow&gt;; rowIndex: number &#125;) =&gt; any</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. |
| <span id="options"></span>`options?` | <code>&#123; label: string; value: unknown &#125;[] &#124; () =&gt; &#123; label: string; value: unknown &#125;[]</code> | For select type - available options |
| `meta?` | <code>Record&lt;string, unknown&gt;</code> | Arbitrary extra metadata for application use. |
| <span id="viewrenderer"></span>`viewRenderer?` | <code><a href="/grid/api/core/types/columnviewrenderer/">ColumnViewRenderer</a>&lt;TRow, any&gt;</code> | Optional custom view renderer used instead of default text rendering |
| <span id="externalview"></span>`externalView?` | <code>&#123; component: unknown; props?: Record&lt;string, unknown&gt;; mount?: (options: &#123; placeholder: HTMLElement; context: <a href="/grid/api/core/interfaces/cellrendercontext/">CellRenderContext</a>&lt;TRow, unknown&gt;; spec: unknown &#125;) =&gt; void &#124; &#123; dispose?: () =&gt; void &#125; &#125;</code> | External view spec (lets host app mount any framework component) |
| <span id="lockvisible"></span>`lockVisible?` | <code>boolean</code> | Prevent this column from being hidden programmatically |
| `cellClass?` | <code>(value: unknown, row: TRow, column: <a href="/grid/vue/api/types/columnconfig/">ColumnConfig</a>&lt;TRow&gt;) =&gt; string &#124; string[]</code> | Dynamic CSS class(es) for cells in this column. Called for each cell during rendering. Return class names to add to the cell element. |
| `headerLabelRenderer?` | <code><a href="/grid/api/core/types/headerlabelrenderer/">HeaderLabelRenderer</a>&lt;TRow&gt;</code> | Custom header label renderer. Customize the label content while the grid handles sort icons, filter buttons, resize handles, and click interactions. |
| `headerRenderer?` | <code><a href="/grid/api/core/types/headerrenderer/">HeaderRenderer</a>&lt;TRow&gt;</code> | Custom header cell renderer. Complete control over the entire header cell. Resize handles are added automatically for resizable columns. |
| `onPaste?` | <code><a href="/grid/plugins/clipboard/types/columnpasteguard/">ColumnPasteGuard</a>&lt;TRow, any&gt;</code> | Per-column paste guard / transform. Requires the ClipboardPlugin. <span class="since-badge" title="Introduced in v3.0.0">v3.0.0+</span> |
| `editable?` | <code>boolean &#124; (row: TRow) =&gt; boolean</code> | Whether the field is editable (enables editors). Requires EditingPlugin. |
| <span id="multi"></span>`multi?` | <code>boolean</code> | For select type - allow multi select. 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). |
| <span id="filterparams"></span>`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. |
| `printHidden?` | <code>boolean</code> | Hide this column when printing (default: false). Use this to exclude interactive or less important columns from print output. |
| `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. |
| `cellTooltip?` | <code>string &#124; false &#124; (ctx: <a href="/grid/api/core/interfaces/cellrendercontext/">CellRenderContext</a>&lt;TRow, any&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. |
| <span id="renderer"></span>`renderer?` | <code>(ctx: <a href="/grid/api/core/interfaces/cellrendercontext/">CellRenderContext</a>&lt;TRow, TValue&gt;) =&gt; VNode</code> | Vue render function for rendering cells of this type |
| <span id="editor"></span>`editor?` | <code>(ctx: <a href="/grid/api/core/interfaces/columneditorcontext/">ColumnEditorContext</a>&lt;TRow, TValue&gt;) =&gt; VNode</code> | Vue render function for editing cells of this type |
| <span id="editorparams"></span>`editorParams?` | <code>Record&lt;string, unknown&gt;</code> | Default editorParams for this type |
| `filterPanelRenderer?` | <code>(params: <a href="/grid/plugins/filtering/interfaces/filterpanelparams/">FilterPanelParams</a>) =&gt; VNode</code> | Vue render function for custom filter panels for this type. |

### Property Details

#### `format`

Default formatter for all columns of this type.

Transforms the raw cell value into a display string. Use when you need
consistent formatting across columns without custom DOM (e.g., currency,
percentages, dates with specific locale).

**Resolution Priority**: Column `format` → Type `format` → Built-in

```typescript
typeDefaults: {
  currency: {
    format: (value) => new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD',
    }).format(value as number),
  },
  percentage: {
    format: (value) => `${(value as number * 100).toFixed(1)}%`,
  }
}
```

---

#### `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`

---

#### `width`

Column width as a CSS grid track size. A number is treated as pixels.

A string accepts any single track value — `'2fr'`, `'30%'`, `'max-content'`,
`'minmax(120px, 1fr)'`, `'calc(...)'`, `'auto'`. An unrecognised string still
reaches the layout but emits a dev-mode diagnostic.

Omit to let `fitMode` size the column: `1fr` (or `minmax(minWidth, 1fr)`) in
`'stretch'`, `max-content` in `'fixed'`.

A user resize replaces the value with a pixel number, so non-pixel units do
not survive a drag.

---

#### `minWidth`

Minimum column width in pixels. Pixels only — the value doubles as the
numeric clamp applied during drag-resize (40px when unset).

Applies only when `width` is omitted: `'stretch'` mode renders the column as
`minmax(minWidth, 1fr)`, `'fixed'` mode uses it as the implicit width.

---

#### `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` → field read
- **Filter**: `filterValue` → `valueAccessor` → field read
- **Render / format / export / copy**: `valueAccessor` → field read

"Field read" means a literal own property named `field` when one exists,
otherwise a nested dotted-path traversal (`'address.city'` reads
`row.address.city`). Sorting, filtering, aggregation, export, clipboard and
cell events all use the same rule.

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.
}
```

---

#### `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.

---

#### `cellClass`

```typescript
// Highlight negative values
cellClass: (value, row, column) => value < 0 ? ['negative', 'text-red'] : []

// Status-based styling
cellClass: (value) => [`status-${value}`]

// Single class as string
cellClass: (value) => value < 0 ? 'negative' : ''
```

---

#### `headerLabelRenderer`

Custom header label renderer. Customize the label content while the grid
handles sort icons, filter buttons, resize handles, and click interactions.

Use this for simple customizations like adding icons, badges, or units.

```typescript
// Add required field indicator
headerLabelRenderer: (ctx) => `${ctx.value} <span class="required">*</span>`

// Add unit to header
headerLabelRenderer: (ctx) => {
  const span = document.createElement('span');
  span.innerHTML = `${ctx.value}<br/><small>(kg)</small>`;
  return span;
}
```

---

#### `headerRenderer`

Custom header cell renderer. Complete control over the entire header cell.
Resize handles are added automatically for resizable columns.

The context provides helper functions to include standard elements:
- `renderSortIcon()` - Returns sort indicator element (null if not sortable)
- `renderFilterButton()` - Returns filter button (null if not filterable)

**Precedence**: `headerRenderer` &gt; `headerLabelRenderer` &gt; `header` &gt; `field`

```typescript
headerRenderer: (ctx) => {
  const div = document.createElement('div');
  div.className = 'custom-header';
  div.innerHTML = `<span>${ctx.value}</span>`;
  const sortIcon = ctx.renderSortIcon();
  if (sortIcon) div.appendChild(sortIcon);
  return div;
}
```

---

#### `onPaste`

Per-column paste guard / transform. Requires the ClipboardPlugin.

Runs for each cell a paste would write into (after the editing plugin's
editability check), letting a consumer reject or rewrite pasted values
**per column** — finer-grained than the whole-operation
ClipboardConfig.pasteHandler.

- `true` / omitted — accept pastes (default behavior).
- `false` — this column never accepts pastes; matching cells are skipped
  (column alignment is preserved, like a non-editable cell).
- `(ctx) => …` — called per cell. Return `false` to reject just that cell,
  `true` (or nothing) to accept as-is, or `{ value }` to write a
  transformed value instead. Returning `{ value }` disambiguates a
  transform from a boolean verdict, so boolean-valued columns stay safe.

Synchronous only — the paste pipeline does not await. Rejected cells
(either form) are reported once per paste via the `paste-rejected` event
(PasteRejectedDetail), so a consumer can show a message.

The callback context also carries `sourceField` — the column the value was
copied FROM (for a same-app paste; `undefined` for external pastes) — so a
column can reject cross-column / cross-type pastes (e.g. refuse a value
copied from a "car" column into a "fruit" column).

```typescript
// Reject pastes into a locked column
{ field: 'id', onPaste: false }

// Reject invalid values, coerce the rest
{
  field: 'price',
  onPaste: ({ value }) => {
    const n = Number(String(value).replace(/[^0-9.]/g, ''));
    return Number.isFinite(n) ? { value: n } : false;
  },
}
```

---

#### `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' }
```

---

#### `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 }
```

---

#### `pinned`

Pin column to an edge of the grid.

**Physical values** (always pin to specified side):
- `'left'` - Pin to left edge
- `'right'` - Pin to right edge

**Logical values** (flip based on text direction for RTL support):
- `'start'` - Pin to inline-start (left in LTR, right in RTL)
- `'end'` - Pin to inline-end (right in LTR, left in RTL)

Requires PinnedColumnsPlugin.

---

#### `lockPinning`

**Default:** `false`

---

#### `printHidden`

```ts
columns: [
  { field: 'name', header: 'Name' },
  { field: 'actions', header: 'Actions', printHidden: true }, // Hidden in print
]
```

---

#### `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 }
```

---

#### `filterPanelRenderer`

Vue render function for custom filter panels for this type.

Unlike the core imperative API `(container, params) => void`, this accepts
a Vue render function that receives only the params and returns a VNode.
The bridge handles mounting and appending to the container automatically.

```ts
import { h } from 'vue';
import CustomFilter from './CustomFilter.vue';

const typeDefault: TypeDefault = {
  filterPanelRenderer: (params) => h(CustomFilter, {
    field: params.field,
    uniqueValues: params.uniqueValues,
    onApply: (values: Set<unknown>) => params.applySetFilter(values),
  }),
};
```

---
