- Header content
+
+
Total rows: {data.length}
+
Selected: {selectedRows.length}
);
};
}
-export const MultiSlotPlugin = definePlugin({
- id: "multi-slot",
- name: "Multi Slot",
- args: MultiSlotSchema,
+export const MySidePanelPlugin = definePlugin({
+ id: "my-side-panel",
+ name: "My Panel",
+ args: SidePanelSchema,
slots: {
sidePanel: {
- position: "right-sider",
- header: "Multi Slot Plugin",
+ position: "right-sider", // or "left-sider"
+ header: "Panel Title", // Can also be a function: (context) => ReactNode
render: createSidePanelRenderer,
},
+ },
+});
+```
+
+### Header & Footer Slots
+
+Render above or below the table body. All plugins with these slots render sequentially.
+
+```tsx
+export const HeaderFooterPlugin = definePlugin({
+ id: "header-footer",
+ name: "Header Footer",
+ args: z.object({}),
+ slots: {
header: {
- render: createHeaderRenderer,
+ render: (context) => function HeaderContent() {
+ const { data } = usePluginContext();
+ return
Showing {data.length} records
;
+ },
+ },
+ footer: {
+ render: (context) => function FooterContent() {
+ return
Footer content here
;
+ },
},
},
});
```
-## Context Menu Only Plugin
+### Cell & InlineRow Slots
-Omit `slots` to create a plugin that only adds context menu items.
+Custom renderers for cells and expandable rows. Only the first matching plugin renders.
```tsx
-import { z } from "zod";
-import { definePlugin, contextMenuItem } from "@izumisy/seizen-datatable-react/plugin";
+export const CellPlugin = definePlugin({
+ id: "custom-cell",
+ name: "Custom Cell",
+ args: z.object({}),
+ slots: {
+ cell: {
+ // Receives TanStack Table's Cell, Column, and Row
+ render: (context) => (cell, column, row) => {
+ const value = cell.getValue();
+ return
{String(value)};
+ },
+ },
+ inlineRow: {
+ // Renders below a row when expanded
+ render: (context) => (row) => {
+ return
Details for row {row.id}
;
+ },
+ },
+ },
+});
+```
+
+## Context Menu Items
+
+Plugins can add items to cell and column header context menus using `cellContextMenuItem` and `columnContextMenuItem`.
+
+### Cell Context Menu
-const RowActionsSchema = z.object({
- enableCopyId: z.boolean().default(true),
- enableDelete: z.boolean().default(false),
+```tsx
+import {
+ definePlugin,
+ cellContextMenuItem,
+} from "@izumisy/seizen-datatable-react/plugin";
+
+export const CellActionsPlugin = definePlugin({
+ id: "cell-actions",
+ name: "Cell Actions",
+ args: z.object({
+ enableCopy: z.boolean().default(true),
+ }),
+ slots: {},
+ contextMenuItems: {
+ cell: [
+ cellContextMenuItem("copy-value", (ctx) => ({
+ label: "Copy value",
+ onClick: () => navigator.clipboard.writeText(String(ctx.value)),
+ visible: ctx.pluginArgs.enableCopy && ctx.value != null,
+ })),
+ cellContextMenuItem("filter-by-value", (ctx) => ({
+ label: `Filter by "${ctx.value}"`,
+ onClick: () => ctx.column.setFilterValue(ctx.value),
+ })),
+ ],
+ },
});
+```
+
+The `ctx` object provides access to the clicked cell, column, row, cell value, selected rows, table instance, plugin configuration, and an `emit` function for EventBus.
+
+### Column Context Menu
-export const RowActionsPlugin = definePlugin({
- id: "row-actions",
- name: "Row Actions",
- args: RowActionsSchema,
- contextMenu: {
- items: [
- contextMenuItem("copy-id", (ctx) => ({
- label: "Copy ID",
- onClick: () => navigator.clipboard.writeText(String(ctx.row.id)),
- visible: ctx.pluginArgs.enableCopyId,
+```tsx
+import {
+ definePlugin,
+ columnContextMenuItem,
+} from "@izumisy/seizen-datatable-react/plugin";
+
+export const ColumnActionsPlugin = definePlugin({
+ id: "column-actions",
+ name: "Column Actions",
+ args: z.object({}),
+ slots: {},
+ contextMenuItems: {
+ column: [
+ columnContextMenuItem("hide-column", (ctx) => ({
+ label: "Hide column",
+ onClick: () => ctx.column.toggleVisibility(false),
})),
- contextMenuItem("delete", (ctx) => ({
- label: `Delete ${ctx.selectedRows.length > 1 ? `${ctx.selectedRows.length} items` : "item"}`,
- onClick: () => handleDelete(ctx.selectedRows.length > 0 ? ctx.selectedRows : [ctx.row]),
- visible: ctx.pluginArgs.enableDelete,
- disabled: ctx.selectedRows.length === 0,
+ columnContextMenuItem("sort-asc", (ctx) => ({
+ label: "Sort ascending",
+ onClick: () => ctx.column.toggleSorting(false),
+ visible: ctx.column.getCanSort(),
+ })),
+ columnContextMenuItem("sort-desc", (ctx) => ({
+ label: "Sort descending",
+ onClick: () => ctx.column.toggleSorting(true),
+ visible: ctx.column.getCanSort(),
})),
],
},
});
```
+The `ctx` object provides access to the clicked column, table instance, plugin configuration, and an `emit` function for EventBus.
+
## Plugin Context (`usePluginContext`)
-Inside your plugin component, use `usePluginContext` to access table data and APIs.
+Inside plugin components, use `usePluginContext()` to access table data and APIs.
```tsx
const {
- table, // DataTable instance
- data, // Current table data (unknown[])
- columns, // Column info ({ key, header }[])
- selectedRows, // Currently selected rows (unknown[])
- openArgs, // Arguments passed via table.plugin.open()
- useEvent, // Hook to subscribe to events
+ table, // DataTableInstance - table methods and state
+ data, // unknown[] - current table data
+ columns, // PluginColumnInfo[] - column info with filter metadata
+ selectedRows, // unknown[] - currently selected rows
+ openArgs, // TOpenArgs | undefined - args passed via table.plugin.open()
+ useEvent, // Hook to subscribe to EventBus events
} = usePluginContext();
```
### `openArgs` - Receiving Initial Data
-When a plugin is opened with `table.plugin.open(pluginId, args)`, the `args` are available via `openArgs`:
+When a plugin is opened via `table.plugin.open(pluginId, args)`, the args are available through `openArgs`:
```tsx
// Application side
@@ -211,6 +319,211 @@ const { openArgs } = usePluginContext<"row-detail">();
const initialRow = openArgs?.row;
```
+### Event Subscription with `useEvent`
+
+Subscribe to built-in and custom events:
+
+```tsx
+const { useEvent } = usePluginContext();
+
+// Built-in events
+useEvent("row-click", (row) => {
+ console.log("Row clicked:", row);
+});
+
+useEvent("selection-change", (selectedRows) => {
+ console.log("Selection changed:", selectedRows);
+});
+
+useEvent("filter-change", (filterState) => {
+ console.log("Filters changed:", filterState);
+});
+```
+
+Built-in events include `data-change`, `selection-change`, `filter-change`, `sorting-change`, `pagination-change`, `row-click`, `cell-context-menu`, and `column-context-menu`.
+
+## Type-Safe Plugin Args (Module Augmentation)
+
+For type-safe `openArgs`, extend the `PluginArgsRegistry` interface:
+
+```tsx
+// In your plugin file
+declare module "@izumisy/seizen-datatable-react/plugin" {
+ interface PluginArgsRegistry {
+ "my-plugin": { row: MyRowType; mode: "view" | "edit" };
+ }
+}
+
+// Now openArgs is typed correctly
+const { openArgs } = usePluginContext<"my-plugin">();
+// openArgs is typed as { row: MyRowType; mode: "view" | "edit" } | undefined
+```
+
+## Custom Events (Module Augmentation)
+
+Plugins can define custom events using module augmentation on `EventBusRegistry`. This enables type-safe inter-plugin communication.
+
+### Defining Custom Events
+
+```tsx
+// In your plugin file
+declare module "@izumisy/seizen-datatable-react/plugin" {
+ interface EventBusRegistry {
+ /** Request to add a filter from context menu */
+ "filter:add-request": {
+ columnKey: string;
+ value: unknown;
+ };
+ /** Notify that export is complete */
+ "export:complete": {
+ format: "csv" | "json";
+ rowCount: number;
+ };
+ }
+}
+```
+
+### Emitting Custom Events
+
+Use the `emit` function from context menu handlers:
+
+```tsx
+cellContextMenuItem("add-filter", (ctx) => ({
+ label: `Filter by "${ctx.value}"`,
+ onClick: () => {
+ ctx.emit("filter:add-request", {
+ columnKey: ctx.column.id,
+ value: ctx.value,
+ });
+ },
+}))
+```
+
+### Subscribing to Custom Events
+
+```tsx
+function MyPluginContent() {
+ const { useEvent } = usePluginContext();
+
+ useEvent("filter:add-request", ({ columnKey, value }) => {
+ console.log(`Add filter: ${columnKey} = ${value}`);
+ // Handle the filter request
+ });
+
+ useEvent("export:complete", ({ format, rowCount }) => {
+ console.log(`Exported ${rowCount} rows as ${format}`);
+ });
+
+ return
...
;
+}
+```
+
+
+
+## Complete Example
+
+Here's a complete plugin that combines slots, context menu items, and custom events:
+
+```tsx
+import { useState } from "react";
+import { z } from "zod";
+import {
+ definePlugin,
+ usePluginContext,
+ cellContextMenuItem,
+ type PluginContext,
+} from "@izumisy/seizen-datatable-react/plugin";
+
+// Type-safe plugin args
+declare module "@izumisy/seizen-datatable-react/plugin" {
+ interface PluginArgsRegistry {
+ "bulk-actions": { initialSelection?: unknown[] };
+ }
+ interface EventBusRegistry {
+ "bulk-actions:delete": { rows: unknown[] };
+ }
+}
+
+const BulkActionsSchema = z.object({
+ enableDelete: z.boolean().default(true),
+ enableExport: z.boolean().default(true),
+});
+
+type BulkActionsConfig = z.infer
;
+
+function createSidePanelRenderer(context: PluginContext) {
+ const { args } = context;
+
+ return function BulkActionsPanel() {
+ const { selectedRows, openArgs, useEvent } = usePluginContext<"bulk-actions">();
+ const [lastDeleted, setLastDeleted] = useState(0);
+
+ // Subscribe to delete events
+ useEvent("bulk-actions:delete", ({ rows }) => {
+ setLastDeleted(rows.length);
+ });
+
+ if (selectedRows.length === 0) {
+ return (
+
+ Select rows to see bulk actions
+
+ );
+ }
+
+ return (
+
+
{selectedRows.length} rows selected
+ {args.enableDelete && (
+
+ )}
+ {args.enableExport && (
+
+ )}
+ {lastDeleted > 0 &&
Last deleted: {lastDeleted} rows
}
+
+ );
+ };
+}
+
+export const BulkActionsPlugin = definePlugin({
+ id: "bulk-actions",
+ name: "Bulk Actions",
+ args: BulkActionsSchema,
+ slots: {
+ sidePanel: {
+ position: "right-sider",
+ header: "Bulk Actions",
+ render: createSidePanelRenderer,
+ },
+ },
+ contextMenuItems: {
+ cell: [
+ cellContextMenuItem("add-to-selection", (ctx) => ({
+ label: "Add to bulk selection",
+ onClick: () => {
+ ctx.row.toggleSelected(true);
+ },
+ visible: !ctx.row.getIsSelected(),
+ })),
+ cellContextMenuItem("delete-row", (ctx) => ({
+ label: "Delete row",
+ onClick: () => {
+ ctx.emit("bulk-actions:delete", { rows: [ctx.row.original] });
+ },
+ visible: ctx.pluginArgs.enableDelete,
+ })),
+ ],
+ },
+});
+```
+