Vertical Dividers (Variant)
Use the variant prop to toggle vertical dividers in body and summary cells and understand exactly where the setting applies.
import { t } from './i18n';
import * as React from 'react';
import { BGrid, type BGridColumn, type BGridDataItem, type BGridProps, type BGridSummaryColumn } from 'beautiful-grid';
import { Segmented } from 'antd';
import DataGridContainer from '../components/DataGridContainer';
import { useContainerSize } from '../hooks/useContainerSize';
interface SalesRow {
id: string;
product: string;
category: string;
quantity: number;
amount: number;
}
type GridVariant = NonNullable<BGridProps<SalesRow>['variant']>;
const categories = [t('하드웨어', 'Hardware'), t('소프트웨어', 'Software'), t('서비스', 'Service')];
const data: BGridDataItem<SalesRow>[] = Array.from({ length: 30 }, (_, index) => ({
values: {
id: `ORD-${String(index + 1).padStart(3, '0')}`,
product: `${t('상품', 'Product')} ${index + 1}`,
category: categories[index % categories.length],
quantity: (index % 5) + 1,
amount: 28000 + index * 3500,
},
}));
const columns: BGridColumn<SalesRow>[] = [
{ key: 'id', label: t('주문번호', 'Order Number'), width: 100, align: 'center' },
{ key: 'product', label: t('상품명', 'Product Name'), width: 180 },
{ key: 'category', label: t('분류', 'Category'), width: 120, align: 'center' },
{ key: 'quantity', label: t('수량', 'Quantity'), width: 90, align: 'right' },
{
key: 'amount',
label: t('금액', 'Amount'),
width: 140,
align: 'right',
itemRender: ({ values }) => `${values.amount.toLocaleString('ko-KR')}${t('원', 'KRW')}`,
},
];
const summaryColumns: BGridSummaryColumn<SalesRow>[] = [
{ columnIndex: 0, align: 'center', itemRender: () => <strong>{t('합계', 'Total')}</strong> },
{
columnIndex: 3,
align: 'right',
itemRender: ({ data }) => data.reduce((sum, item) => sum + item.values.quantity, 0),
},
{
columnIndex: 4,
align: 'right',
itemRender: ({ data }) => `${data.reduce((sum, item) => sum + item.values.amount, 0).toLocaleString('ko-KR')}${t('원', 'KRW')}`,
},
];
const variantOptions: Array<{ value: GridVariant; label: string; description: string }> = [
{ value: 'default', label: 'default', description: t('본문과 요약 셀의 세로 구분선 생략', 'Omit vertical separator between body and summary cell') },
{ value: 'vertical-bordered', label: 'vertical-bordered', description: t('본문과 요약 셀의 세로 구분선 표시', 'Show vertical separator between body and summary cell') },
];
export default function VariantExample() {
const [variant, setVariant] = React.useState<GridVariant>('default');
const containerRef = React.useRef<HTMLDivElement>(null);
const { width, height } = useContainerSize(containerRef);
return (
<div className='flex min-h-0 flex-col gap-3'>
<div className='flex flex-wrap items-center justify-between gap-3 rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm text-slate-700'>
<div>
<strong className='block text-slate-900'>{t('세로 구분선 Variant', 'Vertical Border Variant')}</strong>
<span className='text-xs text-slate-500'>{t('본문과 하단 합계 행에서 세로 구분선의 표시 여부를 비교해 보세요.', 'Compare whether the vertical border is displayed in the body and bottom summary rows.')}</span>
</div>
<Segmented
aria-label={t('세로 구분선 variant 선택', 'Select Vertical Separator Variant')}
value={variant}
options={variantOptions.map(({ value, label }) => ({ value, label }))}
onChange={value => setVariant(value as GridVariant)}
/>
</div>
<p className='m-0 text-xs text-slate-500' aria-live='polite'>
{t('현재 설정:', 'Current Setting:')} <code>variant="{variant}"</code> —{' '}
{variantOptions.find(option => option.value === variant)?.description}
</p>
<DataGridContainer ref={containerRef}>
<BGrid<SalesRow>
width={width}
height={height}
columns={columns}
data={data}
rowKey='id'
showLineNumber
frozenColumnIndex={1}
variant={variant}
summary={{ position: 'bottom', rows: [{ columns: summaryColumns }] }}
/>
</DataGridContainer>
</div>
);
}import * as React from 'react';
import './DataGridContainer.css';
interface DataGridContainerProps extends React.HTMLAttributes<HTMLDivElement> {
children?: React.ReactNode;
}
/**
* Keeps a DataGrid in a measured, fixed layout box.
*
* BGrid's rendered root is absolutely positioned within this relative
* container. This makes a ResizeObserver measurement authoritative when a
* surrounding flex or grid layout shrinks as well as when it expands.
*/
const DataGridContainer = React.forwardRef<HTMLDivElement, DataGridContainerProps>(
({ className, ...rest }, ref) => (
<div ref={ref} className={`data-grid-container ${className ?? ''}`.trim()} {...rest} />
),
);
DataGridContainer.displayName = 'DataGridContainer';
export default DataGridContainer;.data-grid-container {
position: relative;
width: 100%;
height: 400px;
overflow: hidden;
font-size: 13px;
}
.data-grid-container > .bgrid-root {
position: absolute;
inset: 0;
}import * as React from 'react';
export function useContainerSize(ref: React.MutableRefObject<HTMLElement | null>, additionalDeps: unknown[] = []) {
const [width, setWidth] = React.useState(0);
const [height, setHeight] = React.useState(0);
const resizeObserver = React.useRef(
new ResizeObserver(entries => {
if (entries.length !== 1) {
throw new Error('Invalid Container length');
}
const [entry] = entries;
const { width, height } = entry.contentRect;
setWidth(width);
setHeight(height);
}),
);
React.useEffect(() => {
if (!ref.current) return;
const observer = resizeObserver.current;
const element = ref.current;
setWidth(element.clientWidth);
setHeight(element.clientHeight);
observer.observe(element);
return () => {
observer.unobserve(element);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...additionalDeps, ref]);
return {
width,
height,
};
}1. What variant changes
The BGrid variant prop controls vertical dividers in body and summary cells. It does not change data, columns, sorting, selection, or any other grid behavior.
| Value | Appearance | Recommended use |
|---|---|---|
default |
Default style without vertical dividers between body cells | General lists designed for row-by-row scanning |
vertical-bordered |
Adds a vertical divider to the right of each body and summary cell | Dense tables where column boundaries must be easy to distinguish |
If you omit the prop, default is applied.
2. Live example
Use the selector above to switch between the two values and compare the vertical dividers in the body and bottom summary row. When frozen columns are enabled, the same variant is applied consistently to both the frozen and scrollable regions.
The key setting is:
const [variant, setVariant] = useState<'default' | 'vertical-bordered'>('default');
<BGrid
columns={columns}
data={data}
variant={variant}
summary={{
position: 'bottom',
rows: [{ columns: summaryColumns }],
}}
/>;
variant controls vertical dividers between grid cells. It is unrelated to scrollbar.variant, which accepts native | classic | modern. See the scrollbar configuration guide for scrollbar styling.
3. Use it with a theme
The vertical-bordered divider uses --bgrid-border-color-light, falling back to --bgrid-border-color-base when the lighter color is not defined. Override the variable on the grid wrapper to match your application’s theme.
.report-grid {
--bgrid-border-color-light: #cbd5e1;
}
After adjusting the divider color, verify that column boundaries remain clear in regular rows, hover and selection states, and the summary row.