Table 10.18.0
A component for displaying large amounts of data in rows and columns. Based on TanStack Table v8.
Default
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
Test | Test | 60 | 200 | 200 | 200 | 200 |
Test | Test | 90 | 300 | 300 | 300 | 300 |
Test | Test | 120 | 400 | 400 | 400 | 400 |
"use client";
import { useCallback, useMemo } from "react";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type { ColumnDef } from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DefaultHelper = {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
activity: number;
};
const Example = () => {
const makeData = useCallback((length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
activity: Math.floor(index * 100),
};
});
}, []);
const columns = useMemo<ColumnDef<{}, DefaultHelper>[]>(
() => [
{
id: "firstName",
header: () => "First Name",
accessorKey: "firstName",
},
{
id: "lastName",
header: () => "Last Name",
accessorKey: "lastName",
},
{
id: "age",
header: () => "Age",
accessorKey: "age",
cell: (props) => props.getValue(),
},
{
id: "visits",
header: () => "Visits",
accessorKey: "visits",
cell: (props) => props.getValue(),
},
{
id: "progress",
header: () => "Progress",
accessorKey: "progress",
cell: (props) => props.getValue(),
},
{
id: "activity",
header: () => "Activity",
accessorKey: "activity",
},
{
id: "status",
header: () => "Status",
accessorKey: "status",
},
],
[],
);
const data = useMemo(() => makeData(5), [makeData]);
return (
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} />
</div>
);
};
export default Example;
Different row gaps
Examples with gap values of: 0, 2px (default), 4px, 8px and 12px.
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
"use client";
import { useCallback, useMemo } from "react";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type { ColumnDef } from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DefaultHelper = {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
activity: number;
};
const Example = () => {
const makeData = useCallback((length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
activity: Math.floor(index * 100),
};
});
}, []);
const columns = useMemo<ColumnDef<{}, DefaultHelper>[]>(
() => [
{
id: "firstName",
header: () => "First Name",
accessorKey: "firstName",
},
{
id: "lastName",
header: () => "Last Name",
accessorKey: "lastName",
},
{
id: "age",
header: () => "Age",
accessorKey: "age",
cell: (props) => props.getValue(),
},
{
id: "visits",
header: () => "Visits",
accessorKey: "visits",
cell: (props) => props.getValue(),
},
{
id: "progress",
header: () => "Progress",
accessorKey: "progress",
cell: (props) => props.getValue(),
},
{
id: "activity",
header: () => "Activity",
accessorKey: "activity",
},
{
id: "status",
header: () => "Status",
accessorKey: "status",
},
],
[],
);
const data = useMemo(() => makeData(2), [makeData]);
return (
<>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} rowGap="0" />
</div>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} />
</div>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} rowGap="4px" />
</div>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} rowGap="8px" />
</div>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} rowGap="12px" />
</div>
</>
);
};
export default Example;
Different row sizes
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
"use client";
import { useCallback, useMemo } from "react";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type { ColumnDef } from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DefaultHelper = {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
activity: number;
};
const Example = () => {
const makeData = useCallback((length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
activity: Math.floor(index * 100),
};
});
}, []);
const columns = useMemo<ColumnDef<{}, DefaultHelper>[]>(
() => [
{
id: "firstName",
header: () => "First Name",
accessorKey: "firstName",
},
{
id: "lastName",
header: () => "Last Name",
accessorKey: "lastName",
},
{
id: "age",
header: () => "Age",
accessorKey: "age",
cell: (props) => props.getValue(),
},
{
id: "visits",
header: () => "Visits",
accessorKey: "visits",
cell: (props) => props.getValue(),
},
{
id: "progress",
header: () => "Progress",
accessorKey: "progress",
cell: (props) => props.getValue(),
},
{
id: "activity",
header: () => "Activity",
accessorKey: "activity",
},
{
id: "status",
header: () => "Status",
accessorKey: "status",
},
],
[],
);
const data = useMemo(() => makeData(2), [makeData]);
return (
<>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} rowSize="xs" />
</div>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} rowSize="sm" />
</div>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} />
</div>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} rowSize="lg" />
</div>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} rowSize="xl" />
</div>
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table columns={columns} data={data} rowSize="2xl" />
</div>
</>
);
};
export default Example;
Cell borders
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
Test | Test | 60 | 200 | 200 | 200 | 200 |
Test | Test | 90 | 300 | 300 | 300 | 300 |
Test | Test | 120 | 400 | 400 | 400 | 400 |
"use client";
import { useMemo, useCallback } from "react";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type { ColumnDef } from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DefaultHelper = {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
activity: number;
};
const Example = () => {
const makeData = useCallback((length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
activity: Math.floor(index * 100),
};
});
}, []);
const columns = useMemo<ColumnDef<{}, DefaultHelper>[]>(
() => [
{
id: "firstName",
header: () => "First Name",
accessorKey: "firstName",
},
{
id: "lastName",
header: () => "Last Name",
accessorKey: "lastName",
},
{
id: "age",
header: () => "Age",
accessorKey: "age",
cell: (props) => props.getValue(),
},
{
id: "visits",
header: () => "Visits",
accessorKey: "visits",
cell: (props) => props.getValue(),
},
{
id: "progress",
header: () => "Progress",
accessorKey: "progress",
cell: (props) => props.getValue(),
},
{
id: "activity",
header: () => "Activity",
accessorKey: "activity",
},
{
id: "status",
header: () => "Status",
accessorKey: "status",
},
],
[],
);
const data = useMemo(() => makeData(5), [makeData]);
return (
<div className="w-full max-w-screen-lg border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
layout="stretched-auto"
isResizable
withCellBorder
/>
</div>
);
};
export default Example;
Clickable rows
This example demonstrates the capabilities of a table with clickable rows.
Use the mouse wheel or the arrow keys on the keyboard to scroll the data up and down.
Watch the result of clicking on the rows in the browser console.
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
Test | Test | 60 | 200 | 200 | 200 | 200 |
Test | Test | 90 | 300 | 300 | 300 | 300 |
Test | Test | 120 | 400 | 400 | 400 | 400 |
Test | Test | 150 | 500 | 500 | 500 | 500 |
Test | Test | 180 | 600 | 600 | 600 | 600 |
Test | Test | 210 | 700 | 700 | 700 | 700 |
Test | Test | 240 | 800 | 800 | 800 | 800 |
Test | Test | 270 | 900 | 900 | 900 | 900 |
Test | Test | 300 | 1000 | 1000 | 1000 | 1000 |
Test | Test | 330 | 1100 | 1100 | 1100 | 1100 |
Test | Test | 360 | 1200 | 1200 | 1200 | 1200 |
Test | Test | 390 | 1300 | 1300 | 1300 | 1300 |
Test | Test | 420 | 1400 | 1400 | 1400 | 1400 |
Test | Test | 450 | 1500 | 1500 | 1500 | 1500 |
Test | Test | 480 | 1600 | 1600 | 1600 | 1600 |
Test | Test | 510 | 1700 | 1700 | 1700 | 1700 |
Test | Test | 540 | 1800 | 1800 | 1800 | 1800 |
Test | Test | 570 | 1900 | 1900 | 1900 | 1900 |
"use client";
import { useCallback, useMemo, useState } from "react";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type {
ColumnDef,
Row,
} from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DataTypeHelper = {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
activity: number;
};
const Example = () => {
const makeData = useCallback((length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
activity: Math.floor(index * 100),
};
});
}, []);
const [data, setData] = useState(makeData(20));
const columns = useMemo<ColumnDef<{}, DataTypeHelper>[]>(
() => [
{
header: () => "First Name",
accessorKey: "firstName",
},
{
header: () => "Last Name",
accessorKey: "lastName",
},
{
header: () => "Age",
accessorKey: "age",
cell: (props) => props.getValue(),
},
{
header: () => "Visits",
accessorKey: "visits",
cell: (props) => props.getValue(),
},
{
header: () => "Progress",
accessorKey: "progress",
cell: (props) => props.getValue(),
},
{
header: () => "Activity",
accessorKey: "activity",
},
{
header: () => "Status",
accessorKey: "status",
},
],
[],
);
return (
<div className="border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
width={800}
height={400}
layout="stretched-auto"
rowActiveColor="goku"
rowHoverColor="beerus"
isSelectable={true}
getOnRowClickHandler={(row: Row<{}>) => () => {
console.log(`You clicked row with ID - ${row.id}`);
}}
/>
</div>
);
};
export default Example;
Expandable rows
This example demonstrates the capabilities of a table with pre-set expandable rows.
Use the mouse wheel or the arrow keys on the keyboard to scroll the data up and down.
Name | Info | Actions | ||||
---|---|---|---|---|---|---|
More Info | ||||||
First Name | Last Name | Age | Visits | Status | Profile Progress | Actions |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
FirstName | LastName | 40 | 1000 | complicated | 100 | |
Name | Info | Actions |
"use client";
import { useCallback, useMemo, useState } from "react";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import {
ArrowsRefreshRound,
ControlsChevronDown,
ControlsChevronRight,
} from "@heathmont/moon-icons-tw";
import { Chip, Tooltip } from "@heathmont/moon-core-tw";
import type DataHelper from "@heathmont/moon-table-v8-tw/lib/es/private/types/DataHelper";
import type {
ExpandedState,
ColumnDef,
} from "@heathmont/moon-table-v8-tw/lib/es/private/types";
interface Person extends DataHelper {
firstName: string;
lastName: string;
age: number;
visits: number;
progress: number;
status: "relationship" | "complicated" | "single";
actions: JSX.Element;
subRows?: Person[];
}
const Example = () => {
const range = useCallback((len: number) => {
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(i);
}
return arr;
}, []);
const tooltip = useMemo(
() => (
<Tooltip>
<Tooltip.Trigger className="max-h-6">
<Chip
variant="ghost"
iconOnly={<ArrowsRefreshRound className="text-moon-24 max-h-6" />}
onClick={() => {
window.location.reload();
}}
/>
</Tooltip.Trigger>
<Tooltip.Content position="top-start" className="z-[2]">
Reload page
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip>
),
[],
);
const newPerson = useMemo((): Person => {
return {
firstName: "FirstName",
lastName: "LastName",
age: 40,
visits: 1000,
progress: 100,
status: "complicated",
actions: tooltip,
};
}, [tooltip]);
const makeData = useCallback(
(...lens: number[]) => {
const makeDataLevel = (depth = 0): Person[] => {
const len = lens[depth]!;
return range(len).map((d): Person => {
return {
...newPerson,
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
};
});
};
return makeDataLevel();
},
[newPerson, range],
);
const columns = useMemo<ColumnDef<{}, Person>[]>(
() => [
{
header: "Name",
footer: "Name",
columns: [
{
accessorKey: "firstName",
header: ({ table }) => (
<>
<button onClick={table.getToggleAllRowsExpandedHandler()}>
{table.getIsAllRowsExpanded() ? (
<ControlsChevronDown />
) : (
<ControlsChevronRight />
)}
</button>{" "}
First Name
</>
),
cell: ({ row, getValue }) => (
<div
style={{
paddingLeft: `${row.depth * 2}rem`,
}}
className="flex gap-x-1"
>
<>
{row.getCanExpand() ? (
<button
className="cursor-pointer"
onClick={row.getToggleExpandedHandler()}
>
{row.getIsExpanded() ? (
<ControlsChevronDown />
) : (
<ControlsChevronRight />
)}
</button>
) : null}
{getValue()}
</>
</div>
),
},
{
accessorFn: (row: Person) => row.lastName,
id: "lastName",
cell: (info) => info.getValue(),
header: () => <span>Last Name</span>,
},
],
},
{
header: "Info",
footer: "Info",
columns: [
{
accessorKey: "age",
header: "Age",
},
{
header: "More Info",
columns: [
{
accessorKey: "visits",
header: () => <span>Visits</span>,
},
{
accessorKey: "status",
header: "Status",
},
{
accessorKey: "progress",
header: "Profile Progress",
},
],
},
],
},
{
id: "actions",
header: "Actions",
footer: (props) => "Actions",
columns: [
{
header: "Actions",
accessorKey: "actions",
cell: (props) => props.getValue(),
},
],
},
],
[],
);
const preset: ExpandedState = {
"0": true,
"0.2": true,
"0.2.1": true,
};
const [expanded, setExpanded] = useState<ExpandedState>(preset);
const [data, setData] = useState(makeData(10, 5, 3));
const getSubRows = useCallback(
({ subRows }: DataHelper) => subRows as DataHelper[],
[],
);
return (
<div className="border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
width={800}
height={600}
layout="stretched-auto"
state={{ expanded }}
getSubRows={getSubRows}
onExpandedChange={setExpanded}
withFooter={true}
/>
</div>
);
};
export default Example;
Selectable rows
This example demonstrates the capabilities of a table with pre-set selectable rows.
Use the mouse wheel or the arrow keys on the keyboard to scroll the data up and down.
Watch the result of the row selection in the browser console.
First Name | Last Name | Age | Visits | Progress | Activity | Status | Actions |
---|---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 | |
Test | Test | 30 | 100 | 100 | 100 | 100 | |
Test | Test | 60 | 200 | 200 | 200 | 200 | |
Test | Test | 90 | 300 | 300 | 300 | 300 | |
Test | Test | 120 | 400 | 400 | 400 | 400 | |
Test | Test | 150 | 500 | 500 | 500 | 500 | |
Test | Test | 180 | 600 | 600 | 600 | 600 | |
Test | Test | 210 | 700 | 700 | 700 | 700 | |
Test | Test | 240 | 800 | 800 | 800 | 800 | |
Test | Test | 270 | 900 | 900 | 900 | 900 | |
Test | Test | 300 | 1000 | 1000 | 1000 | 1000 | |
Test | Test | 330 | 1100 | 1100 | 1100 | 1100 | |
Test | Test | 360 | 1200 | 1200 | 1200 | 1200 | |
Test | Test | 390 | 1300 | 1300 | 1300 | 1300 | |
Test | Test | 420 | 1400 | 1400 | 1400 | 1400 | |
Test | Test | 450 | 1500 | 1500 | 1500 | 1500 | |
Test | Test | 480 | 1600 | 1600 | 1600 | 1600 | |
Test | Test | 510 | 1700 | 1700 | 1700 | 1700 | |
Test | Test | 540 | 1800 | 1800 | 1800 | 1800 | |
Test | Test | 570 | 1900 | 1900 | 1900 | 1900 |
"use client";
import { ReactNode, useCallback, useMemo, useState } from "react";
import { Chip, Tooltip } from "@heathmont/moon-core-tw";
import { ArrowsRefreshRound } from "@heathmont/moon-icons-tw";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type {
ColumnDef,
Row,
RowSelectionState,
} from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DataTypeHelper = {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
activity: number;
actions: () => void;
};
const preset: RowSelectionState = {
1: true,
3: true,
4: true,
11: true,
16: true,
};
const Example = () => {
const tooltip = useMemo(
() => (
<Tooltip>
<Tooltip.Trigger className="max-h-6">
<Chip
variant="ghost"
iconOnly={<ArrowsRefreshRound className="text-moon-24 max-h-6" />}
onClick={() => {
window.location.reload();
}}
/>
</Tooltip.Trigger>
<Tooltip.Content position="top-start" className="z-[2]">
Reload page
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip>
),
[],
);
const makeData = useCallback(
(length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
activity: Math.floor(index * 100),
actions: tooltip,
};
});
},
[tooltip],
);
const [rowSelection, setRowSelection] = useState<RowSelectionState>(preset);
const [data, setData] = useState(makeData(20));
const columns = useMemo<ColumnDef<{}, DataTypeHelper>[]>(
() => [
{
header: () => "First Name",
accessorKey: "firstName",
},
{
header: () => "Last Name",
accessorKey: "lastName",
},
{
header: () => "Age",
accessorKey: "age",
cell: (props) => (
<div onClick={props.row.getToggleSelectedHandler()}>
{props.getValue() as unknown as ReactNode}
</div>
),
},
{
header: () => "Visits",
accessorKey: "visits",
cell: (props) => (
<div onClick={props.row.getToggleSelectedHandler()}>
{props.getValue() as unknown as ReactNode}
</div>
),
},
{
header: () => "Progress",
accessorKey: "progress",
cell: (props) => (
<div onClick={props.row.getToggleSelectedHandler()}>
{props.getValue() as unknown as ReactNode}
</div>
),
},
{
header: () => "Activity",
accessorKey: "activity",
},
{
header: () => "Status",
accessorKey: "status",
},
{
header: () => "Actions",
accessorKey: "actions",
cell: (props) => props.getValue(),
},
],
[],
);
return (
<div className="border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
width={800}
height={400}
layout="stretched-auto"
state={{ rowSelection }}
onRowSelectionChange={setRowSelection}
isSelectable={true}
getOnRowSelectHandler={() => (rows: Row<{}>[]) => {
console.log(
`IDs of selected rows - ${rows.map((row: Row<{}>) => row.id)}`,
);
}}
/>
</div>
);
};
export default Example;
Selectable rows with checkboxes
This example demonstrates the capabilities of a table with pre-set checkboxes in the selectable rows.
Use the mouse wheel or the arrow keys on the keyboard to scroll the data up and down.
Watch the result of the row selection in the browser console.
First Name | Last Name | Age | Visits | Progress | Activity | Status | Actions | |
---|---|---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 | ||
Test | Test | 30 | 100 | 100 | 100 | 100 | ||
Test | Test | 60 | 200 | 200 | 200 | 200 | ||
Test | Test | 90 | 300 | 300 | 300 | 300 | ||
Test | Test | 120 | 400 | 400 | 400 | 400 | ||
Test | Test | 150 | 500 | 500 | 500 | 500 | ||
Test | Test | 180 | 600 | 600 | 600 | 600 | ||
Test | Test | 210 | 700 | 700 | 700 | 700 | ||
Test | Test | 240 | 800 | 800 | 800 | 800 | ||
Test | Test | 270 | 900 | 900 | 900 | 900 | ||
Test | Test | 300 | 1000 | 1000 | 1000 | 1000 | ||
Test | Test | 330 | 1100 | 1100 | 1100 | 1100 | ||
Test | Test | 360 | 1200 | 1200 | 1200 | 1200 | ||
Test | Test | 390 | 1300 | 1300 | 1300 | 1300 | ||
Test | Test | 420 | 1400 | 1400 | 1400 | 1400 | ||
Test | Test | 450 | 1500 | 1500 | 1500 | 1500 | ||
Test | Test | 480 | 1600 | 1600 | 1600 | 1600 | ||
Test | Test | 510 | 1700 | 1700 | 1700 | 1700 | ||
Test | Test | 540 | 1800 | 1800 | 1800 | 1800 | ||
Test | Test | 570 | 1900 | 1900 | 1900 | 1900 |
"use client";
import { useCallback, useMemo, useState } from "react";
import { Checkbox, Chip, Tooltip } from "@heathmont/moon-core-tw";
import { ArrowsRefreshRound } from "@heathmont/moon-icons-tw";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type {
ColumnDef,
Row,
RowSelectionState,
} from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DataTypeHelper = {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
activity: number;
actions: () => void;
};
const preset: RowSelectionState = {
1: true,
3: true,
4: true,
11: true,
16: true,
};
const Example = () => {
const tooltip = useMemo(
() => (
<Tooltip>
<Tooltip.Trigger className="max-h-6">
<Chip
variant="ghost"
iconOnly={<ArrowsRefreshRound className="text-moon-24 max-h-6" />}
onClick={() => {
window.location.reload();
}}
/>
</Tooltip.Trigger>
<Tooltip.Content position="top-start" className="z-[2]">
Reload page
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip>
),
[],
);
const makeData = useCallback(
(length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
activity: Math.floor(index * 100),
actions: tooltip,
};
});
},
[tooltip],
);
const [rowSelection, setRowSelection] = useState<RowSelectionState>(preset);
const [data, setData] = useState(makeData(20));
const columns = useMemo<ColumnDef<{}, DataTypeHelper>[]>(
() => [
{
id: "select",
header: ({ table }) => (
<div className="w-8 px-1">
<Checkbox
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
</div>
),
cell: ({ row }) => (
<div className="w-8 px-1">
<Checkbox
checked={row.getIsSelected()}
disabled={!row.getCanSelect()}
onChange={row.getToggleSelectedHandler()}
/>
</div>
),
},
{
header: () => "First Name",
accessorKey: "firstName",
},
{
header: () => "Last Name",
accessorKey: "lastName",
},
{
header: () => "Age",
accessorKey: "age",
cell: (props) => props.getValue(),
},
{
header: () => "Visits",
accessorKey: "visits",
cell: (props) => props.getValue(),
},
{
header: () => "Progress",
accessorKey: "progress",
cell: (props) => props.getValue(),
},
{
header: () => "Activity",
accessorKey: "activity",
},
{
header: () => "Status",
accessorKey: "status",
},
{
header: () => "Actions",
accessorKey: "actions",
cell: (props) => props.getValue(),
},
],
[],
);
return (
<div className="border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
width={800}
height={400}
layout="stretched-auto"
state={{ rowSelection }}
onRowSelectionChange={setRowSelection}
preventSelectionByRowClick={true}
isSelectable={true}
getOnRowSelectHandler={() => (rows: Row<{}>[]) => {
console.log(
`IDs of selected rows - ${rows.map((row: Row<{}>) => row.id)}`,
);
}}
/>
</div>
);
};
export default Example;
Expandable/Selectable rows
This example is a combination of expandable and selectable tables with pre-set parameters.
Use the mouse wheel or the arrow keys on the keyboard to scroll the data up and down.
Expand/Select | Name | Info | Actions | ||||
---|---|---|---|---|---|---|---|
First Name | Age | Visits | Activity | Status | Profile Progress | Actions | |
Lvl1 | 36 | 50 | 54 | 19 | 20 | ||
Sub lvl2 | 96 | 8 | 23 | 97 | 2 | ||
Sub lvl3 | 63 | 82 | 46 | 52 | 59 | ||
Sub lvl3 | 64 | 35 | 5 | 65 | 78 | ||
Sub lvl3 | 12 | 4 | 5 | 98 | 44 | ||
Sub lvl2 | 74 | 5 | 2 | 86 | 1 | ||
Sub lvl3 | 89 | 98 | 43 | 24 | 54 | ||
Sub lvl3 | 52 | 25 | 35 | 97 | 25 | ||
Sub lvl3 | 55 | 54 | 33 | 56 | 24 | ||
Sub lvl2 | 53 | 63 | 3 | 48 | 24 | ||
Sub lvl3 | 4 | 653 | 43 | 44 | 36 | ||
Sub lvl3 | 49 | 45 | 4 | 35 | 454 |
"use client";
import { useCallback, useMemo, useState } from "react";
import {
Checkbox,
Chip,
Tooltip,
mergeClassnames,
} from "@heathmont/moon-core-tw";
import {
ArrowsRefreshRound,
ControlsChevronDown,
ControlsChevronRight,
} from "@heathmont/moon-icons-tw";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type DataHelper from "@heathmont/moon-table-v8-tw/lib/es/private/types/DataHelper";
import type {
ColumnDef,
ExpandedState,
Row,
RowSelectionState,
TableInterface,
} from "@heathmont/moon-table-v8-tw/lib/es/private/types";
interface DataTypeHelper extends DataHelper {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
actions: () => void;
subRows?: DataTypeHelper[];
}
const columnShift = (depth: number) => {
const shiftMap: { [key: number]: string } = ["ps-0", "ps-6", "ps-12"];
return shiftMap[depth];
};
const preset: RowSelectionState = {
0: true,
"0.1": true,
"0.1.0": true,
"0.1.1": true,
};
const Example = () => {
const tooltip = useMemo(
() => (
<Tooltip>
<Tooltip.Trigger className="max-h-6">
<Chip
variant="ghost"
iconOnly={<ArrowsRefreshRound className="text-moon-24 max-h-6" />}
onClick={() => {
window.location.reload();
}}
/>
</Tooltip.Trigger>
<Tooltip.Content position="top-start" className="z-[2]">
Reload page
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip>
),
[],
);
const makeData = useMemo(
() => [
{
firstName: "Lvl1",
age: <span>36</span>,
visits: <span>50</span>,
progress: <span>20</span>,
status: 19,
activity: 54,
actions: tooltip,
subRows: [
{
firstName: "Sub lvl2",
age: <span>96</span>,
visits: <span>8</span>,
progress: <span>2</span>,
status: 97,
activity: 23,
actions: tooltip,
subRows: [
{
firstName: "Sub lvl3",
age: <span>63</span>,
visits: <span>82</span>,
progress: <span>59</span>,
status: 52,
activity: 46,
actions: tooltip,
},
{
firstName: "Sub lvl3",
age: <span>64</span>,
visits: <span>35</span>,
progress: <span>78</span>,
status: 65,
activity: 5,
actions: tooltip,
},
{
firstName: "Sub lvl3",
age: <span>12</span>,
visits: <span>4</span>,
progress: <span>44</span>,
status: 98,
activity: 5,
actions: tooltip,
},
],
},
{
firstName: "Sub lvl2",
age: <span>74</span>,
visits: <span>5</span>,
progress: <span>1</span>,
status: 86,
activity: 2,
actions: tooltip,
subRows: [
{
firstName: "Sub lvl3",
age: <span>89</span>,
visits: <span>98</span>,
progress: <span>54</span>,
status: 24,
activity: 43,
actions: tooltip,
},
{
firstName: "Sub lvl3",
age: <span>52</span>,
visits: <span>25</span>,
progress: <span>25</span>,
status: 97,
activity: 35,
actions: tooltip,
},
{
firstName: "Sub lvl3",
age: <span>55</span>,
visits: <span>54</span>,
progress: <span>24</span>,
status: 56,
activity: 33,
actions: tooltip,
},
],
},
{
firstName: "Sub lvl2",
age: <span>53</span>,
visits: <span>63</span>,
progress: <span>24</span>,
status: 48,
activity: 3,
actions: tooltip,
subRows: [
{
firstName: "Sub lvl3",
age: <span>4</span>,
visits: <span>653</span>,
progress: <span>36</span>,
status: 44,
activity: 43,
actions: tooltip,
},
{
firstName: "Sub lvl3",
age: <span>49</span>,
visits: <span>45</span>,
progress: <span>454</span>,
status: 35,
activity: 4,
actions: tooltip,
},
],
},
],
},
],
[tooltip],
);
const [rowSelection, setRowSelection] = useState<RowSelectionState>(preset);
const [expanded, setExpanded] = useState<ExpandedState>(true);
const [data, setData] = useState(makeData);
const trackCheckState = (row: Row<{}>, table: TableInterface<{}>) => {
let parentRowId = row.parentId;
while (parentRowId) {
const nodeRow = table.getRow(parentRowId);
if (nodeRow.getIsAllSubRowsSelected() && !nodeRow.getIsSelected()) {
setRowSelection({ ...rowSelection, [nodeRow.id]: true });
} else if (
!nodeRow.getIsAllSubRowsSelected() &&
nodeRow.getIsSelected()
) {
setRowSelection(
Object.keys(rowSelection)
.filter((rowId) => rowId !== nodeRow.id)
.reduce((acc: RowSelectionState, rowId: string) => {
acc[rowId] = true;
return acc;
}, {}),
);
}
parentRowId = nodeRow.parentId;
}
return row.getIsSelected();
};
const trackIndeterminateState = (row: Row<{}>) => {
const match = new RegExp(`(^${row.id}[\\.]|^${row.id}$)`, "");
const matches = Object.keys(rowSelection).filter(
(rowId) => match.test(rowId) && rowId !== row.id,
);
return (
!row.getIsAllSubRowsSelected() &&
matches.some((rowId) => rowSelection[rowId] === true)
);
};
const columns = useMemo<ColumnDef<{}, DataTypeHelper>[]>(
() => [
{
id: "expand/select",
header: () => "Expand/Select",
columns: [
{
id: "select",
header: ({ table }) => (
<div className="flex px-0 gap-x-1">
<Checkbox
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
<button onClick={table.getToggleAllRowsExpandedHandler()}>
{table.getIsAllRowsExpanded() ? (
<ControlsChevronDown />
) : (
<ControlsChevronRight />
)}
</button>
</div>
),
cell: ({ row, table }) => (
<div
className={mergeClassnames(
"flex gap-x-1",
columnShift(row.depth),
)}
>
<Checkbox
checked={trackCheckState(row, table)}
disabled={!row.getCanSelect()}
indeterminate={trackIndeterminateState(row)}
onChange={row.getToggleSelectedHandler()}
/>
{row.getCanExpand() ? (
<button
onClick={row.getToggleExpandedHandler()}
className="cursor-pointer"
>
{row.getIsExpanded() ? (
<ControlsChevronDown />
) : (
<ControlsChevronRight />
)}
</button>
) : (
""
)}
</div>
),
},
],
},
{
id: "name",
header: () => "Name",
columns: [
{
header: () => "First Name",
accessorKey: "firstName",
},
],
},
{
id: "info",
header: () => "Info",
columns: [
{
header: () => "Age",
accessorKey: "age",
cell: (props) => props.getValue(),
size: 30,
},
{
header: () => "Visits",
accessorKey: "visits",
cell: (props) => props.getValue(),
size: 60,
},
{
header: () => "Activity",
accessorKey: "activity",
size: 80,
},
{
header: () => "Status",
accessorKey: "status",
size: 80,
},
{
header: () => "Profile Progress",
accessorKey: "progress",
cell: (props) => props.getValue(),
},
],
},
{
id: "actions",
header: () => "Actions",
size: 90,
columns: [
{
header: () => "Actions",
accessorKey: "actions",
cell: (props) => props.getValue(),
size: 90,
},
],
},
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[rowSelection],
);
const getSubRows = useCallback(
({ subRows }: DataHelper) => subRows as DataHelper[],
[],
);
return (
<div className="border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
width={800}
height={400}
layout="stretched-auto"
state={{ expanded, rowSelection }}
getSubRows={getSubRows}
onExpandedChange={setExpanded}
onRowSelectionChange={setRowSelection}
preventSelectionByRowClick={true}
isSelectable={true}
/>
</div>
);
};
export default Example;
A table with minimap
Name | Info | Info1 | Info2 | Info3 | Info4 | Info5 | Progress | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
First Name | Last Name | Age | Visits | Activity | Age1 | Visits1 | Activity1 | Age2 | Visits2 | Activity2 | Age3 | Visits3 | Activity3 | Age4 | Visits4 | Activity4 | Age5 | Visits5 | Activity5 | Profile Progress |
Test | Test | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 30 | 100 | 100 | 30 | 100 | 100 | 30 | 100 | 100 | 30 | 100 | 100 | 30 | 100 | 100 | 100 |
Test | Test | 60 | 200 | 200 | 60 | 200 | 200 | 60 | 200 | 200 | 60 | 200 | 200 | 60 | 200 | 200 | 60 | 200 | 200 | 200 |
Test | Test | 90 | 300 | 300 | 90 | 300 | 300 | 90 | 300 | 300 | 90 | 300 | 300 | 90 | 300 | 300 | 90 | 300 | 300 | 300 |
Test | Test | 120 | 400 | 400 | 120 | 400 | 400 | 120 | 400 | 400 | 120 | 400 | 400 | 120 | 400 | 400 | 120 | 400 | 400 | 400 |
Test | Test | 150 | 500 | 500 | 150 | 500 | 500 | 150 | 500 | 500 | 150 | 500 | 500 | 150 | 500 | 500 | 150 | 500 | 500 | 500 |
Test | Test | 180 | 600 | 600 | 180 | 600 | 600 | 180 | 600 | 600 | 180 | 600 | 600 | 180 | 600 | 600 | 180 | 600 | 600 | 600 |
Test | Test | 210 | 700 | 700 | 210 | 700 | 700 | 210 | 700 | 700 | 210 | 700 | 700 | 210 | 700 | 700 | 210 | 700 | 700 | 700 |
Test | Test | 240 | 800 | 800 | 240 | 800 | 800 | 240 | 800 | 800 | 240 | 800 | 800 | 240 | 800 | 800 | 240 | 800 | 800 | 800 |
Test | Test | 270 | 900 | 900 | 270 | 900 | 900 | 270 | 900 | 900 | 270 | 900 | 900 | 270 | 900 | 900 | 270 | 900 | 900 | 900 |
Test | Test | 300 | 1000 | 1000 | 300 | 1000 | 1000 | 300 | 1000 | 1000 | 300 | 1000 | 1000 | 300 | 1000 | 1000 | 300 | 1000 | 1000 | 1000 |
Test | Test | 330 | 1100 | 1100 | 330 | 1100 | 1100 | 330 | 1100 | 1100 | 330 | 1100 | 1100 | 330 | 1100 | 1100 | 330 | 1100 | 1100 | 1100 |
Test | Test | 360 | 1200 | 1200 | 360 | 1200 | 1200 | 360 | 1200 | 1200 | 360 | 1200 | 1200 | 360 | 1200 | 1200 | 360 | 1200 | 1200 | 1200 |
Test | Test | 390 | 1300 | 1300 | 390 | 1300 | 1300 | 390 | 1300 | 1300 | 390 | 1300 | 1300 | 390 | 1300 | 1300 | 390 | 1300 | 1300 | 1300 |
Test | Test | 420 | 1400 | 1400 | 420 | 1400 | 1400 | 420 | 1400 | 1400 | 420 | 1400 | 1400 | 420 | 1400 | 1400 | 420 | 1400 | 1400 | 1400 |
Test | Test | 450 | 1500 | 1500 | 450 | 1500 | 1500 | 450 | 1500 | 1500 | 450 | 1500 | 1500 | 450 | 1500 | 1500 | 450 | 1500 | 1500 | 1500 |
Test | Test | 480 | 1600 | 1600 | 480 | 1600 | 1600 | 480 | 1600 | 1600 | 480 | 1600 | 1600 | 480 | 1600 | 1600 | 480 | 1600 | 1600 | 1600 |
Test | Test | 510 | 1700 | 1700 | 510 | 1700 | 1700 | 510 | 1700 | 1700 | 510 | 1700 | 1700 | 510 | 1700 | 1700 | 510 | 1700 | 1700 | 1700 |
Test | Test | 540 | 1800 | 1800 | 540 | 1800 | 1800 | 540 | 1800 | 1800 | 540 | 1800 | 1800 | 540 | 1800 | 1800 | 540 | 1800 | 1800 | 1800 |
Test | Test | 570 | 1900 | 1900 | 570 | 1900 | 1900 | 570 | 1900 | 1900 | 570 | 1900 | 1900 | 570 | 1900 | 1900 | 570 | 1900 | 1900 | 1900 |
Test | Test | 600 | 2000 | 2000 | 600 | 2000 | 2000 | 600 | 2000 | 2000 | 600 | 2000 | 2000 | 600 | 2000 | 2000 | 600 | 2000 | 2000 | 2000 |
Test | Test | 630 | 2100 | 2100 | 630 | 2100 | 2100 | 630 | 2100 | 2100 | 630 | 2100 | 2100 | 630 | 2100 | 2100 | 630 | 2100 | 2100 | 2100 |
Test | Test | 660 | 2200 | 2200 | 660 | 2200 | 2200 | 660 | 2200 | 2200 | 660 | 2200 | 2200 | 660 | 2200 | 2200 | 660 | 2200 | 2200 | 2200 |
Test | Test | 690 | 2300 | 2300 | 690 | 2300 | 2300 | 690 | 2300 | 2300 | 690 | 2300 | 2300 | 690 | 2300 | 2300 | 690 | 2300 | 2300 | 2300 |
Test | Test | 720 | 2400 | 2400 | 720 | 2400 | 2400 | 720 | 2400 | 2400 | 720 | 2400 | 2400 | 720 | 2400 | 2400 | 720 | 2400 | 2400 | 2400 |
Test | Test | 750 | 2500 | 2500 | 750 | 2500 | 2500 | 750 | 2500 | 2500 | 750 | 2500 | 2500 | 750 | 2500 | 2500 | 750 | 2500 | 2500 | 2500 |
Test | Test | 780 | 2600 | 2600 | 780 | 2600 | 2600 | 780 | 2600 | 2600 | 780 | 2600 | 2600 | 780 | 2600 | 2600 | 780 | 2600 | 2600 | 2600 |
Test | Test | 810 | 2700 | 2700 | 810 | 2700 | 2700 | 810 | 2700 | 2700 | 810 | 2700 | 2700 | 810 | 2700 | 2700 | 810 | 2700 | 2700 | 2700 |
Test | Test | 840 | 2800 | 2800 | 840 | 2800 | 2800 | 840 | 2800 | 2800 | 840 | 2800 | 2800 | 840 | 2800 | 2800 | 840 | 2800 | 2800 | 2800 |
Test | Test | 870 | 2900 | 2900 | 870 | 2900 | 2900 | 870 | 2900 | 2900 | 870 | 2900 | 2900 | 870 | 2900 | 2900 | 870 | 2900 | 2900 | 2900 |
Test | Test | 900 | 3000 | 3000 | 900 | 3000 | 3000 | 900 | 3000 | 3000 | 900 | 3000 | 3000 | 900 | 3000 | 3000 | 900 | 3000 | 3000 | 3000 |
Test | Test | 930 | 3100 | 3100 | 930 | 3100 | 3100 | 930 | 3100 | 3100 | 930 | 3100 | 3100 | 930 | 3100 | 3100 | 930 | 3100 | 3100 | 3100 |
Test | Test | 960 | 3200 | 3200 | 960 | 3200 | 3200 | 960 | 3200 | 3200 | 960 | 3200 | 3200 | 960 | 3200 | 3200 | 960 | 3200 | 3200 | 3200 |
Test | Test | 990 | 3300 | 3300 | 990 | 3300 | 3300 | 990 | 3300 | 3300 | 990 | 3300 | 3300 | 990 | 3300 | 3300 | 990 | 3300 | 3300 | 3300 |
Test | Test | 1020 | 3400 | 3400 | 1020 | 3400 | 3400 | 1020 | 3400 | 3400 | 1020 | 3400 | 3400 | 1020 | 3400 | 3400 | 1020 | 3400 | 3400 | 3400 |
Test | Test | 1050 | 3500 | 3500 | 1050 | 3500 | 3500 | 1050 | 3500 | 3500 | 1050 | 3500 | 3500 | 1050 | 3500 | 3500 | 1050 | 3500 | 3500 | 3500 |
Test | Test | 1080 | 3600 | 3600 | 1080 | 3600 | 3600 | 1080 | 3600 | 3600 | 1080 | 3600 | 3600 | 1080 | 3600 | 3600 | 1080 | 3600 | 3600 | 3600 |
Test | Test | 1110 | 3700 | 3700 | 1110 | 3700 | 3700 | 1110 | 3700 | 3700 | 1110 | 3700 | 3700 | 1110 | 3700 | 3700 | 1110 | 3700 | 3700 | 3700 |
Test | Test | 1140 | 3800 | 3800 | 1140 | 3800 | 3800 | 1140 | 3800 | 3800 | 1140 | 3800 | 3800 | 1140 | 3800 | 3800 | 1140 | 3800 | 3800 | 3800 |
Test | Test | 1170 | 3900 | 3900 | 1170 | 3900 | 3900 | 1170 | 3900 | 3900 | 1170 | 3900 | 3900 | 1170 | 3900 | 3900 | 1170 | 3900 | 3900 | 3900 |
"use client";
import { useCallback, useMemo } from "react";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type { ColumnDef } from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DataTypeHelper = {
firstName: string;
lastName: string;
age: number;
visits: number;
progress: number;
status: number;
activity: number;
age1: number;
visits1: number;
progress1: number;
status1: number;
activity1: number;
age2: number;
visits2: number;
progress2: number;
status2: number;
activity2: number;
age3: number;
visits3: number;
progress3: number;
status3: number;
activity3: number;
age4: number;
visits4: number;
progress4: number;
status4: number;
activity4: number;
age5: number;
visits5: number;
progress5: number;
status5: number;
activity5: number;
};
const Example = () => {
const columns = useMemo<ColumnDef<{}, DataTypeHelper>[]>(
() => [
{
id: "name",
header: () => "Name",
sticky: "left",
columns: [
{
header: () => "First Name",
accessorKey: "firstName",
size: 60,
},
{
header: () => "Last Name",
accessorKey: "lastName",
size: 60,
},
],
},
{
id: "info",
header: () => "Info",
columns: [
{
header: () => "Age",
cell: (props) => props.getValue(),
accessorKey: "age",
size: 50,
},
{
header: () => "Visits",
cell: (props) => props.getValue(),
accessorKey: "visits",
},
{
header: () => "Activity",
accessorKey: "activity",
},
],
},
{
id: "info1",
header: () => "Info1",
columns: [
{
header: () => "Age1",
cell: (props) => props.getValue(),
accessorKey: "age1",
size: 50,
},
{
header: () => "Visits1",
cell: (props) => props.getValue(),
accessorKey: "visits1",
},
{
header: () => "Activity1",
accessorKey: "activity1",
},
],
},
{
id: "info2",
header: () => "Info2",
columns: [
{
header: () => "Age2",
cell: (props) => props.getValue(),
accessorKey: "age2",
size: 50,
},
{
header: () => "Visits2",
cell: (props) => props.getValue(),
accessorKey: "visits2",
},
{
header: () => "Activity2",
accessorKey: "activity2",
},
],
},
{
id: "info3",
header: () => "Info3",
columns: [
{
header: () => "Age3",
cell: (props) => props.getValue(),
accessorKey: "age3",
size: 50,
},
{
header: () => "Visits3",
cell: (props) => props.getValue(),
accessorKey: "visits3",
},
{
header: () => "Activity3",
accessorKey: "activity3",
},
],
},
{
id: "info4",
header: () => "Info4",
columns: [
{
header: () => "Age4",
cell: (props) => props.getValue(),
accessorKey: "age4",
size: 50,
},
{
header: () => "Visits4",
cell: (props) => props.getValue(),
accessorKey: "visits4",
},
{
header: () => "Activity4",
accessorKey: "activity4",
},
],
},
{
id: "info5",
header: () => "Info5",
columns: [
{
header: () => "Age5",
cell: (props) => props.getValue(),
accessorKey: "age5",
size: 50,
},
{
header: () => "Visits5",
cell: (props) => props.getValue(),
accessorKey: "visits5",
},
{
header: () => "Activity5",
accessorKey: "activity5",
},
],
},
{
id: "progress",
header: () => "Progress",
sticky: "right",
columns: [
{
header: () => "Profile Progress",
cell: (props) => props.getValue(),
accessorKey: "progress",
},
],
},
],
[],
);
const makeData = useCallback((length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
activity: Math.floor(index * 100),
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
age1: <span>{Math.floor(index * 30)}</span>,
visits1: <span>{Math.floor(index * 100)}</span>,
activity1: Math.floor(index * 100),
age2: <span>{Math.floor(index * 30)}</span>,
visits2: <span>{Math.floor(index * 100)}</span>,
activity2: Math.floor(index * 100),
age3: <span>{Math.floor(index * 30)}</span>,
visits3: <span>{Math.floor(index * 100)}</span>,
activity3: Math.floor(index * 100),
age4: <span>{Math.floor(index * 30)}</span>,
visits4: <span>{Math.floor(index * 100)}</span>,
activity4: Math.floor(index * 100),
age5: <span>{Math.floor(index * 30)}</span>,
visits5: <span>{Math.floor(index * 100)}</span>,
activity5: Math.floor(index * 100),
};
});
}, []);
const data = useMemo(() => makeData(40), [makeData]);
return (
<div className="border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
width={800}
height={400}
withMinimap
/>
</div>
);
};
export default Example;
A table with sorting data
An example of a table with data that can be sorted.
First Name | Last Name | Age | Visits | Progress | Status | Activity |
---|---|---|---|---|---|---|
Lisa | Brown | 96 | 8 | 2 | 97 | 23 |
John | Dobbins | 52 | 25 | 25 | 97 | 35 |
Tanya | Fox | 63 | 82 | 59 | 52 | 46 |
Clara | Harrison | 53 | 63 | 24 | 48 | 3 |
Burt | Henson | 4 | 653 | 36 | 44 | 43 |
Emma | Herbert | 49 | 45 | 454 | 35 | 4 |
Megan | Lewis | 55 | 54 | 24 | 56 | 33 |
Jacklyn | Perkins | 74 | 5 | 1 | 86 | 2 |
Elroy | Rogers | 12 | 4 | 44 | 98 | 15 |
Magdalena | Smith | 36 | 50 | 20 | 19 | 54 |
John | Watts | 89 | 98 | 54 | 24 | 43 |
Bart | Woods | 64 | 35 | 78 | 65 | 5 |
"use client";
import { mergeClassnames } from "@heathmont/moon-core-tw";
import { ArrowsSorting } from "@heathmont/moon-icons-tw";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type {
ColumnDef,
Header,
SortingState,
} from "@heathmont/moon-table-v8-tw/lib/es/private/types";
import { useMemo, useState } from "react";
type DataTypeHelper = {
firstName: string;
lastName: string;
age: number;
visits: number;
progress: number;
status: number;
activity: number;
};
const headerCell = (header: Header<{}, DataTypeHelper>, cellLabel: string) => {
return (
<div
className={mergeClassnames(
"flex gap-x-1 items-center",
header.column.getCanSort() ? "cursor-pointer select-none" : "",
)}
onClick={header.column.getToggleSortingHandler()}
>
{cellLabel}
{{
asc: <ArrowsSorting className="min-w-[20px] min-h-[20px]" />,
desc: <ArrowsSorting className="min-w-[20px] min-h-[20px]" />,
}[header.column.getIsSorted() as string] ?? null}
</div>
);
};
const preset: SortingState = [
{
id: "last_name",
desc: false,
},
];
const Example = () => {
const makeData = useMemo(
() => [
{
firstName: "Magdalena",
lastName: "Smith",
age: 36,
visits: 50,
progress: 20,
status: 19,
activity: 54,
},
{
firstName: "Lisa",
lastName: "Brown",
age: 96,
visits: 8,
progress: 2,
status: 97,
activity: 23,
},
{
firstName: "Tanya",
lastName: "Fox",
age: 63,
visits: 82,
progress: 59,
status: 52,
activity: 46,
},
{
firstName: "Bart",
lastName: "Woods",
age: 64,
visits: 35,
progress: 78,
status: 65,
activity: 5,
},
{
firstName: "Elroy",
lastName: "Rogers",
age: 12,
visits: 4,
progress: 44,
status: 98,
activity: 15,
},
{
firstName: "Jacklyn",
lastName: "Perkins",
age: 74,
visits: 5,
progress: 1,
status: 86,
activity: 2,
},
{
firstName: "John",
lastName: "Watts",
age: 89,
visits: 98,
progress: 54,
status: 24,
activity: 43,
},
{
firstName: "John",
lastName: "Dobbins",
age: 52,
visits: 25,
progress: 25,
status: 97,
activity: 35,
},
{
firstName: "Megan",
lastName: "Lewis",
age: 55,
visits: 54,
progress: 24,
status: 56,
activity: 33,
},
{
firstName: "Clara",
lastName: "Harrison",
age: 53,
visits: 63,
progress: 24,
status: 48,
activity: 3,
},
{
firstName: "Burt",
lastName: "Henson",
age: 4,
visits: 653,
progress: 36,
status: 44,
activity: 43,
},
{
firstName: "Emma",
lastName: "Herbert",
age: 49,
visits: 45,
progress: 454,
status: 35,
activity: 4,
},
],
[],
);
const [data, setData] = useState(makeData);
const [sorting, setSorting] = useState<SortingState>(preset);
const columns = useMemo<ColumnDef<{}, DataTypeHelper>[]>(
() => [
{
id: "first_name",
header: ({ header }) => headerCell(header, "First Name"),
accessorKey: "firstName",
},
{
id: "last_name",
header: ({ header }) => headerCell(header, "Last Name"),
accessorKey: "lastName",
},
{
id: "age",
header: ({ header }) => headerCell(header, "Age"),
accessorKey: "age",
},
{
id: "visits",
header: ({ header }) => headerCell(header, "Visits"),
accessorKey: "visits",
},
{
id: "progress",
header: ({ header }) => headerCell(header, "Progress"),
accessorKey: "progress",
},
{
id: "status",
header: ({ header }) => headerCell(header, "Status"),
accessorKey: "status",
},
{
id: "activity",
header: ({ header }) => headerCell(header, "Activity"),
accessorKey: "activity",
},
],
[],
);
return (
<div className="border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
width={800}
state={{ sorting }}
onSortingChange={setSorting}
/>
</div>
);
};
export default Example;
Resizable tables
Examples with different table layouts: auto (default), stretched-auto and fixed (with fixedWidth = "w-max").
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
"use client";
import { useCallback, useMemo } from "react";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type { ColumnDef } from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DefaultHelper = {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
activity: number;
};
const Example = () => {
const makeData = useCallback((length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
activity: Math.floor(index * 100),
};
});
}, []);
const columns = useMemo<ColumnDef<{}, DefaultHelper>[]>(
() => [
{
id: "firstName",
header: () => "First Name",
accessorKey: "firstName",
},
{
id: "lastName",
header: () => "Last Name",
accessorKey: "lastName",
},
{
id: "age",
header: () => "Age",
accessorKey: "age",
cell: (props) => props.getValue(),
},
{
id: "visits",
header: () => "Visits",
accessorKey: "visits",
cell: (props) => props.getValue(),
},
{
id: "progress",
header: () => "Progress",
accessorKey: "progress",
cell: (props) => props.getValue(),
},
{
id: "activity",
header: () => "Activity",
accessorKey: "activity",
},
{
id: "status",
header: () => "Status",
accessorKey: "status",
},
],
[],
);
const data = useMemo(() => makeData(2), [makeData]);
const defaultColumn = {
size: 120,
};
return (
<div className="flex flex-col gap-y-2 overflow-hidden">
<div className="w-full border border-beerus rounded-lg overflow-hidden bg-gohan">
<Table
columns={columns}
data={data}
defaultColumn={defaultColumn}
width={800}
isResizable
/>
</div>
<div className="w-full border border-beerus rounded-lg overflow-hidden bg-gohan">
<Table
columns={columns}
data={data}
defaultColumn={defaultColumn}
width={800}
layout="stretched-auto"
isResizable
/>
</div>
<div className="w-full border border-beerus rounded-lg overflow-hidden bg-gohan">
<Table
columns={columns}
data={data}
defaultColumn={defaultColumn}
width={800}
layout="fixed"
fixedWidth="w-max"
isResizable
/>
</div>
</div>
);
};
export default Example;
Long data resizable table
An example of a wide table with clipped data.
Use the mouse wheel or the arrow keys on the keyboard to scroll the data up and down.
Also use the right and left keyboard keys to scroll the columns of the table horizontally.
Transactions | Info | Status | |||||
---|---|---|---|---|---|---|---|
Transaction UUID | User & Supplier user | Process time | Client | Game name & provider | Amount | Currency | Status |
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546 | 2023-09-19T14:31:46.105Z | Bender (old) Coin Gaming | Pragmatic Play | 22.97 | |||
Transaction UUID | User & Supplier user | Process time | Client | Game name & provider | Amount | Currency | Status |
Transactions | Info | Status |
"use client";
import { useCallback, useMemo } from "react";
import { Tag } from "@heathmont/moon-core-tw";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type ClipProps from "@heathmont/moon-table-v8-tw/lib/es/private/types/ClipProps";
import type { ColumnDef } from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DataTypeHelper = {
uuid: string;
user: string;
processTime: string;
client: string;
gameNameAndProvider: string;
amount: number;
currency: JSX.Element;
status: JSX.Element;
};
const Example = () => {
const columns = useMemo<ColumnDef<{}, DataTypeHelper>[]>(
() => [
{
id: "operation",
header: () => "Transactions",
footer: () => "Transactions",
sticky: "left",
size: 180,
minSize: 180,
columns: [
{
header: () => "Transaction UUID",
footer: () => "Transaction UUID",
accessorKey: "uuid",
size: 110,
},
{
header: () => "User & Supplier user",
footer: () => "User & Supplier user",
accessorKey: "user",
size: 70,
},
],
},
{
id: "info",
header: () => "Info",
footer: () => "Info",
columns: [
{
header: () => "Process time",
accessorKey: "processTime",
footer: () => "Process time",
},
{
header: () => "Client",
accessorKey: "client",
footer: () => "Client",
},
{
header: () => "Game name & provider",
accessorKey: "gameNameAndProvider",
footer: () => "Game name & provider",
},
{
header: () => "Amount",
accessorKey: "amount",
footer: () => "Amount",
},
{
header: () => "Currency",
footer: () => "Currency",
accessorKey: "currency",
cell: (props) => props.getValue(),
},
],
},
{
id: "status",
header: () => "Status",
footer: () => "Status",
sticky: "right",
size: 90,
columns: [
{
header: () => "Status",
footer: () => "Status",
accessorKey: "status",
cell: (props) => props.getValue(),
size: 90,
minSize: 90,
},
],
},
],
[],
);
const currency = useMemo(
() => (
<Tag className="bg-gray-100 text-lg text-gray-600 max-w-fit">USD</Tag>
),
[],
);
const success = useMemo(
() => (
<Tag className="bg-roshi-10 text-lg text-roshi max-w-fit">SUCCESS</Tag>
),
[],
);
const defaultColumn = {
minSize: 50,
size: 50,
maxSize: Number.MAX_SAFE_INTEGER,
};
const makeData = useCallback(
(length: number) => {
return Array.from("_".repeat(length)).map((_) => {
return {
uuid: "84837d8ac654aa4689efa4649-84837d8ac654aa4689efa4649756454a5646545546d54f6546f546",
user: "[email protected]",
processTime: "2023-09-19T14:31:46.105Z",
client: "Bender (old) Coin Gaming",
gameNameAndProvider: "Pragmatic Play",
amount: 22.97,
currency: currency,
status: success,
};
});
},
[currency, success],
);
const data = useMemo(() => makeData(40), [makeData]);
const textClip = "clip" as ClipProps;
return (
<div className="border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
defaultColumn={defaultColumn}
width={800}
height={500}
isResizable
textClip={textClip}
withCellBorder="sticky"
withFooter
/>
</div>
);
};
export default Example;
Extra long data resizable table
An example of a table with clipped extra-long data, which can be both text and html structure.
Use the mouse wheel or the arrow keys on the keyboard to scroll the data up and down.
Also use the right and left keyboard keys to scroll through the clipped data in the "Deals" cells.
Location | Deals | Amount | Currency | Date range | Actions |
---|---|---|---|---|---|
Lithuania | 22.97 | ||||
AMD | 22.97 | ||||
Europe | 10.0(10000-20000)|9.0(20000-30000)|8.0(30000-40000)|7.0(40000-50000)|6.0(50000-60000) | 22.97 | |||
Europe | 5.0(2-3) | 22.97 | |||
Europe | 0.0(0-) | 22.97 | |||
Asia | 6.0(3-4) | 22.97 | |||
Asia | 5.0(0-150000)|4.0(150000-500000) | 22.97 |
"use client";
import { Fragment, useCallback, useMemo } from "react";
import { Chip, Tag, Tooltip } from "@heathmont/moon-core-tw";
import {
Other3DotsHorizontal,
TimeCalendarDate,
} from "@heathmont/moon-icons-tw";
import { CellScroller, Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type { ColumnDef } from "@heathmont/moon-table-v8-tw/lib/es/private/types";
type DataTypeHelper = {
location: string;
deals: JSX.Element | string;
range: JSX.Element | string;
amount: number;
currency: JSX.Element;
actions: JSX.Element;
};
const Example = () => {
const columns = useMemo<ColumnDef<{}, DataTypeHelper>[]>(
() => [
{
id: "leftSticky",
header: () => "",
sticky: "left",
columns: [
{
id: "location",
header: () => "Location",
accessorKey: "location",
size: 100,
},
],
},
{
id: "data",
header: () => "",
columns: [
{
id: "deals",
header: () => "Deals",
accessorKey: "deals",
cell: (props) => props.getValue(),
size: 150,
},
{
id: "amount",
header: () => "Amount",
accessorKey: "amount",
},
{
id: "currency",
header: () => "Currency",
accessorKey: "currency",
cell: (props) => props.getValue(),
},
{
id: "range",
header: () => "Date range",
accessorKey: "range",
cell: (props) => props.getValue(),
size: 190,
minSize: 190,
},
],
},
{
id: "rightSticky",
header: () => "",
sticky: "right",
size: 70,
columns: [
{
id: "actions",
header: () => "Actions",
accessorKey: "actions",
cell: (props) => props.getValue(),
size: 70,
},
],
},
],
[],
);
const currency = useMemo(
() => (
<Tag className="bg-gray-100 text-lg text-gray-600 max-w-fit">USD</Tag>
),
[],
);
const tooltip = useMemo(
() => (
<Tooltip>
<Tooltip.Trigger className="max-h-6">
<Chip
variant="default"
iconOnly={<Other3DotsHorizontal className="text-moon-24 max-h-6" />}
/>
</Tooltip.Trigger>
<Tooltip.Content position="top-start" className="z-[2]">
Any activity
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip>
),
[],
);
const rearrangeData = useCallback(
(data: { [key: string]: { start?: string; end?: string } }[]) => {
const deals = data.map((value, index, src) => {
const [key, range] = Object.entries(value)[0];
return (
<Fragment key={index}>
<span className="me-[5px]">{key}</span>
<span>({range.start ? range.start : ""}</span>
<span>-</span>
<span>{range.end ? range.end : ""})</span>
{index < src.length - 1 && <span className="mx-2">|</span>}
</Fragment>
);
});
return deals;
},
[],
);
const data = useMemo(
() => [
{
location: "Lithuania",
deals: "",
range: (
<Chip
size="sm"
className="bg-transparent"
iconLeft={<TimeCalendarDate className="text-moon-24" />}
>
23.10.01 - 23.10.31
</Chip>
),
amount: 22.97,
currency: currency,
actions: tooltip,
},
{
location: "AMD",
deals: "",
range: (
<Chip
size="sm"
className="bg-transparent"
iconLeft={<TimeCalendarDate className="text-moon-24" />}
>
23.10.01 - 23.10.31
</Chip>
),
amount: 22.97,
currency: currency,
actions: tooltip,
},
{
location: "Europe",
deals: (
<CellScroller
className="max-w-[260px]"
data={rearrangeData([
{ "10.0": { start: "10000", end: "20000" } },
{ "9.0": { start: "20000", end: "30000" } },
{ "8.0": { start: "30000", end: "40000" } },
{ "7.0": { start: "40000", end: "50000" } },
{ "6.0": { start: "50000", end: "60000" } },
])}
></CellScroller>
),
range: (
<Chip
size="sm"
className="bg-transparent"
iconLeft={<TimeCalendarDate className="text-moon-24" />}
>
23.10.01 - 23.10.31
</Chip>
),
amount: 22.97,
currency: currency,
actions: tooltip,
},
{
location: "Europe",
deals: (
<CellScroller
data={rearrangeData([{ "5.0": { start: "2", end: "3" } }])}
></CellScroller>
),
range: (
<Chip
size="sm"
className="bg-transparent"
iconLeft={<TimeCalendarDate className="text-moon-24" />}
>
23.12.01 -
</Chip>
),
amount: 22.97,
currency: currency,
actions: tooltip,
},
{
location: "Europe",
deals: (
<CellScroller
data={rearrangeData([{ "0.0": { start: "0" } }])}
></CellScroller>
),
range: (
<Chip
size="sm"
className="bg-transparent"
iconLeft={<TimeCalendarDate className="text-moon-24" />}
>
23.11.01 - 23.11.30
</Chip>
),
amount: 22.97,
currency: currency,
actions: tooltip,
},
{
location: "Asia",
deals: (
<CellScroller
data={rearrangeData([{ "6.0": { start: "3", end: "4" } }])}
></CellScroller>
),
range: (
<Chip
size="sm"
className="bg-transparent"
iconLeft={<TimeCalendarDate className="text-moon-24" />}
>
23.11.01 -
</Chip>
),
amount: 22.97,
currency: currency,
actions: tooltip,
},
{
location: "Asia",
deals: (
<CellScroller
className="max-w-[260px]"
data={rearrangeData([
{ "5.0": { start: "0", end: "150000" } },
{ "4.0": { start: "150000", end: "500000" } },
])}
></CellScroller>
),
range: (
<Chip
size="sm"
className="bg-transparent"
iconLeft={<TimeCalendarDate className="text-moon-24" />}
>
23.05.01 - 23.10.31
</Chip>
),
amount: 22.97,
currency: currency,
actions: tooltip,
},
],
[rearrangeData, currency, tooltip],
);
const defaultColumn = {
minSize: 70,
size: 50,
maxSize: Number.MAX_SAFE_INTEGER,
};
return (
<div className="border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
defaultColumn={defaultColumn}
width={800}
height={400}
isResizable
withCellBorder="sticky"
/>
</div>
);
};
export default Example;
Column Visibility
A table can configure the visibility of its columns.
At table's state
prop set up the columnVisibility
property which should be a custom state itself. This will automatically be handled over to the internal table's configuration.
You can control the columns visibility by updating your custom columns visibility state using your setCustomState
function and assigning it to onColumnVisibilityChange
prop. You can follow an example below.
The state.columnVisibility
prop has a shape of:
{ columnId1: true/false, columnId2: true/false, columnId3: true/false, ... }
Note: Every column is visible by default. Either not present (
undefined
) in state.columnVisibility
prop or a true
value makes the column visible. Only a value of false
hides the column. First Name | Last Name | Age | Visits | Progress | Activity | Status |
---|---|---|---|---|---|---|
Test | Test | 0 | 0 | 0 | 0 | 0 |
Test | Test | 30 | 100 | 100 | 100 | 100 |
Test | Test | 60 | 200 | 200 | 200 | 200 |
Test | Test | 90 | 300 | 300 | 300 | 300 |
Test | Test | 120 | 400 | 400 | 400 | 400 |
"use client";
import { useCallback, useMemo, useState } from "react";
import { Table } from "@heathmont/moon-table-v8-tw/lib/es";
import type { ColumnDef } from "@heathmont/moon-table-v8-tw/lib/es/private/types";
import { Checkbox } from "@heathmont/moon-core-tw";
type DefaultHelper = {
firstName: string;
lastName: string;
age: string;
visits: string;
progress: string;
status: number;
activity: number;
};
const Example = () => {
const makeData = useCallback((length: number) => {
return Array.from("_".repeat(length)).map((_, index) => {
return {
firstName: "Test",
lastName: "Test",
age: <span>{Math.floor(index * 30)}</span>,
visits: <span>{Math.floor(index * 100)}</span>,
progress: <span>{Math.floor(index * 100)}</span>,
status: Math.floor(index * 100),
activity: Math.floor(index * 100),
};
});
}, []);
const columns = useMemo<ColumnDef<{}, DefaultHelper>[]>(
() => [
{
id: "firstName",
header: () => "First Name",
accessorKey: "firstName",
},
{
id: "lastName",
header: () => "Last Name",
accessorKey: "lastName",
},
{
id: "age",
header: () => "Age",
accessorKey: "age",
cell: (props) => props.getValue(),
},
{
id: "visits",
header: () => "Visits",
accessorKey: "visits",
cell: (props) => props.getValue(),
},
{
id: "progress",
header: () => "Progress",
accessorKey: "progress",
cell: (props) => props.getValue(),
},
{
id: "activity",
header: () => "Activity",
accessorKey: "activity",
},
{
id: "status",
header: () => "Status",
accessorKey: "status",
},
],
[],
);
const data = useMemo(() => makeData(5), [makeData]);
const [columnVisibility, setColumnsVisibility] = useState<
Record<string, boolean>
>({});
const hideColumn = (columnId: string) => {
setColumnsVisibility((prevState) => ({
...prevState,
[columnId]:
prevState[columnId] === undefined ? false : !prevState[columnId],
}));
};
const isColumnVisible = (columnId: string) => {
if (columnVisibility[columnId] === undefined) {
return true;
}
return columnVisibility[columnId];
};
const ColumnsVisibilityControls = () => {
return (
<ul className="flex flex-wrap gap-2">
{columns.map((column) => (
<li key={column.id}>
<Checkbox
label={column?.id}
id={`checkbox-${column.id}`}
checked={isColumnVisible(column.id as string)}
onChange={() => {
hideColumn(column.id as string);
}}
/>
</li>
))}
</ul>
);
};
return (
<div className="w-full max-w-screen-lg flex flex-col gap-3">
<ColumnsVisibilityControls />
<div className="w-full border border-beerus rounded-lg overflow-hidden">
<Table
columns={columns}
data={data}
state={{ columnVisibility }}
onColumnVisibilityChange={setColumnsVisibility}
/>
</div>
</div>
);
};
export default Example;
Table
These are props specific to the Table component:
Name | Type | Default |
---|---|---|
bodyBackgroundColor | string | gohan |
columns* | "ColumnDef<TData, any>[]" | - |
data* | "TData[]" | - |
defaultColumn | "Partial<ColumnDef<TData, any>>" | - |
defaultRowBackgroundColor | string | goku |
evenRowBackgroundColor | string | goku |
fixedWidth | string | number | - |
getSubRows | (originalRow: TData, index: number) => TData[] | undefined | - |
headerBackgroundColor | string | gohan |
height | string | number | - |
isResizable | boolean | false |
isSelectable | boolean | false |
isSticky | boolean | true |
layout | "auto" | "stretched-auto" | "fixed" | auto |
onExpandedChange | "OnChangeFn<ExpandedState>" | - |
onRowSelectionChange | "OnChangeFn<RowSelectionState>" | - |
preventSelectionByRowClick | boolean | false |
rowGap | string | 2px |
rowActiveColor | string | - |
rowHoverColor | string | - |
rowSelectColor | string | heles |
rowSize | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | md |
state | "Partial<TableState>" | - |
textClip | "clip" | "break" | - |
width | string | number | - |
withCellBorder | boolean | "sticky" | false |
withFooter | boolean | false |
withMinimap | boolean | false |
onColumnVisibilityChange | "OnChangeFn<VisibilityState>" | - |
Properties indicated with * are required.