Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cbbd2c8
feat(floating-actions): add FloatingActions component
paanSinghCoder Apr 20, 2026
63df3c6
revert: drop FloatingActions section from examples/page.tsx
paanSinghCoder Apr 20, 2026
bbc0680
refactor(floating-actions): delegate Separator to existing primitive
paanSinghCoder Apr 20, 2026
9254ab8
refactor(floating-actions): lock Separator to vertical orientation
paanSinghCoder Apr 20, 2026
1defea7
refactor(floating-actions): update separator class to use data attrib…
paanSinghCoder Apr 20, 2026
b614f17
refactor(floating-actions): update separator role and accessibility a…
paanSinghCoder Apr 20, 2026
fe75605
docs(floating-actions): add row-selection pattern with DataTable
paanSinghCoder Apr 21, 2026
c9d9d96
feat(floating-actions): add inline and floating variants
paanSinghCoder Apr 27, 2026
cd9bba9
refactor(floating-actions): address review feedback
paanSinghCoder Apr 27, 2026
0e3eda4
docs(datatable): add row selection demo with FloatingActions overlay
paanSinghCoder Apr 27, 2026
7a8d02f
docs(datatable): simplify SelectionBar implementation and update posi…
paanSinghCoder Apr 27, 2026
b0b748f
Merge remote-tracking branch 'origin/feat/floating-actions' into docs…
paanSinghCoder Apr 27, 2026
8c8ee5e
Merge remote-tracking branch 'origin/main' into feat/floating-actions
paanSinghCoder Apr 27, 2026
b9ab6c4
docs(floating-actions): pad inline demos so the bar isn't flush with …
paanSinghCoder Apr 27, 2026
3ff91e5
Merge remote-tracking branch 'origin/feat/floating-actions' into docs…
paanSinghCoder Apr 27, 2026
861978c
docs(floating-actions): tab the variants demo and clean up scrolling-…
paanSinghCoder Apr 27, 2026
cd26ef8
docs(floating-actions): add side, align, and disabled demos to docume…
paanSinghCoder Apr 27, 2026
f915dff
docs(floating-actions): update alignment and remove disabled demo fro…
paanSinghCoder Apr 27, 2026
a5d1afa
refactor(floating-actions): drop unreliable disabled prop from public…
paanSinghCoder Apr 27, 2026
591e060
Merge remote-tracking branch 'origin/feat/floating-actions' into docs…
paanSinghCoder Apr 27, 2026
2eeee29
docs(floating-actions, datatable): update links to ensure correct nav…
paanSinghCoder Apr 27, 2026
8c57a4f
feat(floating-actions): add inline demo and update props interface
paanSinghCoder Apr 27, 2026
20bb272
Merge remote-tracking branch 'origin/feat/floating-actions' into docs…
paanSinghCoder Apr 27, 2026
c267ed9
Merge branch 'main' into docs/floating-actions-datatable
paanSinghCoder Apr 29, 2026
3147127
Merge branch 'main' into docs/floating-actions-datatable
paanSinghCoder Apr 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions apps/www/src/components/datatable-selection-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
'use client';

import { TransformIcon } from '@radix-ui/react-icons';
import {
Button,
Checkbox,
Chip,
DataTable,
type DataTableColumnDef,
FloatingActions,
useDataTable
} from '@raystack/apsara';
import { useEffect, useMemo } from 'react';

type Row = {
id: string;
name: string;
email: string;
role: string;
team: string;
};

const names = [
'Alice Ross',
'Bob Evans',
'Clara Patel',
'David Kim',
'Elena Moreno',
'Finn O’Connor',
'Grace Tan',
'Henry Novak',
'Ivy Chen',
'Jason Park'
];
const roles = ['Admin', 'Editor', 'Viewer'];
const teams = ['Engineering', 'Design', 'Product', 'Marketing', 'Sales'];

const selectionColumn: DataTableColumnDef<Row, unknown> = {
id: 'select',
accessorKey: 'select',
header: '',
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={value => row.toggleSelected(Boolean(value))}
aria-label='Select row'
onClick={e => e.stopPropagation()}
/>
),
enableColumnFilter: false,
enableSorting: false,
enableHiding: false,
styles: {
cell: { width: 40, flex: 'none' },
header: { width: 40, flex: 'none' }
}
};

const columns: DataTableColumnDef<Row, unknown>[] = [
selectionColumn,
{ accessorKey: 'name', header: 'Name', enableColumnFilter: true },
{ accessorKey: 'email', header: 'Email', enableColumnFilter: true },
{ accessorKey: 'role', header: 'Role', enableColumnFilter: true },
{ accessorKey: 'team', header: 'Team', enableColumnFilter: true }
];

function InitialSelection() {
const { table } = useDataTable();
useEffect(() => {
table.setRowSelection({ '1': true, '3': true });
}, [table]);
return null;
}

function SelectionBar() {
const { table } = useDataTable();
const selected = table.getSelectedRowModel().rows;
if (selected.length === 0) return null;

return (
<FloatingActions aria-label='Selection actions'>
<Chip
variant='outline'
size='large'
color='neutral'
leadingIcon={<TransformIcon />}
isDismissible
onDismiss={() => table.resetRowSelection()}
>
{selected.length} selected
</Chip>
<FloatingActions.Separator />
<Button variant='outline' color='neutral' size='small'>
Move to
</Button>
<Button variant='outline' color='neutral' size='small'>
Actions
</Button>
</FloatingActions>
);
}

const DataTableSelectionDemo = () => {
const data = useMemo<Row[]>(
() =>
Array.from({ length: 80 }, (_, i) => {
const name = names[i % names.length];
const handle = name.toLowerCase().replace(/[^a-z]/g, '');
return {
id: String(i + 1),
name,
email: `${handle}${i + 1}@example.com`,
role: roles[i % roles.length],
team: teams[i % teams.length]
};
}),
[]
);

return (
<div
style={{
position: 'relative',
height: 760,
width: '100%',
overflow: 'hidden',
transform: 'translateZ(0)'
}}
>
<DataTable
data={data}
columns={columns}
mode='client'
defaultSort={{ name: 'name', order: 'asc' }}
getRowId={row => row.id}
>
<DataTable.Toolbar />
<DataTable.Content classNames={{ root: 'dt-selection-demo-scroll' }} />
<InitialSelection />
<SelectionBar />
</DataTable>
<style>{`.dt-selection-demo-scroll { padding-bottom: 80px; }`}</style>
</div>
);
};

export default DataTableSelectionDemo;
2 changes: 2 additions & 0 deletions apps/www/src/components/demo/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { Home, Info, Laugh, X } from 'lucide-react';
import NextLink from 'next/link';
import { Suspense } from 'react';
import DataTableDemo from '../datatable-demo';
import DataTableSelectionDemo from '../datatable-selection-demo';
import LinearMenuDemo from '../linear-dropdown-demo';
import PopoverColorPicker from '../popover-color-picker';
import DemoPlayground from './demo-playground';
Expand All @@ -54,6 +55,7 @@ export default function Demo(props: DemoProps) {
OrganizationIcon,
SidebarIcon,
DataTableDemo,
DataTableSelectionDemo,
LinearMenuDemo,
PopoverColorPicker,
Info,
Expand Down
91 changes: 91 additions & 0 deletions apps/www/src/content/docs/components/datatable/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,94 @@ export const preview = {
type: 'code',
code: `<DataTableDemo />`
};

export const rowSelectionDemo = {
type: 'code',
previewCode: false,
code: `<DataTableSelectionDemo />`,
codePreview: [
{
label: 'index.tsx',
code: `import {
Button,
Checkbox,
Chip,
DataTable,
FloatingActions,
useDataTable,
} from "@raystack/apsara";
import { TransformIcon } from "@radix-ui/react-icons";

// 1. Leading checkbox column that wires TanStack selection.
const selectionColumn = {
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllRowsSelected()
? true
: table.getIsSomeRowsSelected()
? "indeterminate"
: false
}
onCheckedChange={(v) => table.toggleAllRowsSelected(Boolean(v))}
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(v) => row.toggleSelected(Boolean(v))}
onClick={(e) => e.stopPropagation()}
/>
),
enableSorting: false,
enableColumnFilter: false,
enableHiding: false,
};

// 2. Render the bar alongside DataTable, reading selection from context.
// FloatingActions defaults to variant="floating" (position: fixed,
// bottom-center), so no positioning CSS is needed here.
function SelectionBar() {
const { table } = useDataTable();
const selected = table.getSelectedRowModel().rows;
if (selected.length === 0) return null;

return (
<FloatingActions aria-label="Selection actions">
<Chip
variant="outline"
size="large"
color="neutral"
leadingIcon={<TransformIcon />}
isDismissible
onDismiss={() => table.resetRowSelection()}
>
{selected.length} selected
</Chip>
<FloatingActions.Separator />
<Button variant="outline" color="neutral" size="small">
Move to
</Button>
<Button variant="outline" color="neutral" size="small">
Actions
</Button>
</FloatingActions>
);
}

// 3. Compose.
<DataTable
data={rows}
columns={[selectionColumn, ...columns]}
mode="client"
defaultSort={{ name: "name", order: "asc" }}
>
<DataTable.Toolbar />
<DataTable.Search />
<DataTable.Content />
<SelectionBar />
</DataTable>`
}
]
};
13 changes: 12 additions & 1 deletion apps/www/src/content/docs/components/datatable/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: An advanced React table that supports filtering, sorting, and pagin
source: packages/raystack/components/datatable
---

import { preview } from "./demo.ts";
import { preview, rowSelectionDemo } from "./demo.ts";

<Demo data={preview} />

Expand Down Expand Up @@ -470,6 +470,17 @@ import { OrganizationIcon, FilterIcon } from "@raystack/apsara/icons";
/>
```

### Row selection

DataTable does not ship a built-in selection toolbar, but the underlying TanStack table instance is exposed via `useDataTable()`, so you can wire row selection yourself and float an [`FloatingActions`](/docs/components/floating-actions) bar over the table when rows are selected.

<Demo data={rowSelectionDemo} />

Notes:
- `table.resetRowSelection()` clears the selection; wire it to `Chip`'s `onDismiss`.
- `FloatingActions` defaults to `variant="floating"` (`position: fixed`, bottom-center), so no positioning CSS is needed at the call site. To scope the bar to the table region rather than the viewport, give an ancestor `transform`, `filter`, or `contain: paint` so it becomes the containing block for `position: fixed`. Add `padding-bottom` on `DataTable.Content`'s scroll container if rows would otherwise sit behind the bar.
- When DataTable ships first-class row selection, this pattern will migrate to a selection-aware helper; the user-level API (the chip + buttons inside `FloatingActions`) stays the same.

## Accessibility

- Uses semantic `table`, `thead`, `tbody`, `tr`, `th`, and `td` elements
Expand Down
Loading
Loading