diff --git a/.changeset/spotty-chairs-tie.md b/.changeset/spotty-chairs-tie.md
new file mode 100644
index 00000000..c9982d6c
--- /dev/null
+++ b/.changeset/spotty-chairs-tie.md
@@ -0,0 +1,6 @@
+---
+"@example/erp": patch
+"@genseki/react": patch
+---
+
+feat: add action select
diff --git a/examples/erp/genseki/collections/posts.client.tsx b/examples/erp/genseki/collections/posts.client.tsx
index 5842eebb..a5c2d476 100644
--- a/examples/erp/genseki/collections/posts.client.tsx
+++ b/examples/erp/genseki/collections/posts.client.tsx
@@ -5,24 +5,20 @@ import { useState } from 'react'
import { type SubmitErrorHandler, type SubmitHandler, useFormContext } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
-import { DotsThreeVerticalIcon } from '@phosphor-icons/react'
import { useQueryClient } from '@tanstack/react-query'
import { createColumnHelper } from '@tanstack/react-table'
import z from 'zod'
-import type { BaseData, CollectionLayoutProps, InferCreateFields } from '@genseki/react'
+import type { CollectionLayoutProps, InferCreateFields } from '@genseki/react'
import {
- BaseIcon,
+ actionsColumn,
Button,
- Checkbox,
CollectionListToolbar,
+ createDeleteActionItem,
+ createEditActionItem,
+ createSeparatorItem,
Form,
type InferFields,
- Menu,
- MenuContent,
- MenuItem,
- MenuSeparator,
- MenuTrigger,
SubmitButton,
TanstackTable,
toast,
@@ -71,6 +67,7 @@ export const columns = [
header: 'Updated At',
cell: (info) =>
{new Date(info.getValue()).toLocaleDateString('en-GB')}
,
}),
+ actionsColumn([createEditActionItem(), createSeparatorItem(), createDeleteActionItem()]),
]
/**
@@ -82,7 +79,7 @@ export function PostClientToolbar() {
return (
-
+
)
}
@@ -115,105 +112,10 @@ export const PostClientTable = (props: { children?: React.ReactNode }) => {
},
})
- const columnHelper = createColumnHelper()
- // You can setup your own custom columns
- const enhancedColumns = [
- ...(context.actions?.delete
- ? [
- columnHelper.display({
- id: 'select',
- header: ({ table }) => (
-
- table.getToggleAllRowsSelectedHandler()({ target: { checked } })
- }
- />
- ),
- cell: ({ row }) => (
- {
- const handler = row.getToggleSelectedHandler()
- handler(event)
- }}
- />
- ),
- }),
- ]
- : []),
- ...context.columns,
- columnHelper.display({
- id: 'actions',
- cell: ({ row }) => {
- if (!context.actions?.one && !context.actions?.update && !context.actions?.delete) {
- return null
- }
-
- return (
-
-
-
- )
- },
- }),
- ]
-
const table = useListTable({
total: query.data?.total,
data: query.data?.data || [],
- columns: enhancedColumns,
+ columns: context.columns,
})
return (
diff --git a/examples/erp/genseki/collections/posts.tsx b/examples/erp/genseki/collections/posts.tsx
index 2fd00994..7e8edd8c 100644
--- a/examples/erp/genseki/collections/posts.tsx
+++ b/examples/erp/genseki/collections/posts.tsx
@@ -192,7 +192,10 @@ export const postsCollection = createPlugin('posts', (app) => {
search: ['title'],
sortBy: ['updatedAt', 'title'],
},
- actions: { delete: true, update: true, create: true },
+ toolbar: {
+ create: true,
+ delete: true,
+ },
layout: Layout,
page: CustomListPage,
})
diff --git a/examples/erp/genseki/collections/tags.client.ts b/examples/erp/genseki/collections/tags.client.tsx
similarity index 52%
rename from examples/erp/genseki/collections/tags.client.ts
rename to examples/erp/genseki/collections/tags.client.tsx
index 3fcdf121..ab8fd8bf 100644
--- a/examples/erp/genseki/collections/tags.client.ts
+++ b/examples/erp/genseki/collections/tags.client.tsx
@@ -2,7 +2,15 @@
import { createColumnHelper } from '@tanstack/react-table'
-import type { InferFields } from '@genseki/react'
+import {
+ actionsColumn,
+ createDeleteActionItem,
+ createEditActionItem,
+ createSeparatorItem,
+ createViewActionItem,
+ type InferFields,
+ selectColumn,
+} from '@genseki/react'
import type { fields } from './tags'
@@ -10,10 +18,17 @@ type Tag = InferFields
const columnHelper = createColumnHelper()
export const columns = [
+ selectColumn(),
columnHelper.accessor('__id', {
cell: (info) => info.getValue(),
}),
columnHelper.accessor('name', {
cell: (info) => info.getValue(),
}),
+ actionsColumn([
+ createViewActionItem(),
+ createEditActionItem(),
+ createSeparatorItem(),
+ createDeleteActionItem(),
+ ]),
]
diff --git a/examples/erp/genseki/collections/tags.ts b/examples/erp/genseki/collections/tags.ts
index fd7d36f9..7658c3a4 100644
--- a/examples/erp/genseki/collections/tags.ts
+++ b/examples/erp/genseki/collections/tags.ts
@@ -23,14 +23,14 @@ export const tagsCollection = createPlugin('tags', (app) => {
search: ['name'],
sortBy: ['name'],
},
- actions: {
+ toolbar: {
create: true,
- update: true,
delete: true,
},
})
)
.addPageAndApiRouter(collection.create(fields, {}))
.addPageAndApiRouter(collection.update(fields, {}))
+ .addPageAndApiRouter(collection.one(fields))
.addApiRouter(collection.deleteApiRouter(fields))
})
diff --git a/packages/react/src/core/collection/index.tsx b/packages/react/src/core/collection/index.tsx
index 50ce7379..163871d6 100644
--- a/packages/react/src/core/collection/index.tsx
+++ b/packages/react/src/core/collection/index.tsx
@@ -319,12 +319,7 @@ export type CollectionListConfig<
/**
* @param actions will decide whether or not to show actios in `list` view screen, This is not related to available features of collection, but rather only visible UI part of the `list` page
*/
- actions?: {
- create?: boolean
- update?: boolean
- delete?: boolean
- one?: boolean
- }
+ toolbar?: CollectionToolbarActions
}
export type CollectionUpdateApiArgs<
@@ -538,7 +533,7 @@ export class CollectionBuilder<
columns={config.columns}
search={config.configuration?.search}
sortBy={config.configuration?.sortBy}
- actions={config.actions}
+ toolbar={config.toolbar}
>
{page}
@@ -774,9 +769,7 @@ export class CollectionBuilder<
}
}
-export interface CollectionListActions {
+export interface CollectionToolbarActions {
create?: boolean
- update?: boolean
delete?: boolean
- one?: boolean
}
diff --git a/packages/react/src/react/views/collections/list/context.tsx b/packages/react/src/react/views/collections/list/context.tsx
index 879c7aa3..affea1ea 100644
--- a/packages/react/src/react/views/collections/list/context.tsx
+++ b/packages/react/src/react/views/collections/list/context.tsx
@@ -13,7 +13,7 @@ import { CollectionListPagination } from './table/pagination'
import { CollectionListToolbar, type CollectionListToolbarProps } from './toolbar'
import { toast } from '../../../..'
-import type { CollectionListActions } from '../../../../core/collection'
+import type { CollectionToolbarActions } from '../../../../core/collection'
import type { FieldsClient } from '../../../../core/field'
import { TableStatesProvider, useTableStatesContext } from '../../../providers/table'
import { useCollection } from '../context'
@@ -51,7 +51,7 @@ export interface CollectionListContextValue {
columns: ColumnDef[]
search?: string[]
sortBy?: string[]
- actions?: CollectionListActions
+ toolbar?: CollectionToolbarActions
// Helper functions
deleteRows: (rows?: string[]) => void
@@ -66,7 +66,7 @@ export interface CollectionListProviderProps {
columns: ColumnDef[]
search?: string[]
sortBy?: string[]
- actions?: CollectionListActions
+ toolbar?: CollectionToolbarActions
}
/**
diff --git a/packages/react/src/react/views/collections/list/default.tsx b/packages/react/src/react/views/collections/list/default.tsx
index 5626059e..aabc1645 100644
--- a/packages/react/src/react/views/collections/list/default.tsx
+++ b/packages/react/src/react/views/collections/list/default.tsx
@@ -1,10 +1,5 @@
'use client'
-import React, { useMemo } from 'react'
-
-import { DotsThreeVerticalIcon } from '@phosphor-icons/react'
-import { type ColumnDef, createColumnHelper } from '@tanstack/react-table'
-
import { Banner } from './banner'
import { CollectionListTableContainer } from './container'
import { useCollectionList } from './context'
@@ -12,113 +7,15 @@ import { CollectionListTable } from './table'
import { CollectionListPagination } from './table/pagination'
import { CollectionListToolbar } from './toolbar'
-import {
- BaseIcon,
- Checkbox,
- Menu,
- MenuContent,
- MenuItem,
- MenuSeparator,
- MenuTrigger,
-} from '../../../components'
-import { useNavigation } from '../../../providers'
-import type { BaseData } from '../types'
-
export function DefaultCollectionListPage() {
- const navigation = useNavigation()
-
const context = useCollectionList()
- const columns = useMemo(() => {
- if (context.isQuerying) return context.columns
-
- const columnHelper = createColumnHelper()
- return [
- ...(context.actions?.delete
- ? [
- columnHelper.display({
- id: 'select',
- header: ({ table }) => (
-
- table.getToggleAllRowsSelectedHandler()({ target: { checked } })
- }
- />
- ),
- cell: ({ row }) => (
- row.getToggleSelectedHandler()({ target: { checked } })}
- />
- ),
- }),
- ]
- : []),
- ...context.columns,
- columnHelper.display({
- id: 'actions',
- cell: ({ row }) => {
- if (!context.actions?.one && !context.actions?.update && !context.actions?.delete) {
- return null
- }
-
- return (
-
-
-
- )
- },
- }),
- ] as ColumnDef<{ __pk: string; __id: string }>[]
- }, [context.columns, context.actions, context.isQuerying])
-
return (
<>
- columns={columns} />
+ columns={context.columns} onRowClick={undefined} />
>
diff --git a/packages/react/src/react/views/collections/list/helper/actionsColumn.tsx b/packages/react/src/react/views/collections/list/helper/actionsColumn.tsx
new file mode 100644
index 00000000..139fedf5
--- /dev/null
+++ b/packages/react/src/react/views/collections/list/helper/actionsColumn.tsx
@@ -0,0 +1,113 @@
+import { useId } from 'react'
+
+import type { Icon } from '@phosphor-icons/react'
+import { DotsThreeVerticalIcon } from '@phosphor-icons/react/dist/ssr'
+import { createColumnHelper, type Row } from '@tanstack/react-table'
+
+import {
+ BaseIcon,
+ Menu,
+ MenuContent,
+ MenuItem,
+ MenuSeparator,
+ MenuTrigger,
+} from '../../../../components'
+import { type NavigationContextValue, useNavigation } from '../../../../providers'
+import type { BaseData } from '../../types'
+import { useCollectionList } from '../context'
+
+type ActionItem = (row: Row, key: string) => React.ReactNode
+
+export function createActionItem(render: (row: Row, key: string) => React.ReactNode) {
+ return render
+}
+
+function createDefaultActionItem(
+ title: string,
+ icon?: Icon,
+ onAction?: (
+ context: ReturnType,
+ row: Row,
+ navigation: NavigationContextValue
+ ) => string | void,
+ isDanger?: boolean
+) {
+ return createActionItem((row, key) => {
+ const context = useCollectionList()
+ const navigation = useNavigation()
+
+ return (
+
+ )
+ })
+}
+
+export function createViewActionItem(title: string = 'View', icon: Icon | undefined = undefined) {
+ return createDefaultActionItem(title, icon, (context, row, navigation) =>
+ navigation.navigate(`./${context.slug}/${row.original.__id}`)
+ )
+}
+
+export function createEditActionItem(title: string = 'Edit', icon: Icon | undefined = undefined) {
+ return createDefaultActionItem(title, icon, (context, row, navigation) =>
+ navigation.navigate(`./${context.slug}/update/${row.original.__id}`)
+ )
+}
+
+export function createDeleteActionItem(
+ title: string = 'Delete',
+ icon: Icon | undefined = undefined
+) {
+ return createDefaultActionItem(
+ title,
+ icon,
+ (context, row) => {
+ context.deleteRows([row.original.__id as string])
+ },
+ true
+ )
+}
+
+export function createSeparatorItem() {
+ return createActionItem((row, key) => )
+}
+
+export function actionsColumn(actionItems: ActionItem[] = []) {
+ const columnHelper = createColumnHelper()
+
+ return columnHelper.display({
+ id: 'actions',
+ cell: ({ row }) => {
+ return (
+
+
+
+ )
+ },
+ })
+}
diff --git a/packages/react/src/react/views/collections/list/helper/index.ts b/packages/react/src/react/views/collections/list/helper/index.ts
new file mode 100644
index 00000000..c7ae9417
--- /dev/null
+++ b/packages/react/src/react/views/collections/list/helper/index.ts
@@ -0,0 +1,2 @@
+export * from './actionsColumn'
+export * from './selectColumn'
diff --git a/packages/react/src/react/views/collections/list/helper/selectColumn.tsx b/packages/react/src/react/views/collections/list/helper/selectColumn.tsx
new file mode 100644
index 00000000..71cec0d1
--- /dev/null
+++ b/packages/react/src/react/views/collections/list/helper/selectColumn.tsx
@@ -0,0 +1,26 @@
+import { createColumnHelper } from '@tanstack/react-table'
+
+import { Checkbox } from '../../../../components'
+import type { BaseData } from '../../types'
+
+export function selectColumn() {
+ const columnHelper = createColumnHelper()
+
+ return columnHelper.display({
+ id: 'select',
+ header: ({ table }) => (
+ table.getToggleAllRowsSelectedHandler()({ target: { checked } })}
+ />
+ ),
+ cell: ({ row }) => (
+ row.getToggleSelectedHandler()({ target: { checked } })}
+ />
+ ),
+ })
+}
diff --git a/packages/react/src/react/views/collections/list/index.ts b/packages/react/src/react/views/collections/list/index.ts
index c00b9419..9d5540bb 100644
--- a/packages/react/src/react/views/collections/list/index.ts
+++ b/packages/react/src/react/views/collections/list/index.ts
@@ -1,5 +1,6 @@
export * from './banner'
export * from './context'
+export * from './helper'
export * from './hooks'
export * from './table'
export * from './table/pagination'
diff --git a/packages/react/src/react/views/collections/list/toolbar/index.tsx b/packages/react/src/react/views/collections/list/toolbar/index.tsx
index 018b6c0d..01e9ec50 100644
--- a/packages/react/src/react/views/collections/list/toolbar/index.tsx
+++ b/packages/react/src/react/views/collections/list/toolbar/index.tsx
@@ -8,7 +8,7 @@ import { CollectionListDelete } from './delete'
import { CollectionListFilter } from './filter'
import { CollectionListSearch } from './search'
-import type { CollectionListActions } from '../../../../../core/collection'
+import type { CollectionToolbarActions } from '../../../../../core/collection'
import { toast } from '../../../..'
import { BaseIcon, ButtonLink } from '../../../../components'
import { useTableStatesContext } from '../../../../providers/table'
@@ -16,7 +16,7 @@ import { useCollectionList } from '../context'
import { useCollectionDeleteMutation } from '../hooks/use-collection-delete'
export interface CollectionListToolbarProps {
- actions?: Partial
+ toolbar?: Partial
}
export function CollectionListToolbar(props: CollectionListToolbarProps) {
@@ -24,11 +24,9 @@ export function CollectionListToolbar(props: CollectionListToolbarProps) {
const queryClient = useQueryClient()
const { rowSelectionIds, setRowSelection, isRowsSelected } = useTableStatesContext()
- const actions: CollectionListActions = {
- create: props.actions?.create ?? context.actions?.create,
- update: props.actions?.update ?? context.actions?.update,
- delete: props.actions?.delete ?? context.actions?.delete,
- one: props.actions?.one ?? context.actions?.one,
+ const toolbar: CollectionToolbarActions = {
+ create: props.toolbar?.create ?? context.toolbar?.create,
+ delete: props.toolbar?.delete ?? context.toolbar?.delete,
}
const deleteMutation = useCollectionDeleteMutation({
slug: context.slug,
@@ -56,13 +54,13 @@ export function CollectionListToolbar(props: CollectionListToolbarProps) {
Back
- {actions?.delete && isRowsSelected && (
+ {toolbar?.delete && isRowsSelected && (
deleteMutation.mutate(rowSelectionIds)} />
)}
{/* TODO: Filter */}
- {actions?.create && }
+ {toolbar?.create && }
)