// src/components/row-autosize/rowAutosize.ts
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();
import {
RowHeaderPlugin,
RowAutoSizePlugin,
} from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
import { faker } from '@faker-js/faker';
const { isDark } = currentTheme();
const ROW_COUNT = 1000;
const COLUMN_COUNT = 50;
const STATUSES = ['Active', 'Pending', 'Completed', 'Blocked'];
const BADGE_STYLES: Record<string, Record<string, string>> = {
Active: { backgroundColor: '#d1fae5', color: '#065f46', borderColor: '#34d399' },
Pending: { backgroundColor: '#fef3c7', color: '#92400e', borderColor: '#f59e0b' },
Completed: { backgroundColor: '#dbeafe', color: '#1e40af', borderColor: '#60a5fa' },
Blocked: { backgroundColor: '#fee2e2', color: '#991b1b', borderColor: '#f87171' },
};
const getColumnKind = (columnIndex: number) => {
if (columnIndex % 5 === 2) return 'badge';
if (columnIndex % 5 === 3) return 'date';
return 'text';
};
const getColumnName = (columnIndex: number) => {
const group = Math.floor(columnIndex / 5) + 1;
const names = ['Summary', 'Notes', 'Status', 'Due Date', 'Owner'];
return `${names[columnIndex % 5]} ${group}`;
};
const getColumnSize = (columnIndex: number) => {
if (columnIndex % 5 === 1) return 320;
if (columnIndex % 5 === 2) return 150;
if (columnIndex % 5 === 3) return 170;
return 190;
};
const formatDate = (value: unknown) => new Intl.DateTimeFormat('en-US', {
month: 'short',
day: '2-digit',
year: 'numeric',
}).format(new Date(String(value)));
// Generate sample data with varying content lengths
const generateData = (count: number) => {
return Array.from({ length: count }, (_, rowIndex) => {
const row: Record<string, string> = {};
for (let columnIndex = 0; columnIndex < COLUMN_COUNT; columnIndex++) {
const kind = getColumnKind(columnIndex);
if (kind === 'badge') {
row[`field${columnIndex}`] = STATUSES[(rowIndex + columnIndex) % STATUSES.length];
} else if (kind === 'date') {
row[`field${columnIndex}`] = new Date(2026, columnIndex % 12, (rowIndex % 28) + 1).toISOString();
} else {
row[`field${columnIndex}`] = columnIndex % 5 === 1
? Array.from(
{ length: Math.floor(Math.random() * 4) + 1 },
() => faker.lorem.sentence()
).join('\n')
: `${faker.commerce.productName()} ${rowIndex + 1}.${columnIndex + 1}`;
}
}
return row;
});
};
const cellTemplate = (h: any, { value, prop }: { value: unknown; prop: string }) => {
const columnIndex = Number(prop.replace('field', ''));
const kind = getColumnKind(columnIndex);
if (kind === 'badge') {
const style = BADGE_STYLES[String(value)] || BADGE_STYLES.Pending;
return h('span', {
style: {
display: 'inline-flex',
alignItems: 'center',
border: `1px solid ${style.borderColor}`,
borderRadius: '999px',
padding: '2px 8px',
fontSize: '12px',
fontWeight: '600',
lineHeight: '18px',
backgroundColor: style.backgroundColor,
color: style.color,
},
}, value);
}
if (kind === 'date') {
return h('span', {
style: {
color: '#4b5563',
fontVariantNumeric: 'tabular-nums',
whiteSpace: 'nowrap',
},
}, formatDate(value));
}
return h('span', { style: { whiteSpace: 'pre-wrap' } }, value);
};
export function load(parentSelector: string) {
const parent = document.querySelector(parentSelector);
if (!parent) return;
const grid = document.createElement('revo-grid');
// Define columns;
grid.columns = Array.from({ length: COLUMN_COUNT }, (_, columnIndex) => ({
prop: `field${columnIndex}`,
name: getColumnName(columnIndex),
size: getColumnSize(columnIndex),
readonly: columnIndex % 5 === 4,
cellTemplate,
}));
// Define plugin
grid.plugins = [RowHeaderPlugin, RowAutoSizePlugin];
grid.rowAutoSize = {
minHeight: 24,
maxHeight: 200,
};
grid.theme = isDark() ? 'darkCompact' : 'compact';
grid.resize = true;
grid.range = true;
grid.hideAttribution = true;
parent.appendChild(grid);
grid.source = generateData(ROW_COUNT);
}
import React, { useState, useMemo } from 'react';
import { RevoGrid, Template, type ColumnDataSchemaModel } from '@revolist/react-datagrid';
import { RowHeaderPlugin, RowAutoSizePlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
import { faker } from '@faker-js/faker';
const ROW_COUNT = 1000;
const COLUMN_COUNT = 50;
const STATUSES = ['Active', 'Pending', 'Completed', 'Blocked'];
const BADGE_STYLES: Record<string, React.CSSProperties> = {
Active: { backgroundColor: '#d1fae5', color: '#065f46', borderColor: '#34d399' },
Pending: { backgroundColor: '#fef3c7', color: '#92400e', borderColor: '#f59e0b' },
Completed: { backgroundColor: '#dbeafe', color: '#1e40af', borderColor: '#60a5fa' },
Blocked: { backgroundColor: '#fee2e2', color: '#991b1b', borderColor: '#f87171' },
};
const getColumnKind = (columnIndex: number) => {
if (columnIndex % 5 === 2) return 'badge';
if (columnIndex % 5 === 3) return 'date';
return 'text';
};
const getColumnName = (columnIndex: number) => {
const group = Math.floor(columnIndex / 5) + 1;
const names = ['Summary', 'Notes', 'Status', 'Due Date', 'Owner'];
return `${names[columnIndex % 5]} ${group}`;
};
const getColumnSize = (columnIndex: number) => {
if (columnIndex % 5 === 1) return 320;
if (columnIndex % 5 === 2) return 150;
if (columnIndex % 5 === 3) return 170;
return 190;
};
const formatDate = (value: unknown) => new Intl.DateTimeFormat('en-US', {
month: 'short',
day: '2-digit',
year: 'numeric',
}).format(new Date(String(value)));
function CellValue({ value, prop }: Partial<ColumnDataSchemaModel>) {
const columnIndex = Number(String(prop).replace('field', ''));
const kind = getColumnKind(columnIndex);
if (kind === 'badge') {
return (
<span
style={{
display: 'inline-flex',
alignItems: 'center',
border: `1px solid ${(BADGE_STYLES[String(value)] || BADGE_STYLES.Pending).borderColor}`,
borderRadius: 999,
padding: '2px 8px',
fontSize: 12,
fontWeight: 600,
lineHeight: '18px',
...(BADGE_STYLES[String(value)] || BADGE_STYLES.Pending),
}}
>
{value}
</span>
);
}
if (kind === 'date') {
return <span style={{ color: '#4b5563', fontVariantNumeric: 'tabular-nums', whiteSpace: 'nowrap' }}>{formatDate(value)}</span>;
}
return <span style={{ whiteSpace: 'pre-wrap' }}>{value}</span>;
}
const RowAutosizeGrid = () => {
const { isDark } = currentTheme();
const theme = isDark() ? 'darkCompact' : 'compact';
// Generate sample data with varying content lengths
const generateData = (count: number) => {
return Array.from({ length: count }, (_, rowIndex) => {
const row: Record<string, string> = {};
for (let columnIndex = 0; columnIndex < COLUMN_COUNT; columnIndex++) {
const kind = getColumnKind(columnIndex);
if (kind === 'badge') {
row[`field${columnIndex}`] = STATUSES[(rowIndex + columnIndex) % STATUSES.length];
} else if (kind === 'date') {
row[`field${columnIndex}`] = new Date(2026, columnIndex % 12, (rowIndex % 28) + 1).toISOString();
} else {
row[`field${columnIndex}`] = columnIndex % 5 === 1
? Array.from(
{ length: Math.floor(Math.random() * 4) + 1 },
() => faker.lorem.sentence()
).join('\n')
: `${faker.commerce.productName()} ${rowIndex + 1}.${columnIndex + 1}`;
}
}
return row;
});
};
const [rows] = useState(() => generateData(ROW_COUNT));
const columns = useMemo(
() => {
const cellTemplate = Template(CellValue);
return Array.from({ length: COLUMN_COUNT }, (_, columnIndex) => ({
prop: `field${columnIndex}`,
name: getColumnName(columnIndex),
size: getColumnSize(columnIndex),
readonly: columnIndex % 5 === 4,
cellTemplate,
}));
},
[]
);
const plugins = useMemo(() => [RowHeaderPlugin, RowAutoSizePlugin], []);
const rowAutoSize = useMemo(
() => ({
minHeight: 24,
maxHeight: 200,
}),
[],
);
return (
<RevoGrid
theme={theme}
columns={columns}
source={rows}
plugins={plugins}
rowAutoSize={rowAutoSize}
resize
hideAttribution
/>
);
};
export default RowAutosizeGrid;
<template>
<RevoGrid
class="overflow-hidden cell-border"
:theme="isDark ? 'darkMaterial' : 'material'"
:columns="columns"
:source="rows"
:plugins="plugins"
:row-auto-size.prop="rowAutoSize"
range
resize
hide-attribution
/>
</template>
<script setup lang="ts">
import { defineComponent, h, ref, shallowRef } from 'vue';
import RevoGrid, { VGridVueTemplate, type ColumnDataSchemaModel } from '@revolist/vue3-datagrid';
import {
RowHeaderPlugin,
RowOddPlugin,
RowAutoSizePlugin,
} from '@revolist/revogrid-pro';
import { currentThemeVue } from '../composables/useRandomData';
import { faker } from '@faker-js/faker';
const { isDark } = currentThemeVue();
const ROW_COUNT = 1000;
const COLUMN_COUNT = 50;
const STATUSES = ['Active', 'Pending', 'Completed', 'Blocked'];
const BADGE_STYLES: Record<string, Record<string, string>> = {
Active: { backgroundColor: '#d1fae5', color: '#065f46', borderColor: '#34d399' },
Pending: { backgroundColor: '#fef3c7', color: '#92400e', borderColor: '#f59e0b' },
Completed: { backgroundColor: '#dbeafe', color: '#1e40af', borderColor: '#60a5fa' },
Blocked: { backgroundColor: '#fee2e2', color: '#991b1b', borderColor: '#f87171' },
};
const getColumnKind = (columnIndex: number) => {
if (columnIndex % 5 === 2) return 'badge';
if (columnIndex % 5 === 3) return 'date';
return 'text';
};
const getColumnName = (columnIndex: number) => {
const group = Math.floor(columnIndex / 5) + 1;
const names = ['Summary', 'Notes', 'Status', 'Due Date', 'Owner'];
return `${names[columnIndex % 5]} ${group}`;
};
const getColumnSize = (columnIndex: number) => {
if (columnIndex % 5 === 1) return 320;
if (columnIndex % 5 === 2) return 150;
if (columnIndex % 5 === 3) return 170;
return 190;
};
const formatDate = (value: unknown) => new Intl.DateTimeFormat('en-US', {
month: 'short',
day: '2-digit',
year: 'numeric',
}).format(new Date(String(value)));
// Generate sample data with varying content lengths
const generateData = (count: number) => {
return Array.from({ length: count }, (_, rowIndex) => {
const row: Record<string, string> = {};
for (let columnIndex = 0; columnIndex < COLUMN_COUNT; columnIndex++) {
const kind = getColumnKind(columnIndex);
if (kind === 'badge') {
row[`field${columnIndex}`] = STATUSES[(rowIndex + columnIndex) % STATUSES.length];
} else if (kind === 'date') {
row[`field${columnIndex}`] = new Date(2026, columnIndex % 12, (rowIndex % 28) + 1).toISOString();
} else {
row[`field${columnIndex}`] = columnIndex % 5 === 1
? Array.from(
{ length: Math.floor(Math.random() * 4) + 1 },
() => faker.lorem.sentence()
).join('\n')
: `${faker.commerce.productName()} ${rowIndex + 1}.${columnIndex + 1}`;
}
}
return row;
});
};
const rows = shallowRef(generateData(ROW_COUNT));
const CellValue = defineComponent({
props: ['value', 'prop'],
setup(cellProps: Partial<ColumnDataSchemaModel>) {
return () => {
const columnIndex = Number(String(cellProps.prop).replace('field', ''));
const kind = getColumnKind(columnIndex);
if (kind === 'badge') {
const style = BADGE_STYLES[String(cellProps.value)] || BADGE_STYLES.Pending;
return h('span', {
style: {
display: 'inline-flex',
alignItems: 'center',
border: `1px solid ${style.borderColor}`,
borderRadius: '999px',
padding: '2px 8px',
fontSize: '12px',
fontWeight: '600',
lineHeight: '18px',
backgroundColor: style.backgroundColor,
color: style.color,
},
}, cellProps.value);
}
if (kind === 'date') {
return h('span', {
style: {
color: '#4b5563',
fontVariantNumeric: 'tabular-nums',
whiteSpace: 'nowrap',
},
}, formatDate(cellProps.value));
}
return h('span', { style: { whiteSpace: 'pre-wrap' } }, cellProps.value);
};
},
});
const cellTemplate = VGridVueTemplate(CellValue);
const columns = ref(Array.from({ length: COLUMN_COUNT }, (_, columnIndex) => ({
prop: `field${columnIndex}`,
name: getColumnName(columnIndex),
size: getColumnSize(columnIndex),
readonly: columnIndex % 5 === 4,
cellTemplate,
})));
const plugins = [RowHeaderPlugin, RowAutoSizePlugin, RowOddPlugin];
const rowAutoSize = {
minHeight: 24,
maxHeight: 200,
};
</script>
import { Component, Input, ViewEncapsulation, OnInit, NO_ERRORS_SCHEMA } from '@angular/core';
import { RevoGrid, Template } from '@revolist/angular-datagrid';
import type { ColumnDataSchemaModel } from '@revolist/revogrid';
import {
RowHeaderPlugin,
RowAutoSizePlugin,
} from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
import { faker } from '@faker-js/faker';
const ROW_COUNT = 1000;
const COLUMN_COUNT = 50;
const STATUSES = ['Active', 'Pending', 'Completed', 'Blocked'];
const BADGE_STYLES: Record<string, Record<string, string>> = {
Active: { backgroundColor: '#d1fae5', color: '#065f46', borderColor: '#34d399' },
Pending: { backgroundColor: '#fef3c7', color: '#92400e', borderColor: '#f59e0b' },
Completed: { backgroundColor: '#dbeafe', color: '#1e40af', borderColor: '#60a5fa' },
Blocked: { backgroundColor: '#fee2e2', color: '#991b1b', borderColor: '#f87171' },
};
const getColumnKind = (columnIndex: number) => {
if (columnIndex % 5 === 2) return 'badge';
if (columnIndex % 5 === 3) return 'date';
return 'text';
};
const getColumnName = (columnIndex: number) => {
const group = Math.floor(columnIndex / 5) + 1;
const names = ['Summary', 'Notes', 'Status', 'Due Date', 'Owner'];
return `${names[columnIndex % 5]} ${group}`;
};
const getColumnSize = (columnIndex: number) => {
if (columnIndex % 5 === 1) return 320;
if (columnIndex % 5 === 2) return 150;
if (columnIndex % 5 === 3) return 170;
return 190;
};
const formatDate = (value: unknown) => new Intl.DateTimeFormat('en-US', {
month: 'short',
day: '2-digit',
year: 'numeric',
}).format(new Date(String(value)));
@Component({
selector: 'row-autosize-cell-value',
standalone: true,
template: `
@if (kind === 'badge') {
<span
[style.display]="'inline-flex'"
[style.align-items]="'center'"
[style.border]="'1px solid ' + badgeStyle.borderColor"
[style.border-radius]="'999px'"
[style.padding]="'2px 8px'"
[style.font-size]="'12px'"
[style.font-weight]="'600'"
[style.line-height]="'18px'"
[style.background-color]="badgeStyle.backgroundColor"
[style.color]="badgeStyle.color"
>{{ value }}</span>
} @else if (kind === 'date') {
<span
[style.color]="'#4b5563'"
[style.font-variant-numeric]="'tabular-nums'"
[style.white-space]="'nowrap'"
>{{ formattedDate }}</span>
} @else {
<span [style.white-space]="'pre-wrap'">{{ value }}</span>
}
`,
})
export class RowAutosizeCellValueComponent {
@Input() props!: ColumnDataSchemaModel;
get value() {
return this.props?.value;
}
get kind() {
const columnIndex = Number(String(this.props?.prop).replace('field', ''));
return getColumnKind(columnIndex);
}
get badgeStyle() {
return BADGE_STYLES[String(this.value)] || BADGE_STYLES.Pending;
}
get formattedDate() {
return formatDate(this.value);
}
}
@Component({
selector: 'row-autosize-grid',
standalone: true,
imports: [RevoGrid],
template: `
<revo-grid
[theme]="theme"
[columns]="columns"
[source]="rows"
[plugins]="plugins"
[rowAutoSize]="rowAutoSize"
[resize]="true"
[hideAttribution]="true"
style="min-height: 400px; min-width: 600px"
></revo-grid>
`,
encapsulation: ViewEncapsulation.None,
// Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
schemas: [NO_ERRORS_SCHEMA],
})
export class RowAutosizeGridComponent implements OnInit {
theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
// Generate sample data with varying content lengths
generateData(count: number) {
return Array.from({ length: count }, (_, rowIndex) => {
const row: Record<string, string> = {};
for (let columnIndex = 0; columnIndex < COLUMN_COUNT; columnIndex++) {
const kind = getColumnKind(columnIndex);
if (kind === 'badge') {
row[`field${columnIndex}`] = STATUSES[(rowIndex + columnIndex) % STATUSES.length];
} else if (kind === 'date') {
row[`field${columnIndex}`] = new Date(2026, columnIndex % 12, (rowIndex % 28) + 1).toISOString();
} else {
row[`field${columnIndex}`] = columnIndex % 5 === 1
? Array.from(
{ length: Math.floor(Math.random() * 4) + 1 },
() => faker.lorem.sentence()
).join('\n')
: `${faker.commerce.productName()} ${rowIndex + 1}.${columnIndex + 1}`;
}
}
return row;
});
}
rows = this.generateData(ROW_COUNT);
cellTemplate = Template(RowAutosizeCellValueComponent);
columns = Array.from({ length: COLUMN_COUNT }, (_, columnIndex) => ({
prop: `field${columnIndex}`,
name: getColumnName(columnIndex),
size: getColumnSize(columnIndex),
readonly: columnIndex % 5 === 4,
cellTemplate: this.cellTemplate,
}));
plugins = [RowHeaderPlugin, RowAutoSizePlugin];
rowAutoSize = {
minHeight: 24,
maxHeight: 200,
};
ngOnInit() {
// Any additional setup if needed
}
}