Customizing your DataGrid component with Tailwind CSS

By raduยท

Customizing your DataGrid component with Tailwind CSS
We haven't spoken about this very much, but Infinite Table does offer you very powerful ways to customize the structure of your component. After all, we're a React-first DataGrid, so the component and composition patterns it offers should feel at home in a React app.
Default structure of InfiniteTable
<DataSource>
  <InfiniteTable />
</DataSource>

Customizing the nesting of the InfiniteTable component#

However, be aware that you the <InfiniteTable /> component doesn't have to be a direct child of the <DataSource /> component. The <DataSource /> component doesn't actually render anything, but its job is to load, process and prepare the data in a way that <InfiniteTable /> understands and can display. And actually you can use the DataSource context to gain access to the data yourself.
InfiniteTable can be nested anywhere inside the <DataSource /> component
<DataSource>
  <h1>Your DataGrid</h1>
  <App>
    <InfiniteTable />
  </App>
</DataSource>
Inside the <DataSource /> component you can use the DataSource-provided context via the useDataSourceState hook that our component exposes.

Choosing what to render#

Besides the flexibility of nesting your DataGrid component anywhere in your app, we also offer you the ability to choose what parts of the DataGrid you want to render and where.
Let's suppose you want to show the header after the body of the DataGrid or choose to insert something in between. That should be easy, right? It is with Infinite! - but try to do that with the other commercial DataGrids out there!
View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  InfiniteTableColumn,
  DataSourceGroupBy,
  components,
  useDataSourceState,
} from '@infinite-table/infinite-react';
import * as React from 'react';

const { CheckBox } = components;

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const columns: Record<string, InfiniteTableColumn<Developer>> = {
  age: {
    field: 'age',
    header: 'Age',
    type: 'number',
    defaultWidth: 100,
    renderValue: ({ value }) => value,
  },
  salary: {
    field: 'salary',
    type: 'number',
    defaultWidth: 210,
  },
  currency: { field: 'currency', header: 'Currency', defaultWidth: 100 },
  preferredLanguage: {
    field: 'preferredLanguage',
    header: 'Programming Language',
  },

  canDesign: {
    defaultWidth: 135,
    field: 'canDesign',
    header: 'Design Skills',
    renderValue: ({ value }) => {
      return (
        <div style={{ display: 'flex', alignItems: 'center' }}>
          <CheckBox
            defaultChecked={value === null ? null : value === 'yes'}
            domProps={{
              style: {
                marginRight: 10,
              },
            }}
          />
          {value === null ? 'Some' : value === 'yes' ? 'Yes' : 'No'}
        </div>
      );
    },
  },
  country: {
    field: 'country',
    header: 'Country',
  },
  firstName: { field: 'firstName', header: 'First Name' },
  stack: { field: 'stack', header: 'Stack' },

  city: {
    field: 'city',
    header: 'City',
    renderHeader: ({ column }) => `${column.computedVisibleIndex} City`,
  },
};

export default function App() {
  const groupBy: DataSourceGroupBy<Developer>[] = React.useMemo(
    () => [
      {
        field: 'country',
      },
      { field: 'stack' },
    ],
    [],
  );

  return (
    <div className="flex flex-col h-[80vh] text-foreground bg-background">
      <DataSource<Developer>
        data={dataSource}
        primaryKey="id"
        defaultGroupBy={groupBy}
      >
        <h1 className="text-2xl">Your DataGrid</h1>
        <AppGrid />
      </DataSource>
    </div>
  );
}

function AppGrid() {
  const { dataArray } = useDataSourceState();
  return (
    <div className="flex-1 flex flex-col gap-1 ">
      <InfiniteTable<Developer>
        groupRenderStrategy="single-column"
        defaultActiveRowIndex={0}
        columns={columns}
        columnDefaultWidth={150}
      >
        <div className="flex flex-1 flex-row">
          <div className="flex flex-1 flex-col">
            <InfiniteTable.Body />
            <div className="text-foreground text-lg p-3">
              Showing {dataArray.length} rows
            </div>
            <InfiniteTable.Header />
          </div>
          <InfiniteTable.GroupingToolbar
            orientation="vertical"
            domProps={{
              className: 'bg-orange-700',
            }}
          />
        </div>
      </InfiniteTable>
    </div>
  );
}

const dataSource = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};
As demoed above, the good part is that you can very easily add additional elements to your structure and have the grouping toolbar displayed on the side, vertically.
Example structure for vertical grouping toolbar
<DataSource>
  <InfiniteTable>
    <div className="flex flex-1 flex-row">
      <div className="flex flex-1 flex-col">
        <InfiniteTable.Header />
        <InfiniteTable.Body />
      </div>
      <InfiniteTable.GroupingToolbar orientation="vertical" />
    </div>
  </InfiniteTable>
</DataSource>
In the example above, try dragging the header of the age column onto the GroupingToolbar to add grouping by age.

Usage with TailwindCSS#

Our Tailwind DataGrid example below shows how you can leverage Tailwind CSS to style the DataGrid.
When setting up InfiniteTable in a TailwindCSS app, you'll need to use the CSS layer that is defined in our styles, called 'infinite-table'.
Basically, you have to update your app to list this CSS layer in the proper order.
Tailwind CSS docs on layer ordering list the CSS layers that Tailwind works with.
Default tailwind CSS layer order
@layer theme, base, components, utilities;

@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css" layer(utilities);
We need to insert 'infinite-table' CSS layer before the Tailwind 'components' layer, so Tailwind CSs can easily override our styles.
But we to put it before 'base' so the Tailwind resets don't unexpectedly affect Infinite Table.
Tailwind CSS layers with infinite-table layer specified in the correct position
@layer theme, base, infinite-table, components, utilities;

@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css" layer(utilities);
Using Tailwind to customize all cells
In this demo, hover over a cell to see a Tailwind color applied. This affects all cells, as the Tailwind CSS className was applied by overriding the className of all columnTypes.
Also we applied px-6 to change the horizontal padding.
View Mode
Fork

Advanced Tailwind configuration#

The next example provides you with a more in-depth configuration.
For changing the className for rows on hover, use the rowHoverClassName prop.
In this example, we applied bg-orange-900/70! (notice the Tailwind important ! modifier) - because the default Infinite styles target the background of the rows, and not the background-color as Tailwind targets. Hence the important CSS modifier.
Advanced Tailwind configuration
View Mode
Fork