feat: search filter in list view
This commit is contained in:
@@ -71,13 +71,16 @@ export const initPage = async ({
|
||||
translations,
|
||||
})
|
||||
|
||||
const queryString = `${qs.stringify(searchParams, { addQueryPrefix: true })}`
|
||||
|
||||
const req = await createLocalReq(
|
||||
{
|
||||
fallbackLocale: null,
|
||||
locale: locale.code,
|
||||
req: {
|
||||
i18n,
|
||||
url: `${payload.config.serverURL}${route}${searchParams ? `${qs.stringify(searchParams, { addQueryPrefix: true })}` : ''}`,
|
||||
query: qs.parse(queryString, { ignoreQueryPrefix: true }),
|
||||
url: `${payload.config.serverURL}${route}${searchParams ? queryString : ''}`,
|
||||
} as PayloadRequest,
|
||||
user,
|
||||
},
|
||||
|
||||
@@ -45,9 +45,6 @@ export const DefaultListView: React.FC = () => {
|
||||
data,
|
||||
handlePageChange,
|
||||
handlePerPageChange,
|
||||
handleSearchChange,
|
||||
handleSortChange,
|
||||
handleWhereChange,
|
||||
hasCreatePermission,
|
||||
limit,
|
||||
modifySearchParams,
|
||||
@@ -130,12 +127,8 @@ export const DefaultListView: React.FC = () => {
|
||||
</header>
|
||||
<ListControls
|
||||
collectionConfig={collectionConfig}
|
||||
// textFieldsToBeSearched={textFieldsToBeSearched}
|
||||
// handleSearchChange={handleSearchChange}
|
||||
// handleSortChange={handleSortChange}
|
||||
// handleWhereChange={handleWhereChange}
|
||||
fieldMap={fieldMap}
|
||||
// modifySearchQuery={modifySearchParams}
|
||||
modifySearchQuery={modifySearchParams}
|
||||
titleField={titleField}
|
||||
/>
|
||||
{BeforeListTable}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
export type DefaultListViewProps = {
|
||||
collectionSlug: SanitizedCollectionConfig['slug']
|
||||
listSearchableFields: SanitizedCollectionConfig['admin']['listSearchableFields']
|
||||
}
|
||||
|
||||
export type ListIndexProps = {
|
||||
@@ -12,5 +13,5 @@ export type ListIndexProps = {
|
||||
export type ListPreferences = {
|
||||
columns: ColumnPreferences
|
||||
limit: number
|
||||
sort: number
|
||||
sort: string
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AdminViewProps } from 'payload/types'
|
||||
import type { Where } from 'payload/types'
|
||||
|
||||
import {
|
||||
HydrateClientUser,
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
TableColumnsProvider,
|
||||
} from '@payloadcms/ui'
|
||||
import { notFound } from 'next/navigation.js'
|
||||
import { isEntityHidden } from 'payload/utilities'
|
||||
import { createClientCollectionConfig } from 'packages/payload/src/collections/config/client.js'
|
||||
import { type AdminViewProps } from 'payload/types'
|
||||
import { isEntityHidden, isNumber, mergeListSearchAndWhere } from 'payload/utilities'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { DefaultListViewProps, ListPreferences } from './Default/types.js'
|
||||
@@ -25,6 +27,7 @@ export const ListView: React.FC<AdminViewProps> = async ({ initPageResult, searc
|
||||
locale,
|
||||
payload,
|
||||
payload: { config },
|
||||
query,
|
||||
user,
|
||||
},
|
||||
} = initPageResult
|
||||
@@ -43,6 +46,7 @@ export const ListView: React.FC<AdminViewProps> = async ({ initPageResult, searc
|
||||
collection: 'payload-preferences',
|
||||
depth: 0,
|
||||
limit: 1,
|
||||
user,
|
||||
where: {
|
||||
key: {
|
||||
equals: `${collectionSlug}-list`,
|
||||
@@ -73,31 +77,53 @@ export const ListView: React.FC<AdminViewProps> = async ({ initPageResult, searc
|
||||
CustomListView = CustomList.Component
|
||||
}
|
||||
|
||||
const limit = Number(searchParams?.limit) || collectionConfig.admin.pagination.defaultLimit
|
||||
const page = isNumber(query?.page) ? query.page : 0
|
||||
const whereQuery = mergeListSearchAndWhere({
|
||||
collectionConfig,
|
||||
query: {
|
||||
search: typeof query?.search === 'string' ? query.search : undefined,
|
||||
where: (query?.where as Where) || undefined,
|
||||
},
|
||||
})
|
||||
const limit = isNumber(query?.limit)
|
||||
? query.limit
|
||||
: listPreferences?.limit || collectionConfig.admin.pagination.defaultLimit
|
||||
const sort =
|
||||
query?.sort && typeof query.sort === 'string'
|
||||
? query.sort
|
||||
: listPreferences?.sort || undefined
|
||||
|
||||
const data = await payload.find({
|
||||
collection: collectionSlug,
|
||||
depth: 0,
|
||||
draft: true,
|
||||
fallbackLocale: null,
|
||||
limit,
|
||||
locale,
|
||||
overrideAccess: false,
|
||||
page,
|
||||
sort,
|
||||
user,
|
||||
where: whereQuery || {},
|
||||
})
|
||||
|
||||
const viewComponentProps: DefaultListViewProps = {
|
||||
collectionSlug,
|
||||
listSearchableFields: collectionConfig.admin.listSearchableFields,
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<HydrateClientUser permissions={permissions} user={user} />
|
||||
<ListInfoProvider
|
||||
collectionConfig={createClientCollectionConfig(collectionConfig)}
|
||||
collectionSlug={collectionSlug}
|
||||
data={data}
|
||||
hasCreatePermission={permissions?.collections?.[collectionSlug]?.create?.permission}
|
||||
limit={limit}
|
||||
listSearchableFields={collectionConfig.admin.listSearchableFields}
|
||||
newDocumentURL={`${admin}/collections/${collectionSlug}/create`}
|
||||
page={page}
|
||||
>
|
||||
<TableColumnsProvider
|
||||
collectionSlug={collectionSlug}
|
||||
|
||||
@@ -37,8 +37,9 @@ export { isPlainObject } from '../utilities/isPlainObject.js'
|
||||
|
||||
export { isValidID } from '../utilities/isValidID.js'
|
||||
export { default as isolateObjectProperty } from '../utilities/isolateObjectProperty.js'
|
||||
|
||||
export { mapAsync } from '../utilities/mapAsync.js'
|
||||
|
||||
export { mergeListSearchAndWhere } from '../utilities/mergeListSearchAndWhere.js'
|
||||
export { setsAreEqual } from '../utilities/setsAreEqual.js'
|
||||
export { default as toKebabCase } from '../utilities/toKebabCase.js'
|
||||
export { default as wait } from '../utilities/wait.js'
|
||||
|
||||
74
packages/payload/src/utilities/mergeListSearchAndWhere.ts
Normal file
74
packages/payload/src/utilities/mergeListSearchAndWhere.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import QueryString from 'qs'
|
||||
|
||||
import type { SanitizedCollectionConfig } from '../collections/config/types.js'
|
||||
import type { FieldAffectingData } from '../fields/config/types.js'
|
||||
import type { Where } from '../types/index.js'
|
||||
|
||||
import { fieldAffectsData } from '../fields/config/types.js'
|
||||
import { default as flattenTopLevelFields } from './flattenTopLevelFields.js'
|
||||
|
||||
const hoistQueryParamsToAnd = (where: Where, queryParams: Where) => {
|
||||
if ('and' in where) {
|
||||
where.and.push(queryParams)
|
||||
} else if ('or' in where) {
|
||||
where = {
|
||||
and: [where, queryParams],
|
||||
}
|
||||
} else {
|
||||
where = {
|
||||
and: [where, queryParams],
|
||||
}
|
||||
}
|
||||
|
||||
return where
|
||||
}
|
||||
|
||||
const getTitleField = (collection: SanitizedCollectionConfig): FieldAffectingData => {
|
||||
const {
|
||||
admin: { useAsTitle },
|
||||
fields,
|
||||
} = collection
|
||||
|
||||
const topLevelFields = flattenTopLevelFields(fields)
|
||||
return topLevelFields.find(
|
||||
(field) => fieldAffectsData(field) && field.name === useAsTitle,
|
||||
) as FieldAffectingData
|
||||
}
|
||||
|
||||
type Args = {
|
||||
collectionConfig: SanitizedCollectionConfig
|
||||
query: {
|
||||
search?: string
|
||||
where?: Where
|
||||
}
|
||||
}
|
||||
|
||||
export const mergeListSearchAndWhere = ({ collectionConfig, query }: Args): Where => {
|
||||
const search = query?.search || undefined
|
||||
let where = QueryString.parse(typeof query?.where === 'string' ? query.where : '') as Where
|
||||
|
||||
if (search) {
|
||||
let copyOfWhere = { ...(where || {}) }
|
||||
|
||||
const searchAsConditions = (
|
||||
collectionConfig.admin.listSearchableFields || [getTitleField(collectionConfig)?.name || 'id']
|
||||
).map((fieldName) => {
|
||||
return {
|
||||
[fieldName]: {
|
||||
like: search,
|
||||
},
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (searchAsConditions.length > 0) {
|
||||
const conditionalSearchFields = {
|
||||
or: [...searchAsConditions],
|
||||
}
|
||||
copyOfWhere = hoistQueryParamsToAnd(copyOfWhere, conditionalSearchFields)
|
||||
}
|
||||
|
||||
where = copyOfWhere
|
||||
}
|
||||
|
||||
return where
|
||||
}
|
||||
@@ -11,6 +11,7 @@ const AnimateHeight = (AnimateHeightImport.default ||
|
||||
import type { Props } from './types.js'
|
||||
|
||||
import { Chevron } from '../../icons/Chevron/index.js'
|
||||
import { useListInfo } from '../../index.js'
|
||||
import { useSearchParams } from '../../providers/SearchParams/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import ColumnSelector from '../ColumnSelector/index.js'
|
||||
@@ -37,15 +38,13 @@ export const ListControls: React.FC<Props> = (props) => {
|
||||
enableColumns = true,
|
||||
enableSort = false,
|
||||
fieldMap,
|
||||
handleSearchChange,
|
||||
handleSortChange,
|
||||
handleWhereChange,
|
||||
modifySearchQuery = true,
|
||||
textFieldsToBeSearched,
|
||||
titleField,
|
||||
} = props
|
||||
|
||||
const { useWindowInfo } = facelessUIImport
|
||||
const { handleSearchChange, handleWhereChange } = useListInfo()
|
||||
|
||||
const { searchParams } = useSearchParams()
|
||||
const shouldInitializeWhereOpened = validateWhereQuery(searchParams?.where)
|
||||
@@ -68,7 +67,6 @@ export const ListControls: React.FC<Props> = (props) => {
|
||||
fieldName={titleField && fieldAffectsData(titleField) ? titleField.name : undefined}
|
||||
handleChange={handleSearchChange}
|
||||
listSearchableFields={textFieldsToBeSearched}
|
||||
modifySearchQuery={modifySearchQuery}
|
||||
/>
|
||||
<div className={`${baseClass}__buttons`}>
|
||||
<div className={`${baseClass}__buttons-wrap`}>
|
||||
|
||||
@@ -247,6 +247,7 @@ export const ListDrawerContent: React.FC<ListDrawerProps> = ({
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
aria-label={t('general:close')}
|
||||
className={`${baseClass}__header-close`}
|
||||
onClick={() => {
|
||||
closeModal(drawerSlug)
|
||||
@@ -277,6 +278,7 @@ export const ListDrawerContent: React.FC<ListDrawerProps> = ({
|
||||
)}
|
||||
</header>
|
||||
}
|
||||
collectionConfig={selectedCollectionConfig}
|
||||
collectionSlug={selectedCollectionConfig.slug}
|
||||
data={data}
|
||||
handlePageChange={setPage}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
// TODO: abstract the `next/navigation` dependency out from this component
|
||||
import { usePathname, useRouter } from 'next/navigation.js'
|
||||
import queryString from 'qs'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
@@ -15,13 +14,7 @@ import './index.scss'
|
||||
const baseClass = 'search-filter'
|
||||
|
||||
const SearchFilter: React.FC<Props> = (props) => {
|
||||
const {
|
||||
fieldLabel = 'ID',
|
||||
fieldName = 'id',
|
||||
handleChange,
|
||||
listSearchableFields,
|
||||
modifySearchQuery = true,
|
||||
} = props
|
||||
const { fieldLabel = 'ID', fieldName = 'id', handleChange, listSearchableFields } = props
|
||||
|
||||
const { searchParams } = useSearchParams()
|
||||
const router = useRouter()
|
||||
@@ -40,17 +33,6 @@ const SearchFilter: React.FC<Props> = (props) => {
|
||||
useEffect(() => {
|
||||
if (debouncedSearch !== previousSearch) {
|
||||
if (handleChange) handleChange(debouncedSearch)
|
||||
|
||||
if (modifySearchQuery) {
|
||||
const search = queryString.stringify({
|
||||
...searchParams,
|
||||
page: 1,
|
||||
search: debouncedSearch || undefined,
|
||||
})
|
||||
|
||||
router.replace(`${pathname}?${search}`)
|
||||
}
|
||||
|
||||
setPreviousSearch(debouncedSearch)
|
||||
}
|
||||
}, [
|
||||
@@ -60,24 +42,26 @@ const SearchFilter: React.FC<Props> = (props) => {
|
||||
fieldName,
|
||||
searchParams,
|
||||
handleChange,
|
||||
modifySearchQuery,
|
||||
listSearchableFields,
|
||||
pathname,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (listSearchableFields?.length > 0) {
|
||||
placeholder.current = listSearchableFields.reduce<string>((prev, curr, i) => {
|
||||
if (i === 0) {
|
||||
return `${t('general:searchBy', {
|
||||
label: getTranslation(curr.label || curr.name, i18n),
|
||||
})}`
|
||||
}
|
||||
if (i === listSearchableFields.length - 1) {
|
||||
return `${prev} ${t('general:or')} ${getTranslation(curr.label || curr.name, i18n)}`
|
||||
}
|
||||
return `${prev}, ${getTranslation(curr.label || curr.name, i18n)}`
|
||||
}, '')
|
||||
placeholder.current = listSearchableFields.reduce(
|
||||
(placeholderText: string, field, i: number) => {
|
||||
if (i === 0) {
|
||||
return `${t('general:searchBy', {
|
||||
label: getTranslation(field.label || field.name, i18n),
|
||||
})}`
|
||||
}
|
||||
if (i === listSearchableFields.length - 1) {
|
||||
return `${placeholderText} ${t('general:or')} ${getTranslation(field.label || field.name, i18n)}`
|
||||
}
|
||||
return `${placeholderText}, ${getTranslation(field?.label || field?.name, i18n)}`
|
||||
},
|
||||
'',
|
||||
)
|
||||
} else {
|
||||
placeholder.current = t('general:searchBy', { label: getTranslation(fieldLabel, i18n) })
|
||||
}
|
||||
|
||||
@@ -5,5 +5,4 @@ export type Props = {
|
||||
fieldName?: string
|
||||
handleChange?: (search: string) => void
|
||||
listSearchableFields?: FieldAffectingData[]
|
||||
modifySearchQuery?: boolean
|
||||
}
|
||||
|
||||
@@ -1,16 +1,178 @@
|
||||
'use client'
|
||||
import type { Where } from 'payload/types.js'
|
||||
|
||||
import { useRouter } from 'next/navigation.js'
|
||||
import { isNumber } from 'payload/utilities.js'
|
||||
import QueryString from 'qs'
|
||||
import React, { createContext, useContext } from 'react'
|
||||
|
||||
import type { ListInfoContext, ListInfoProps } from './types.js'
|
||||
|
||||
import { usePreferences } from '../Preferences/index.js'
|
||||
|
||||
const Context = createContext({} as ListInfoContext)
|
||||
|
||||
export const useListInfo = (): ListInfoContext => useContext(Context)
|
||||
|
||||
type RefineOverrides = {
|
||||
limit?: string
|
||||
page?: string
|
||||
search?: string
|
||||
sort?: string
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export const ListInfoProvider: React.FC<
|
||||
ListInfoProps & {
|
||||
children: React.ReactNode
|
||||
}
|
||||
> = ({ children, ...rest }) => {
|
||||
return <Context.Provider value={{ ...rest }}>{children}</Context.Provider>
|
||||
> = ({
|
||||
children,
|
||||
collectionSlug,
|
||||
data,
|
||||
handlePageChange: handlePageChangeFromProps,
|
||||
handlePerPageChange: handlePerPageChangeFromProps,
|
||||
handleSearchChange: handleSearchChangeFromProps,
|
||||
handleSortChange: handleSortChangeFromProps,
|
||||
handleWhereChange: handleWhereChangeFromProps,
|
||||
hasCreatePermission,
|
||||
limit,
|
||||
listSearchableFields,
|
||||
modifySearchParams,
|
||||
newDocumentURL,
|
||||
sort,
|
||||
titleField,
|
||||
}) => {
|
||||
const router = useRouter()
|
||||
const { setPreference } = usePreferences()
|
||||
const hasSetInitialParams = React.useRef(false)
|
||||
|
||||
const refineListData = React.useCallback(
|
||||
async (query: RefineOverrides) => {
|
||||
if (!modifySearchParams) return
|
||||
|
||||
const currentQuery = QueryString.parse(window.location.search, {
|
||||
ignoreQueryPrefix: true,
|
||||
}) as QueryString.ParsedQs & {
|
||||
where: Where
|
||||
}
|
||||
|
||||
const updatedPreferences: Record<string, unknown> = {}
|
||||
let updatePreferences = false
|
||||
|
||||
if ('limit' in query) {
|
||||
updatedPreferences.limit = query.limit
|
||||
updatePreferences = true
|
||||
}
|
||||
if ('sort' in query) {
|
||||
updatedPreferences.sort = query.sort
|
||||
updatePreferences = true
|
||||
}
|
||||
if (updatePreferences) {
|
||||
await setPreference(`${collectionSlug}-list`, updatedPreferences)
|
||||
}
|
||||
|
||||
const params = {
|
||||
limit: 'limit' in query ? query.limit : currentQuery?.limit,
|
||||
page: 'page' in query ? query.page : currentQuery?.page,
|
||||
search: 'search' in query ? query.search : currentQuery?.search,
|
||||
sort: 'sort' in query ? query.sort : currentQuery?.sort,
|
||||
where: 'where' in query ? query.where : currentQuery?.where,
|
||||
}
|
||||
|
||||
router.replace(`?${QueryString.stringify(params)}`)
|
||||
},
|
||||
[collectionSlug, modifySearchParams, router, setPreference],
|
||||
)
|
||||
|
||||
const handlePageChange = React.useCallback(
|
||||
async (arg: number) => {
|
||||
if (typeof handlePageChangeFromProps === 'function') {
|
||||
handlePageChangeFromProps(arg)
|
||||
}
|
||||
await refineListData({ page: String(arg) })
|
||||
},
|
||||
[refineListData, handlePageChangeFromProps],
|
||||
)
|
||||
const handlePerPageChange = React.useCallback(
|
||||
async (arg: number) => {
|
||||
if (typeof handlePerPageChangeFromProps === 'function') {
|
||||
handlePerPageChangeFromProps(arg)
|
||||
}
|
||||
await refineListData({ limit: String(arg) })
|
||||
},
|
||||
[refineListData, handlePerPageChangeFromProps],
|
||||
)
|
||||
const handleSearchChange = React.useCallback(
|
||||
async (arg: string) => {
|
||||
if (typeof handleSearchChangeFromProps === 'function') {
|
||||
handleSearchChangeFromProps(arg)
|
||||
}
|
||||
await refineListData({ search: arg })
|
||||
},
|
||||
[refineListData, handleSearchChangeFromProps],
|
||||
)
|
||||
const handleSortChange = React.useCallback(
|
||||
async (arg: string) => {
|
||||
if (typeof handleSortChangeFromProps === 'function') {
|
||||
handleSortChangeFromProps(arg)
|
||||
}
|
||||
await refineListData({ sort: arg })
|
||||
},
|
||||
[refineListData, handleSortChangeFromProps],
|
||||
)
|
||||
const handleWhereChange = React.useCallback(
|
||||
async (arg: Where) => {
|
||||
if (typeof handleWhereChangeFromProps === 'function') {
|
||||
handleWhereChangeFromProps(arg)
|
||||
}
|
||||
await refineListData({ where: arg })
|
||||
},
|
||||
[refineListData, handleWhereChangeFromProps],
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!hasSetInitialParams.current) {
|
||||
const currentQuery = QueryString.parse(window.location.search, {
|
||||
ignoreQueryPrefix: true,
|
||||
})
|
||||
let shouldUpdateQueryString = false
|
||||
|
||||
if (isNumber(limit) && !('limit' in currentQuery)) {
|
||||
currentQuery.limit = String(limit)
|
||||
shouldUpdateQueryString = true
|
||||
}
|
||||
if (sort && !('sort' in currentQuery)) {
|
||||
currentQuery.sort = sort
|
||||
shouldUpdateQueryString = true
|
||||
}
|
||||
if (shouldUpdateQueryString) {
|
||||
router.replace(`?${QueryString.stringify(currentQuery)}`)
|
||||
}
|
||||
|
||||
hasSetInitialParams.current = true
|
||||
}
|
||||
}, [sort, limit, router])
|
||||
|
||||
return (
|
||||
<Context.Provider
|
||||
value={{
|
||||
collectionSlug,
|
||||
data,
|
||||
handlePageChange,
|
||||
handlePerPageChange,
|
||||
handleSearchChange,
|
||||
handleSortChange,
|
||||
handleWhereChange,
|
||||
hasCreatePermission,
|
||||
limit,
|
||||
listSearchableFields,
|
||||
modifySearchParams,
|
||||
newDocumentURL,
|
||||
titleField,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { Data, FieldAffectingData, SanitizedCollectionConfig, Where } from 'payload/types'
|
||||
import type {
|
||||
ClientConfig,
|
||||
Data,
|
||||
FieldAffectingData,
|
||||
SanitizedCollectionConfig,
|
||||
Where,
|
||||
} from 'payload/types'
|
||||
import type React from 'react'
|
||||
|
||||
import type { Column } from '../../elements/Table/types.js'
|
||||
@@ -7,6 +13,7 @@ export type ColumnPreferences = Pick<Column, 'accessor' | 'active'>[]
|
||||
|
||||
export type ListInfoProps = {
|
||||
Header?: React.ReactNode
|
||||
collectionConfig: ClientConfig['collections'][0]
|
||||
collectionSlug: SanitizedCollectionConfig['slug']
|
||||
data: Data
|
||||
handlePageChange?: (page: number) => void
|
||||
@@ -16,10 +23,13 @@ export type ListInfoProps = {
|
||||
handleWhereChange?: (where: Where) => void
|
||||
hasCreatePermission: boolean
|
||||
limit: SanitizedCollectionConfig['admin']['pagination']['defaultLimit']
|
||||
listSearchableFields?: SanitizedCollectionConfig['admin']['listSearchableFields']
|
||||
modifySearchParams?: false
|
||||
newDocumentURL: string
|
||||
page?: number
|
||||
setLimit?: (limit: number) => void
|
||||
setSort?: (sort: string) => void
|
||||
sort?: string
|
||||
titleField?: FieldAffectingData
|
||||
}
|
||||
|
||||
@@ -28,7 +38,19 @@ export type ListInfo = ListInfoProps & {
|
||||
// see `DocumentInfo` for an example
|
||||
}
|
||||
|
||||
export type ListInfoContext = ListInfo & {
|
||||
// add context methods here as needed
|
||||
// see `DocumentInfoContext` for an example
|
||||
export type ListInfoContext = {
|
||||
Header?: React.ReactNode
|
||||
collectionSlug: string
|
||||
data: Data
|
||||
handlePageChange?: (page: number) => void
|
||||
handlePerPageChange?: (limit: number) => void
|
||||
handleSearchChange?: (search: string) => void
|
||||
handleSortChange?: (sort: string) => void
|
||||
handleWhereChange?: (where: Where) => void
|
||||
hasCreatePermission: boolean
|
||||
limit: number
|
||||
listSearchableFields: SanitizedCollectionConfig['admin']['listSearchableFields']
|
||||
modifySearchParams: boolean
|
||||
newDocumentURL: string
|
||||
titleField?: FieldAffectingData
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user