Dropdown Editor
The ColumnDropdown column type renders the Pro dropdown editor inside a grid cell. Use it for single-select values, or enable config.multiSelect when the cell value is an array.
Register ColumnDropdown in columnTypes, set columnType: 'dropdown' on a column, and provide a dropdown.source list with { value, label } options.
Source code
import { defineCustomElements } from '@revolist/revogrid/loader';
import type { ColumnRegular } from '@revolist/revogrid';
import { ColumnDropdown } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
defineCustomElements();
const statusOptions = [
{ value: 'Open', label: 'Open' },
{ value: 'In Review', label: 'In Review' },
{ value: 'Complete', label: 'Complete' },
];
const tagOptions = [
{ value: 'Design', label: 'Design' },
{ value: 'Frontend', label: 'Frontend' },
{ value: 'Backend', label: 'Backend' },
{ value: 'QA', label: 'QA' },
];
const columns: ColumnRegular[] = [
{ prop: 'task', name: 'Task', size: 220 },
{
prop: 'status',
name: 'Status',
size: 180,
columnType: 'dropdown',
dropdown: {
source: statusOptions,
syncCellTemplate: true,
placeholder: 'Select status',
config: {
search: true,
},
},
},
{
prop: 'tags',
name: 'Tags',
size: 260,
columnType: 'dropdown',
dropdown: {
source: tagOptions,
syncCellTemplate: true,
placeholder: 'Select tags',
config: {
multiSelect: true,
search: true,
},
},
},
];
export function load(parentSelector: string) {
const grid = document.createElement('revo-grid');
const { isDark } = currentTheme();
grid.className = 'rounded-lg overflow-hidden cell-border';
grid.style.minHeight = '300px';
grid.range = true;
grid.theme = isDark() ? 'darkMaterial' : 'material';
grid.columnTypes = {
dropdown: ColumnDropdown,
};
grid.columns = columns;
grid.hideAttribution = true;
document.querySelector(parentSelector)?.appendChild(grid);
grid.source = [
{ task: 'Design filters', status: 'Open', tags: ['Design', 'Frontend'] },
{ task: 'Wire API payload', status: 'In Review', tags: ['Backend'] },
{ task: 'Regression pass', status: 'Complete', tags: ['QA', 'Frontend'] },
];
return () => grid.remove();
}
<template>
<RevoGrid
class="rounded-lg overflow-hidden cell-border"
range
:columns="columns"
:source="source"
:column-types="columnTypes"
:theme="isDark ? 'darkMaterial' : 'material'"
hide-attribution
style="min-height: 300px"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import type { ColumnRegular } from '@revolist/revogrid';
import { ColumnDropdown } from '@revolist/revogrid-pro';
import { currentThemeVue } from '../composables/useRandomData';
const { isDark } = currentThemeVue();
const source = ref([
{ task: 'Design filters', status: 'Open', tags: ['Design', 'Frontend'] },
{ task: 'Wire API payload', status: 'In Review', tags: ['Backend'] },
{ task: 'Regression pass', status: 'Complete', tags: ['QA', 'Frontend'] },
]);
const columnTypes = {
dropdown: ColumnDropdown,
};
const columns: ColumnRegular[] = [
{ prop: 'task', name: 'Task', size: 220 },
{
prop: 'status',
name: 'Status',
size: 180,
columnType: 'dropdown',
dropdown: {
syncCellTemplate: true,
source: [
{ value: 'Open', label: 'Open' },
{ value: 'In Review', label: 'In Review' },
{ value: 'Complete', label: 'Complete' },
],
placeholder: 'Select status',
config: {
search: true,
},
},
},
{
prop: 'tags',
name: 'Tags',
size: 260,
columnType: 'dropdown',
dropdown: {
syncCellTemplate: true,
source: [
{ value: 'Design', label: 'Design' },
{ value: 'Frontend', label: 'Frontend' },
{ value: 'Backend', label: 'Backend' },
{ value: 'QA', label: 'QA' },
],
placeholder: 'Select tags',
config: {
multiSelect: true,
search: true,
},
},
},
];
</script>
import { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import type { ColumnRegular } from '@revolist/revogrid';
import { ColumnDropdown } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
function EditorDropdown() {
const { isDark } = currentTheme();
const source = useMemo(
() => [
{ task: 'Design filters', status: 'Open', tags: ['Design', 'Frontend'] },
{ task: 'Wire API payload', status: 'In Review', tags: ['Backend'] },
{ task: 'Regression pass', status: 'Complete', tags: ['QA', 'Frontend'] },
],
[],
);
const columnTypes = useMemo(
() => ({
dropdown: ColumnDropdown,
}),
[],
);
const columns: ColumnRegular[] = useMemo(
() => [
{ prop: 'task', name: 'Task', size: 220 },
{
prop: 'status',
name: 'Status',
size: 180,
columnType: 'dropdown',
dropdown: {
syncCellTemplate: true,
source: [
{ value: 'Open', label: 'Open' },
{ value: 'In Review', label: 'In Review' },
{ value: 'Complete', label: 'Complete' },
],
placeholder: 'Select status',
config: {
search: true,
},
},
},
{
prop: 'tags',
name: 'Tags',
size: 260,
columnType: 'dropdown',
dropdown: {
syncCellTemplate: true,
source: [
{ value: 'Design', label: 'Design' },
{ value: 'Frontend', label: 'Frontend' },
{ value: 'Backend', label: 'Backend' },
{ value: 'QA', label: 'QA' },
],
placeholder: 'Select tags',
config: {
multiSelect: true,
search: true,
},
},
},
],
[],
);
return (
<RevoGrid
className="rounded-lg overflow-hidden cell-border"
range
source={source}
columns={columns}
columnTypes={columnTypes}
theme={isDark() ? 'darkMaterial' : 'material'}
hideAttribution
style={{ minHeight: '300px' }}
/>
);
}
export default EditorDropdown;
import { Component, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { ColumnDropdown } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
@Component({
selector: 'editor-dropdown-grid',
standalone: true,
imports: [RevoGrid],
template: `
<revo-grid
class="rounded-lg overflow-hidden cell-border"
[range]="true"
[source]="source"
[columns]="columns"
[columnTypes]="columnTypes"
[theme]="theme"
[hideAttribution]="true"
style="min-height: 300px;"
></revo-grid>
`,
encapsulation: ViewEncapsulation.None,
})
export class EditorDropdownGridComponent {
theme = currentTheme().isDark() ? 'darkMaterial' : 'material';
source = [
{ task: 'Design filters', status: 'Open', tags: ['Design', 'Frontend'] },
{ task: 'Wire API payload', status: 'In Review', tags: ['Backend'] },
{ task: 'Regression pass', status: 'Complete', tags: ['QA', 'Frontend'] },
];
columnTypes = {
dropdown: ColumnDropdown,
};
columns = [
{ prop: 'task', name: 'Task', size: 220 },
{
prop: 'status',
name: 'Status',
size: 180,
columnType: 'dropdown',
dropdown: {
syncCellTemplate: true,
source: [
{ value: 'Open', label: 'Open' },
{ value: 'In Review', label: 'In Review' },
{ value: 'Complete', label: 'Complete' },
],
placeholder: 'Select status',
config: {
search: true,
},
},
},
{
prop: 'tags',
name: 'Tags',
size: 260,
columnType: 'dropdown',
dropdown: {
syncCellTemplate: true,
source: [
{ value: 'Design', label: 'Design' },
{ value: 'Frontend', label: 'Frontend' },
{ value: 'Backend', label: 'Backend' },
{ value: 'QA', label: 'QA' },
],
placeholder: 'Select tags',
config: {
multiSelect: true,
search: true,
},
},
},
];
}
Features:
- Supports single-select cells with scalar values
- Supports multi-select cells with array values through
config.multiSelect: true - Can enable search with
config.search: true - Saves changes through RevoGrid cell edit events, so the grid data model stays in sync
- Supports custom option and selected-value templates through the dropdown render callbacks
- Can mirror the column cell template in options and selected values with
dropdown.syncCellTemplate - Can disable the dropdown per cell and disable individual options
Disable a dropdown for every cell
Section titled “Disable a dropdown for every cell”Set dropdown.disabled: true to keep the selected value visible while blocking the dropdown. A disabled dropdown never opens through a click, double-click, Enter, F2, typing, or config.autoOpen. It cannot change a selection or remove a multiselect chip.
import type { ColumnRegular } from '@revolist/revogrid';import { ColumnDropdown } from '@revolist/revogrid-pro';
const grid = document.querySelector<HTMLRevoGridElement>('revo-grid')!;grid.columnTypes = { dropdown: ColumnDropdown };const columns: ColumnRegular[] = [{ prop: 'status', name: 'Status', columnType: 'dropdown', dropdown: { disabled: true, source: [ { value: 'active', label: 'Active' }, { value: 'archived', label: 'Archived' }, ], },}];grid.columns = columns;grid.source = [{ status: 'active' }];Disable dropdowns based on the row
Section titled “Disable dropdowns based on the row”Use a synchronous callback returning a boolean. It receives the current ColumnDataSchemaModel: model, value, column, prop, rowIndex, colIndex, type, colType, and data. The value is the stored cell value, even when cellParser supplies a display label. Keep this callback free of side effects; do not change grid state while rendering. Promise-returning callbacks are unsupported.
import type { ColumnRegular } from '@revolist/revogrid';import { ColumnDropdown } from '@revolist/revogrid-pro';
const grid = document.querySelector<HTMLRevoGridElement>('revo-grid')!;grid.columnTypes = { dropdown: ColumnDropdown };const columns: ColumnRegular[] = [{ prop: 'status', name: 'Status', columnType: 'dropdown', dropdown: { disabled: ({ model }) => model.locked === true, source: [ { value: 'active', label: 'Active' }, { value: 'archived', label: 'Archived' }, ], },}];grid.columns = columns;grid.source = [ { status: 'active', locked: false }, { status: 'archived', locked: true },];The callback is resolved separately for each cell and reevaluated on grid updates. Update rows through the grid source/data APIs so the rendered state refreshes. Disabling an open dropdown closes it without saving a new value. Re-enabling restores interaction and does not automatically reopen the popup. The Formula Bar dropdown also respects the resolved disabled state.
Disable individual items
Section titled “Disable individual items”Set disabled: true on an option to keep it visible while preventing selection. Other items remain available. Disabled options have disabled styling and aria-disabled="true"; initial focus and Arrow Up/Down navigation skip them. Click, Enter, and Space cannot select or toggle them. Search, sorting, and custom option renderers preserve these restrictions. A custom renderer should also make the disabled state visually clear.
import type { ColumnRegular } from '@revolist/revogrid';import { ColumnDropdown } from '@revolist/revogrid-pro';
const grid = document.querySelector<HTMLRevoGridElement>('revo-grid')!;grid.columnTypes = { dropdown: ColumnDropdown };const statusOptions = [ { value: 'active', label: 'Active' }, { value: 'paused', label: 'Paused' }, { value: 'archived', label: 'Archived', disabled: true },];const columns: ColumnRegular[] = [ { prop: 'status', name: 'Status', columnType: 'dropdown', dropdown: { source: statusOptions }, }, { prop: 'tags', name: 'Tags', columnType: 'dropdown', dropdown: { source: statusOptions, config: { multiSelect: true, search: true, sortDirection: 'asc' }, }, },];grid.columns = columns;grid.source = [{ status: 'archived', tags: ['archived', 'active'] }];An already-selected disabled value remains displayed and stored. For single-select cells, an enabled option can replace that value. For multiselect cells, disabled selected items cannot be toggled or removed, and their remove controls are omitted; enabled selected items can still change. The dropdown never silently removes stored values when options become disabled.
If all visible items are disabled, an enabled dropdown can still open to show them, but has no active option and cannot select an item. This also applies when a search matches only disabled items. Option disabling defaults to false and does not disable the whole dropdown.
Dropdown disabling versus readonly
Section titled “Dropdown disabling versus readonly”| Setting | Effect |
|---|---|
dropdown.disabled | Blocks dropdown opening, selection, and chip removal; does not make the entire grid cell readonly. |
column.readonly | Blocks dropdown interaction and Core cell edits such as paste and Delete; accepts a boolean or a cell-context callback. |
grid.readonly | Blocks dropdown interaction and editing throughout the grid. |
dropdown.source[i].disabled | Blocks interaction with that option while keeping enabled options available. |
Grid and column readonly rules always win, even when dropdown.disabled returns false. Dropdown columns are editable by default. Readonly cells preserve their passive cell rendering without an interactive dropdown trigger. Disabled dropdown triggers expose aria-disabled="true" and are removed from tab order.
See the Editor Dropdown API for the column contract and the Dropdown API for standalone properties and option types. Standalone DropdownProps.disabled accepts a boolean; cell callbacks belong to DropdownColumnProps.disabled.
Keep cell and dropdown templates in sync
Section titled “Keep cell and dropdown templates in sync”Set dropdown.syncCellTemplate to true to reuse the column cellTemplate for both option rows and selected values. Object option fields are merged into the synthetic template model, so the same renderer can use metadata such as a status tone, avatar URL, or icon. Explicit renderOption and renderValue callbacks remain authoritative.
import type { ColumnRegular } from '@revolist/revogrid';import { ColumnDropdown } from '@revolist/revogrid-pro';
const columns: ColumnRegular[] = [{ prop: 'status', columnType: 'dropdown', cellTemplate: (h, { model, value }) => h('span', { class: `status-badge status-badge--${model.tone}`, }, model.label ?? value), dropdown: { syncCellTemplate: true, source: [ { value: 'active', label: 'Active', tone: 'green' }, { value: 'paused', label: 'Paused', tone: 'amber' }, ], },}];const grid = document.querySelector<HTMLRevoGridElement>('revo-grid')!;grid.columnTypes = { dropdown: ColumnDropdown };grid.columns = columns;