DataTable
The DataTable component simplifies the creation of tabular data. It uses CSS flex for layout and doesn't rely on native tables.
Basic primary table
To create a table, provide columns with titles using <DataTable.Column name={name}/>
and data with data={data}
.
TIP
<DataTable.Column/>
must be a child component of <DataTable.Head/>
.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Base table example'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Basic secondary table
Use the secondary table to compactly display a small amount of data.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable
data={data}
use='secondary'
sort={['kd', 'desc']}
aria-label={'Table title. Secondary'}
>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Table styles
Compact
Reduce table cel paddings by adding the compact
property.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} compact aria-label={'Table title. Compact'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Borders
Add borders to columns by passing the vBorders
property to specific columns.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Borders'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column vBorders>
Organic Sessions
<DataTable.Column name='kd' children='KD %' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Column>
<DataTable.Column name='other' children='Other' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '1.25',
vol: '32,500,000',
other: 'ebay buy',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '3.4',
vol: '65,457,920',
other: 'ebay buy',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '0.65',
vol: '47,354,640',
other: 'ebay buy',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '0',
vol: '2,456,789',
other: 'ebay buy',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '0',
vol: '21,644,290',
other: 'ebay buy',
},
];
export default Demo;
Table header
Fixed header
Use the <Box position="sticky" top={top} />
to fix the table header.
TIP
Set zIndex=2
for correct display.
Scroll in the table header is useful for very long tables with fixed columns, allowing users to scroll more conveniently without reaching the end. In such cases, scroll can be added to the header and the bottom of the table.
with Scroll.Bar in Header
import React from 'react';
import DataTable from '@semcore/data-table';
import { Box } from '@semcore/flex-box';
const Demo = () => {
const top = 0; // Here should be height of Header in your application
return (
<>
<DataTable data={data} aria-label={'Table title. Fixed header'}>
<Box position='sticky' top={top} zIndex={2}>
<DataTable.Head wMin={1000}>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
</Box>
<DataTable.Body />
</DataTable>
<h3>with Scroll.Bar in Header</h3>
<DataTable data={data} aria-label={'Table title. Fixed header'}>
<Box position='sticky' top={top} zIndex={2}>
<DataTable.Head wMin={1000} withScrollBar>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
</Box>
<DataTable.Body />
</DataTable>
</>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Fixed header with loading state in table
For correct components overlapping, use the SpinContainer
component with SpinContainer.Overlay
but without SpinContainer.Content
.
import React from 'react';
import DataTable from '@semcore/data-table';
import SpinContainer from '@semcore/spin-container';
import { Box } from '@semcore/flex-box';
const Demo = () => {
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
const timer = setInterval(() => {
setLoading(!loading);
}, 1500);
return () => {
clearInterval(timer);
};
}, [loading]);
return (
<DataTable data={data} aria-label={'Table title. Fixed header with spin overlay'}>
<Box position='sticky' top={0} zIndex={2}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
</Box>
<SpinContainer loading={loading} style={{ overflow: 'initial' }}>
<DataTable.Body />
<SpinContainer.Overlay />
</SpinContainer>
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Header customization
You can insert tooltips, selects, and other components into the table header using the children
property.
import React from 'react';
import DataTable from '@semcore/data-table';
import Tooltip from '@semcore/tooltip';
import { Text } from '@semcore/typography';
import DropdownMenu from '@semcore/dropdown-menu';
import { LinkTrigger } from '@semcore/base-trigger';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Customizing header'}>
<DataTable.Head>
<DataTable.Column
name='keyword'
tag={Tooltip}
title="Jesus Christ, Joe, fucking forget about it. I'm Mr. Pink. Let's move on."
tabIndex={0}
>
<Text noWrap>
Keyword <Text color='text-secondary'>(1 - 100)</Text>
</Text>
</DataTable.Column>
<DataTable.Column name='kd'>
<DropdownMenu>
<DropdownMenu.Trigger
tag={LinkTrigger}
color='text-primary'
style={{ fontSize: '12px' }}
>
KD,%
</DropdownMenu.Trigger>
<DropdownMenu.Menu>
<DropdownMenu.Item>Options 1</DropdownMenu.Item>
<DropdownMenu.Item>Options 2</DropdownMenu.Item>
</DropdownMenu.Menu>
</DropdownMenu>
</DataTable.Column>
<DataTable.Column
name='cpc'
tag={Tooltip}
title="Jesus Christ, Joe, fucking forget about it. I'm Mr. Pink. Let's move on."
tabIndex={0}
>
CPC
</DataTable.Column>
<DataTable.Column
name='vol'
tag={Tooltip}
title="Jesus Christ, Joe, fucking forget about it. I'm Mr. Pink. Let's move on."
tabIndex={0}
>
Vol.
</DataTable.Column>
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Multi-level header
Create a multi-level header by nesting columns within each other.
TIP
name
property isn't applicable for group columns.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Multi level header'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column vBorders wMax={'40%'}>
Organic Sessions
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Column>
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Additional elements in header
Components added to <DataTable.Head/>
will be inserted at the end of the header.
import React from 'react';
import DataTable from '@semcore/data-table';
import ProgressBar from '@semcore/progress-bar';
const maxValue = 100;
const Demo = () => {
const [value, setValue] = React.useState(0);
React.useEffect(() => {
const timerFetch = setInterval(() => {
setValue((value) => (value < maxValue ? value + 4 : 0));
}, 3000);
return () => {
clearInterval(timerFetch);
};
}, []);
return (
<>
<style>
{`
.progress-row {
position: relative;
}
.progress-cell:focus-visible::after {
content: '';
display: block;
position: absolute;
box-shadow: var(--intergalactic-keyboard-focus, 0px 0px 0px 3px rgba(0, 143, 248, 0.5));
z-index: 1;
width: calc(100% - 3px * 2);
height: calc(100% - 3px * 2);
top: 3px;
left: 3px;
}
`}
</style>
<DataTable data={data} aria-label={'Table title. Additional elements in header'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
<div role={'row'} className={'progress-row'}>
<div
role={'gridcell'}
// @ts-ignore
name={'keyword/kd/cpc/vol'}
tabIndex={-1}
className={'progress-cell'}
>
<ProgressBar
value={value}
size='s'
style={{ borderRadius: 0 }}
aria-label={'Loading table progress bar'}
>
<ProgressBar.Value style={{ borderRadius: 0 }} />
</ProgressBar>
</div>
</div>
</DataTable.Head>
<DataTable.Body />
</DataTable>
</>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Table columns
Column sizes
Columns are inherited from the Flex
component and accept its parameters, such as flex
, wMin
, and wMax
, to adjust the column width.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Column size'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' wMin={100} flex='1 0 auto' />
<DataTable.Column name='kd' children='KD,%' flex='0' wMin={100} />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Column alignment
Columns and cells inherit properties from the Flex
component, so you can use justifyContent
and alignItems
to align columns and cells. Table cells automatically inherit the same properties as the column.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Column alignment'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' justifyContent='flex-end' />
<DataTable.Column name='cpc' children='CPC' justifyContent='flex-end' />
<DataTable.Column name='vol' children='Vol.' justifyContent='flex-end' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Fixed column
To fix table columns, use the fixed
property with <DataTable.Column/>
.
TIP
If fixed columns aren't visible in the following example, try reducing the window size.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Fixed columns'}>
<DataTable.Head wMin={1000}>
<DataTable.Column name='keyword' children='Keyword' fixed='left' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' fixed='right' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Columns grouping
Merge columns by changing the table data and using /
to combine column keys. You can merge columns for a specific row, as shown in the following example, or for all rows.
import React from 'react';
import DataTable from '@semcore/data-table';
const data = [
{
keyword: 'ebay buy',
'kd/cpc/vol': 'These three columns are grouped.',
},
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Columns merging'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
export default Demo;
Column expansion
The active column will expand if there isn't enough space. Fixed-width columns won't change size.
TIP
Be cautious with columns with a wMax
property, as the sort icon may overlap the header text on hover, hiding part of the text.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Column expanded'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' wMax={'300px'} />
<DataTable.Column name='kd' children='Difficulty Difficulty' wMax={'85px'} />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' wMax={'300px'} />
<DataTable.Column name='md' children='Marketing SEO' wMax={'90px'} />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
md: '221',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
md: '221',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
md: 'n/a',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
md: '221',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
md: '221',
},
];
export default Demo;
Table rows
Rows grouping
Group cells from different rows by adding a special grouping key to the table data.
import React from 'react';
import DataTable, { ROW_GROUP } from '@semcore/data-table';
const data = [
{
keyword: 'ebay buy',
[ROW_GROUP]: [
{
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
],
},
{
keyword: 'www.ebay.com',
[ROW_GROUP]: [
{
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
],
},
];
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Rows grouping'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
);
};
export default Demo;
Custom rows rendering
If built-in virtualization doesn't meet your requirements, you can implement your own virtualization using renderRows
prop.
import React from 'react';
import DataTable from '@semcore/data-table';
import { AutoSizer, List, CellMeasurer, CellMeasurerCache } from 'react-virtualized';
const cache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 100,
});
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Custom rows rendering'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='tags' children='Tags' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body
renderRows={({ rows, renderRow }) => {
const rowRenderer = ({
key,
index,
style,
parent,
}: { key: string; index: number; style: any; parent: any }) => (
<CellMeasurer key={key} cache={cache} parent={parent} columnIndex={0} rowIndex={index}>
{({ measure }: { measure: (event: any) => void }) => (
<div key={key} style={style} onLoad={measure}>
{renderRow(rows[index], { dataIndex: index })}
</div>
)}
</CellMeasurer>
);
return (
<AutoSizer disableHeight>
{({ width }: { width: number }) => (
<List
height={600}
rowCount={rows.length}
deferredMeasurementCache={cache}
rowHeight={cache.rowHeight}
rowRenderer={rowRenderer}
width={width}
overscanRowCount={3}
/>
)}
</AutoSizer>
);
}}
>
<DataTable.Cell name='tags' direction='column' />
</DataTable.Body>
</DataTable>
);
};
const data = Array(100)
.fill(0)
.map((_, i) => ({
keyword: `keyword ${i}`,
tags: Array(Math.floor(Math.random() * 4))
.fill(0)
.map((_, i) => <div key={i}>tag {i + 1}</div>),
cpc: Math.round(Math.random() * 10),
vol: Math.round(Math.random() * 1000000),
}));
export default Demo;
Table cells
Access to cells
Define <DataTable.Cell/>
with the appropriate name={name}
to apply properties to a table cell. You can use multiple <DataTable.Cell/>
for different business logic.
TIP
<DataTable.Cell/>
must be a direct child component of <DataTable.Body/>
. Don't wrap it in higher-order components, and using styled components (for example, styled(DataTable. Cell) `...`
) isn't allowed.
You can provide data
property for <DataTable.Cell/>
. It isn't used in the component runtime but improves strict typings.
import React from 'react';
import DataTable from '@semcore/data-table';
import { ButtonLink } from '@semcore/button';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Access to cells'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body>
<DataTable.Cell data={data} name='keyword'>
{(props, row, index) => {
return {
children: (
<ButtonLink
onClick={() => {
alert(`Click row
props: ${JSON.stringify(Object.keys(props), null, ' ')};
row: ${JSON.stringify(row, null, ' ')};
index: ${index};`);
}}
>
{row[props.name]}
</ButtonLink>
),
};
}}
</DataTable.Cell>
</DataTable.Body>
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Access to set of cells
To apply properties to multiple table cells, define <DataTable.Cell />
with their names listed using /
.
import React from 'react';
import DataTable from '@semcore/data-table';
import Spin from '@semcore/spin';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Access to set of cells'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body>
<DataTable.Cell data={data} name='keyword/kd/cpc/vol'>
{(props, row) => {
// @ts-ignore
const value = row[props.name];
return {
children: ['-', '$0', 'n/a'].includes(value) ? <Spin /> : props.children,
};
}}
</DataTable.Cell>
</DataTable.Body>
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Sorting
To enable column sorting:
- Set the
sortable
property on the column. - Subscribe to the
onSortChange
event. - Pass the
sort
property to the table. - Sort the data provided in the
data
property.
import React from 'react';
import DataTable, { DataTableSort } from '@semcore/data-table';
type SortableColumn = Exclude<keyof typeof data[0], 'keyword'>;
const Demo = () => {
const [sort, setSort] = React.useState<DataTableSort<keyof typeof data[0]>>(['kd', 'desc']);
const sortedData = React.useMemo(
() =>
[...data].sort((aRow, bRow) => {
const [prop, sortDirection] = sort;
const a = aRow[prop as SortableColumn];
const b = bRow[prop as SortableColumn];
if (a === b) return 0;
if (sortDirection === 'asc') return a > b ? 1 : -1;
else return a > b ? -1 : 1;
}),
[sort],
);
const numberFormat = React.useMemo(() => new Intl.NumberFormat('en-US'), []);
const currencyFormat = React.useMemo(
() => new Intl.NumberFormat('en-US', { currency: 'USD', style: 'currency' }),
[],
);
return (
<DataTable
data={sortedData}
sort={sort}
onSortChange={setSort}
aria-label={'Table title. Sorting'}
>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' justifyContent='left' sortable />
<DataTable.Column name='kd' children='KD,%' justifyContent='right' wMax={68} sortable />
<DataTable.Column name='cpc' children='CPC' wMax={60} sortable />
<DataTable.Column name='vol' children='Vol.' wMax={120} justifyContent='left' sortable />
</DataTable.Head>
<DataTable.Body>
<DataTable.Cell data={data} name='kd'>
{(_, row) => ({
children: row.kd === -1 ? 'n/a' : numberFormat.format(row.kd),
})}
</DataTable.Cell>
<DataTable.Cell data={data} name='cpc'>
{(_, row) => ({
children: row.cpc === -1 ? 'n/a' : currencyFormat.format(row.cpc),
})}
</DataTable.Cell>
<DataTable.Cell data={data} name='vol'>
{(_, row) => ({
children: row.vol === -1 ? 'n/a' : numberFormat.format(row.vol),
})}
</DataTable.Cell>
</DataTable.Body>
</DataTable>
);
};
export default Demo;
const data = [
{
keyword: 'ebay buy',
kd: 77.8,
cpc: 1.25,
vol: 32500000,
},
{
keyword: 'www.ebay.com',
kd: 11.2,
cpc: 3.4,
vol: 65457920,
},
{
keyword: 'www.ebay.com',
kd: 10,
cpc: 0.65,
vol: 47354640,
},
{
keyword: 'ebay buy',
kd: -1,
cpc: 0,
vol: -1,
},
{
keyword: 'ebay buy',
kd: 75.89,
cpc: 0,
vol: 21644290,
},
];
Changing width for sorting column
If some column has changeSortSize={true}
, by default, it will be increased by the largest column if the computed width less than content width
+ sorting icon width
.
import React from 'react';
import DataTable, { DataTableSort } from '@semcore/data-table';
type SortableColumn = Exclude<keyof typeof data[0], 'keyword'>;
const Demo = () => {
const [sort, setSort] = React.useState<DataTableSort<keyof typeof data[0]>>(['kd', 'desc']);
const sortedData = React.useMemo(
() =>
[...data].sort((aRow, bRow) => {
const [prop, sortDirection] = sort;
const a = aRow[prop as SortableColumn];
const b = bRow[prop as SortableColumn];
if (a === b) return 0;
if (sortDirection === 'asc') return a - b;
else return b - a;
}),
[sort],
);
const numberFormat = React.useMemo(() => new Intl.NumberFormat('en-US'), []);
const currencyFormat = React.useMemo(
() => new Intl.NumberFormat('en-US', { currency: 'USD', style: 'currency' }),
[],
);
return (
<DataTable
data={sortedData}
sort={sort}
onSortChange={setSort}
aria-label={'Table title. Sorting with change sortable column size'}
>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' justifyContent='left' sortable />
<DataTable.Column name='kd' children='KD,%' justifyContent='right' wMax={68} sortable />
<DataTable.Column name='cpc' children='CPC' wMax={60} sortable changeSortSize />
<DataTable.Column name='vol' children='Vol.' wMax={120} justifyContent='left' sortable />
</DataTable.Head>
<DataTable.Body>
<DataTable.Cell data={data} name='kd'>
{(_, row) => ({
children: row.kd === -1 ? 'n/a' : numberFormat.format(row.kd),
})}
</DataTable.Cell>
<DataTable.Cell data={data} name='cpc'>
{(_, row) => ({
children: row.cpc === -1 ? 'n/a' : currencyFormat.format(row.cpc),
})}
</DataTable.Cell>
<DataTable.Cell data={data} name='vol'>
{(_, row) => ({
children: row.vol === -1 ? 'n/a' : numberFormat.format(row.vol),
})}
</DataTable.Cell>
</DataTable.Body>
</DataTable>
);
};
export default Demo;
const data = [
{
keyword: 'ebay buy',
kd: 77.8,
cpc: 1.25,
vol: 32500000,
},
{
keyword: 'www.ebay.com',
kd: 11.2,
cpc: 3.4,
vol: 65457920,
},
{
keyword: 'www.ebay.com',
kd: 10,
cpc: 0.65,
vol: 47354640,
},
{
keyword: 'ebay buy',
kd: -1,
cpc: 0,
vol: -1,
},
{
keyword: 'ebay buy',
kd: 75.89,
cpc: 0,
vol: 21644290,
},
];
Changing width of column by reducing width of another column
You could set sortSizeRecalculation={true}
for using this column as column to recalculation width (after increase to sorting column). The needed width will be divided equally between all such columns.
import React from 'react';
import DataTable, { DataTableSort } from '@semcore/data-table';
type SortableColumn = Exclude<keyof typeof data[0], 'keyword'>;
const Demo = () => {
const [sort, setSort] = React.useState<DataTableSort<keyof typeof data[0]>>(['kd', 'desc']);
const sortedData = React.useMemo(
() =>
[...data].sort((aRow, bRow) => {
const [prop, sortDirection] = sort;
const a = aRow[prop as SortableColumn];
const b = bRow[prop as SortableColumn];
if (a === b) return 0;
if (sortDirection === 'asc') return a - b;
else return b - a;
}),
[sort],
);
const numberFormat = React.useMemo(() => new Intl.NumberFormat('en-US'), []);
const currencyFormat = React.useMemo(
() => new Intl.NumberFormat('en-US', { currency: 'USD', style: 'currency' }),
[],
);
return (
<DataTable
data={sortedData}
sort={sort}
onSortChange={setSort}
aria-label={'Table title. With columns marked to recalculate'}
>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' justifyContent='left' sortable />
<DataTable.Column name='kd' children='KD,%' justifyContent='right' wMax={68} sortable />
<DataTable.Column name='cpc' children='CPC' wMax={60} sortable changeSortSize />
<DataTable.Column
name='vol'
children='Vol.'
justifyContent='left'
sortable
wMax={120}
sortSizeRecalculation
/>
</DataTable.Head>
<DataTable.Body>
<DataTable.Cell data={data} name='kd'>
{(_, row) => ({
children: row.kd === -1 ? 'n/a' : numberFormat.format(row.kd),
})}
</DataTable.Cell>
<DataTable.Cell data={data} name='cpc'>
{(_, row) => ({
children: row.cpc === -1 ? 'n/a' : currencyFormat.format(row.cpc),
})}
</DataTable.Cell>
<DataTable.Cell data={data} name='vol'>
{(_, row) => ({
children: row.vol === -1 ? 'n/a' : numberFormat.format(row.vol),
})}
</DataTable.Cell>
</DataTable.Body>
</DataTable>
);
};
export default Demo;
const data = [
{
keyword: 'ebay buy',
kd: 77.8,
cpc: 1.25,
vol: 32500000,
},
{
keyword: 'www.ebay.com',
kd: 11.2,
cpc: 3.4,
vol: 65457920,
},
{
keyword: 'www.ebay.com',
kd: 10,
cpc: 0.65,
vol: 47354640,
},
{
keyword: 'ebay buy',
kd: -1,
cpc: 0,
vol: -1,
},
{
keyword: 'ebay buy',
kd: 75.89,
cpc: 0,
vol: 21644290,
},
];
Table scroll
Basic scroll
<DataTable/>
, <DataTable.Head/>
, and <DataTable.Body/>
are inherited from the Box component and accept all its parameters. <DataTable/>
serves as a container for <DataTable.Head/>
and <DataTable.Body/>
where scrolling is implemented.
TIP
If horizontal scrolling isn't visible, try reducing the window size
By default, scrolling is displayed at the bottom of the table, but it can also be added to the table header. Scroll in the table header is useful for very long tables with fixed columns, allowing users to scroll more conveniently without reaching the end. For examples, refer to the Fixed header section.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Scroll inside'}>
<DataTable.Head wMin={1000}>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body hMax={200} />
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Virtual scroll
Enable scroll virtualization using the virtualScroll
property. Note that built-in virtualization support tables with fixed-height rows only.
import React from 'react';
import DataTable, { ROW_GROUP } from '@semcore/data-table';
const keyword = ['ebay buy', 'www.ebay.com', 'ebay buy'];
const kd = ['77.8', '10', '11.2', '-', '75.89'];
const cpc = ['$3.4', '$0.65', '$1.25', '$0', '$0'];
const vol = ['32,500,000', '65,457,920', '47,354,640', 'n/a', '21,644,290'];
const data = Array(10000)
.fill(0)
.map((_, index) => ({
id: `#${index + 1}`,
keyword: keyword[Math.floor(keyword.length * Math.random())],
[ROW_GROUP]: [
{
kd: kd[Math.floor(kd.length * Math.random())],
cpc: cpc[Math.floor(cpc.length * Math.random())],
vol: vol[Math.floor(vol.length * Math.random())],
},
],
}));
const Demo = () => {
return (
<DataTable data={data} totalRows={10000} aria-label={'Table title. Virtual scroll:x::'}>
<DataTable.Head>
<DataTable.Column name='id' children='ID' />
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column>
Organic Sessions
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Column>
</DataTable.Head>
<DataTable.Body h={400} virtualScroll />
</DataTable>
);
};
export default Demo;
Pagination
Avoid placing Pagination inside the table, as the pagination component has a nav
landmark assigned to it.
import React from 'react';
import DataTable from '@semcore/data-table';
import Pagination from '@semcore/pagination';
const Demo = () => {
const [currentPage, setCurrentPage] = React.useState(0);
const numberFormat = React.useMemo(() => new Intl.NumberFormat('en-US'), []);
const currencyFormat = React.useMemo(
() => new Intl.NumberFormat('en-US', { currency: 'USD', style: 'currency' }),
[],
);
const limit = 2;
const tableData = data.slice(currentPage * limit, currentPage * limit + limit);
return (
<>
<DataTable data={tableData} aria-label={'Table title. Pagination'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' justifyContent='left' />
<DataTable.Column name='kd' children='KD,%' justifyContent='right' wMax={68} />
<DataTable.Column name='cpc' children='CPC' wMax={60} />
<DataTable.Column name='vol' children='Vol.' wMax={120} justifyContent='left' />
</DataTable.Head>
<DataTable.Body>
<DataTable.Cell data={data} name='kd'>
{(_, row) => ({
children: row.kd === -1 ? 'n/a' : numberFormat.format(row.kd),
})}
</DataTable.Cell>
<DataTable.Cell data={data} name='cpc'>
{(_, row) => ({
children: row.cpc === -1 ? 'n/a' : currencyFormat.format(row.cpc),
})}
</DataTable.Cell>
<DataTable.Cell data={data} name='vol'>
{(_, row) => ({
children: row.vol === -1 ? 'n/a' : numberFormat.format(row.vol),
})}
</DataTable.Cell>
</DataTable.Body>
</DataTable>
<Pagination
mt={4}
totalPages={Math.ceil(data.length / limit)}
currentPage={currentPage + 1}
onCurrentPageChange={(page) => setCurrentPage(page - 1)}
/>
</>
);
};
export default Demo;
const data = [
{
keyword: 'ebay buy',
kd: 77.8,
cpc: 1.25,
vol: 32500000,
},
{
keyword: 'www.ebay.com',
kd: 11.2,
cpc: 3.4,
vol: 65457920,
},
{
keyword: 'www.ebay.com',
kd: 10,
cpc: 0.65,
vol: 47354640,
},
{
keyword: 'ebay buy',
kd: -1,
cpc: 0,
vol: -1,
},
{
keyword: 'ebay buy last',
kd: 75.89,
cpc: 0,
vol: 21644290,
},
];
Table states
Loading data
Replace the tag
property with <DataTable.Body/>
on the SpinContainer
to cover the table with a Spin.
import React from 'react';
import DataTable from '@semcore/data-table';
import SpinContainer from '@semcore/spin-container';
const Demo = (): any => {
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
const timer = setInterval(() => {
setLoading(!loading);
}, 1500);
return () => {
clearInterval(timer);
};
}, [loading]);
return (
<DataTable data={data} aria-label={'Table title. Download status'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<SpinContainer
loading={loading}
style={{ overflow: 'initial' }}
use:aria-busy={undefined}
// @ts-ignore
inert={loading ? '' : undefined}
>
<DataTable.Body aria-busy={loading} />
<SpinContainer.Overlay />
</SpinContainer>
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Skeleton
Add a skeleton to the table by directly substituting it in the data
or replacing rows
with <DataTable.Body/>
.
import React from 'react';
import DataTable from '@semcore/data-table';
import Skeleton from '@semcore/skeleton';
function getSkeleton() {
return ['keyword', 'kd', 'cpc', 'vol'].map((c) => ({
cssVar: `--${c}_width`,
name: c,
data: (
<Skeleton height={17}>
<Skeleton.Text y='5' width='60%' />
</Skeleton>
),
}));
}
const Demo = () => {
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
const timer = setInterval(() => {
setLoading(!loading);
}, 2000);
return () => {
clearInterval(timer);
};
}, [loading]);
return (
<DataTable data={data} aria-label={'Table title. Skeleton'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body
{...(loading ? { rows: [getSkeleton(), getSkeleton(), getSkeleton()] } : {})}
/>
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
];
export default Demo;
Accordion inside table
Extend table functionality using the intergalactic/accordion
component. This allows you to add accordions to table rows.
- Wrap the table in the
Accordion
component. - Replace the tag in
DataTable.Row
with our extended tag usingAccordion.Item
. - Define a value for
Accordion.Item
. - Calculate the active line to highlight.
- Render the children as accordion content.
- Add the arrow (
ChevronRight
icon) if needed.
import React from 'react';
import { scaleLinear } from 'd3-scale';
import DataTable from '@semcore/data-table';
import Accordion from '@semcore/accordion';
import { Flex } from '@semcore/flex-box';
import { Plot, Line, XAxis, YAxis, ResponsiveContainer, minMax } from '@semcore/d3-chart';
const RowAccordion = React.forwardRef(function (
{ value, collapse = {}, ...props }: any,
ref: React.Ref<HTMLDivElement>,
) {
return (
<Accordion.Item value={value} ref={ref}>
<Accordion.Item.Toggle {...props} />
<Accordion.Item.Collapse {...collapse} />
</Accordion.Item>
);
});
const Demo = () => {
const [exapnded, setExapnded] = React.useState<number[]>([]);
return (
/* [1] Wrapping the table in the Accordion control component; */
<Accordion value={exapnded} onChange={setExapnded}>
<DataTable data={data} aria-label={'Table title. Accordion inside table'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body>
{/* [2] Replacing the tag in DataTable.Row with our extended tag with Accordion.Item */}
<DataTable.Row tag={RowAccordion}>
{(_props, _row, index) => {
return {
/* [3] Setting the value for Accordion.Item; */
value: index,
/* [4] Calculating the active line to highlight it */
active: exapnded.includes(index),
collapse: {
/* [5] Render the children to accordion content; */
children: <ChartExample />,
},
};
}}
</DataTable.Row>
<DataTable.Cell data={data} name='keyword'>
{(props) => {
return {
children: (
<Flex alignItems='center'>
{/* [6] Set the arrow (Chevron icon), if necessary. */}
<Accordion.Item.Chevron color='icon-secondary-neutral' mr={2} />
{props.children}
</Flex>
),
};
}}
</DataTable.Cell>
</DataTable.Body>
</DataTable>
</Accordion>
);
};
const ChartExample = () => {
const [[width, height], setSize] = React.useState([0, 0]);
const MARGIN = 40;
const [dataChart, setDataChart] = React.useState<any[]>([]);
React.useEffect(() => {
const dataChart = Array(20)
.fill({})
.map((d, i) => ({
x: i,
y: Math.random() * 10,
}));
setDataChart(dataChart);
}, []);
const xScale = scaleLinear()
.range([MARGIN, width - MARGIN])
.domain(minMax(dataChart, 'x'));
const yScale = scaleLinear()
.range([height - MARGIN, MARGIN])
.domain([0, 10]);
return (
<ResponsiveContainer h={300} onResize={setSize}>
<Plot data={dataChart} scale={[xScale, yScale]} width={width} height={height}>
<YAxis>
<YAxis.Ticks />
<YAxis.Grid />
</YAxis>
<XAxis>
<XAxis.Ticks />
</XAxis>
<Line x='x' y='y'>
<Line.Dots display />
</Line>
</Plot>
</ResponsiveContainer>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Specific cases
Table in table
Refer to the example with the accordion.
- Hide the table header.
- Set "inherit" to use the size from the top table for each column.
import React from 'react';
import DataTable from '@semcore/data-table';
import Accordion from '@semcore/accordion';
import { Flex } from '@semcore/flex-box';
const RowAccordion = React.forwardRef(
({ value, collapse = {}, ...props }: any, ref: React.Ref<HTMLDivElement>) => {
return (
<Accordion.Item value={value} ref={ref}>
<Accordion.Item.Toggle {...props} />
<Accordion.Item.Collapse {...collapse} />
</Accordion.Item>
);
},
);
const Demo = () => {
const [value, setValue] = React.useState<number[]>([]);
return (
<Accordion value={value} onChange={setValue}>
<DataTable data={data} aria-label={'Table title. In table'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body>
<DataTable.Row tag={RowAccordion}>
{(_props, _row, index) => {
return {
value: index,
active: value.includes(index),
collapse: {
children: (
<DataTable data={data} aria-label={'Table title. In table'}>
{/* [1] Hide the table header */}
<DataTable.Head hidden>
{/* [2] Set "inherit" to use the size from the top table for each column. */}
<DataTable.Column name='keyword' flex='inherit' />
<DataTable.Column name='kd' flex='inherit' />
<DataTable.Column name='cpc' flex='inherit' />
<DataTable.Column name='vol' flex='inherit' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
),
},
};
}}
</DataTable.Row>
<DataTable.Cell data={data} name='keyword'>
{(props) => {
return {
children: (
<Flex alignItems='center'>
<Accordion.Item.Chevron color='icon-secondary-neutral' mr={2} />
{props.children}
</Flex>
),
};
}}
</DataTable.Cell>
</DataTable.Body>
</DataTable>
</Accordion>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Table in table with fixed column
Refer to the example with the table inside the table.
- Set the desired
z-index
. - Set the variable to block the scroll.
- Set the variable to remove overflow.
import React from 'react';
import DataTable from '@semcore/data-table';
import Accordion from '@semcore/accordion';
import { Flex } from '@semcore/flex-box';
const RowAccordion = React.forwardRef(
({ value, collapse = {}, ...props }: any, ref: React.Ref<HTMLDivElement>) => {
return (
<Accordion.Item value={value} ref={ref}>
<Accordion.Item.Toggle {...props} />
<Accordion.Item.Collapse {...collapse} />
</Accordion.Item>
);
},
);
const Demo = () => {
const [value, setValue] = React.useState<number[]>([]);
return (
<Accordion value={value} onChange={setValue}>
<DataTable data={data} aria-label={'Table title. Table in table'}>
<DataTable.Head wMin={1000}>
<DataTable.Column name='keyword' children='Keyword' fixed='left' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body>
<DataTable.Row tag={RowAccordion}>
{(_props, _row, index) => {
return {
value: index,
active: value.includes(index),
collapse: {
children: (
<DataTable data={data} aria-label={'Table title. In accordion'}>
{/* [1] Set the desired z-index */}
<DataTable.Head hidden z-index={1}>
<DataTable.Column name='keyword' flex='inherit' fixed='left' />
<DataTable.Column name='kd' flex='inherit' />
<DataTable.Column name='cpc' flex='inherit' />
<DataTable.Column name='vol' flex='inherit' />
</DataTable.Head>
{/* [2] Set a variable to block the scroll */}
<DataTable.Body disabledScroll />
</DataTable>
),
},
};
}}
</DataTable.Row>
<DataTable.Cell data={data} name='keyword'>
{(props) => {
return {
children: (
<Flex alignItems='center'>
<Accordion.Item.Chevron color='icon-secondary-neutral' mr={2} />
{props.children}
</Flex>
),
};
}}
</DataTable.Cell>
</DataTable.Body>
</DataTable>
</Accordion>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Adding additional elements to table body
Components added to <DataTable.Body/>
will be inserted at the end of the table body. Use z-index=1
to block fixed columns or z-index=2
to block scrolling if needed.
import React from 'react';
import DataTable from '@semcore/data-table';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Additional elements'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body>
<div
style={{
position: 'absolute',
width: '100%',
height: 'calc(45px * 2)',
left: 0,
bottom: 0,
background:
'linear-gradient(45deg, rgba(255, 187, 51, 0.3) 25%, rgba(85, 136, 170, 0.3) 0px, rgba(85, 136,' +
' 170, 0.3)' +
' 50%,' +
' rgba(255, 187, 51, 0.3) 0px, rgba(255, 187, 51, 0.3) 75%, rgba(85, 136, 170, 0.3) 0px) 0% 0% / 42px 42px',
zIndex: 2,
}}
/>
</DataTable.Body>
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
export default Demo;
Custom styles for table body
It’s an example of a custom styles for the table body, which uses CSS variables to set the correct cell width in a flex row. To reuse column sizes, use CSS variables like var(--<%column-name%>_width)
.
import React from 'react';
import DataTable from '@semcore/data-table';
import { Box, Flex } from '@semcore/flex-box';
const Demo = () => {
return (
<DataTable data={data} aria-label={'Table title. Custom footer cells'}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body>
<Flex role={'row'}>
<Box p={3} style={{ width: 'var(--keyword_width)' }} role={'gridcell'} tabIndex={-1}>
Summary
</Box>
<Box p={3} style={{ width: 'var(--kd_width)' }} role={'gridcell'} tabIndex={-1} />
<Box p={3} style={{ width: 'var(--cpc_width)' }} role={'gridcell'} tabIndex={-1}>
{data.reduce((sum, row) => sum + row.cpc, 0)}
</Box>
<Box p={3} style={{ width: 'var(--vol_width)' }} role={'gridcell'} tabIndex={-1}>
{data.reduce((sum, row) => sum + row.vol, 0)}
</Box>
</Flex>
</DataTable.Body>
</DataTable>
);
};
const data = [
{
keyword: 'ebay buy',
kd: 77.8,
cpc: 125,
vol: 32500000,
},
{
keyword: 'www.ebay.com',
kd: 11.2,
cpc: 3.4,
vol: 65457920,
},
{
keyword: 'www.ebay.com',
kd: 10,
cpc: 0.65,
vol: 47354640,
},
{
keyword: 'ebay buy',
kd: 0,
cpc: 0,
vol: 0,
},
{
keyword: 'ebay buy',
kd: 75.89,
cpc: 0,
vol: 21644290,
},
];
export default Demo;
Export to image
import React from 'react';
import { Flex } from '@semcore/flex-box';
import DropdownMenu from '@semcore/dropdown-menu';
import Button from '@semcore/button';
import FileExportM from '@semcore/icon/FileExport/m';
import DataTable from '@semcore/data-table';
const extensions = ['png', 'jpeg', 'webp'];
const Demo = () => {
const tableRef = React.useRef<HTMLDivElement>(null);
const ref = React.useRef<SVGForeignObjectElement>(null);
const width = 500;
const height = 300;
const downloadImage = React.useCallback(
(extention: string) => async () => {
if (tableRef.current) {
ref.current?.append(tableRef.current.cloneNode(true));
const svgElement = ref.current?.parentElement;
if (svgElement) {
let svgText = svgElementToSvgText(svgElement);
svgText = svgText.replace('xmlns="http://www.w3.org/1999/xhtml"', '');
svgText = svgText.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgText = svgText.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
const downloadUrl = await svgText2DownloadUrl(svgText, 2 * width, 2 * height, extention);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = `image.${extention}`;
link.dispatchEvent(
new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window,
}),
);
setTimeout(() => {
link.remove();
}, 100);
}
}
},
[],
);
return (
<Flex>
<div style={{ display: 'none' }}>
<svg xmlns='http://www.w3.org/2000/svg' width='500' height='300' aria-hidden='true'>
<foreignObject width='100%' height='100%' ref={ref} />
</svg>
</div>
<DataTable data={data} aria-label={'Table title. Export in image'} ref={tableRef} w={500}>
<DataTable.Head>
<DataTable.Column name='keyword' children='Keyword' />
<DataTable.Column name='kd' children='KD,%' />
<DataTable.Column name='cpc' children='CPC' />
<DataTable.Column name='vol' children='Vol.' />
</DataTable.Head>
<DataTable.Body />
</DataTable>
<DropdownMenu>
<DropdownMenu.Trigger tag={Button} ml={4}>
<Button.Addon>
<FileExportM />
</Button.Addon>
<Button.Text>Export</Button.Text>
</DropdownMenu.Trigger>
<DropdownMenu.Popper wMax='257px' aria-label={'Extensions'}>
<DropdownMenu.List>
{extensions.map((name) => (
<DropdownMenu.Item key={name} onClick={downloadImage(name)}>
{name}
</DropdownMenu.Item>
))}
</DropdownMenu.List>
</DropdownMenu.Popper>
</DropdownMenu>
</Flex>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290',
},
];
const getCSSStyles = (parentElement: Element) => {
const selectorTextArr: string[] = [];
for (let c = 0; c < parentElement.classList.length; c++) {
if (!selectorTextArr.includes(`.${parentElement.classList[c]}`))
selectorTextArr.push(`.${parentElement.classList[c]}`);
}
// Add Children element Ids and Classes to the list
const nodes = parentElement.getElementsByTagName('*');
for (let i = 0; i < nodes.length; i++) {
const id = nodes[i].id;
if (!selectorTextArr.includes(`#${id}`)) selectorTextArr.push(`#${id}`);
const classes = nodes[i].classList;
for (let c = 0; c < classes.length; c++)
if (!selectorTextArr.includes(`.${classes[c]}`)) selectorTextArr.push(`.${classes[c]}`);
}
// Extract CSS Rules
let extractedCSSText = '';
for (let i = 0; i < document.styleSheets.length; i++) {
const s = document.styleSheets[i];
try {
if (!s.cssRules) continue;
} catch (e: any) {
if (e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
const cssRules: any = s.cssRules;
for (let r = 0; r < cssRules.length; r++) {
if (
cssRules[r].selectorText &&
selectorTextArr.some((s) => cssRules[r].selectorText.includes(s))
)
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
};
const appendCSS = (cssText: string, element: Element) => {
const styleElement = document.createElement('style');
styleElement.setAttribute('type', 'text/css');
styleElement.innerHTML = cssText;
const refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore(styleElement, refNode);
};
const svgElementToSvgText = (svgNode: Element) => {
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
const cssStyleText = getCSSStyles(svgNode);
appendCSS(cssStyleText, svgNode);
const serializer = new XMLSerializer();
const svgString = serializer.serializeToString(svgNode);
return svgString;
};
const svgText2DownloadUrl = async (svg: string, width: number, height: number, format: string) =>
new Promise<string>((resolve, reject) => {
const imgsrc = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svg)))}`;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
const image = new Image();
image.onload = function () {
context?.clearRect(0, 0, width, height);
context?.drawImage(image, 0, 0, width, height);
const img = canvas.toDataURL(`image/${format}`);
resolve(img);
};
image.onerror = reject;
image.src = imgsrc;
});
export default Demo;