From b9dfa1aafe2e5de369996a718ff610a065a070b9 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Fri, 23 Feb 2024 12:32:43 -0500 Subject: [PATCH] chore(ui): server renders custom list view --- .gitignore | 3 +- .../[collection]/[...segments]/page.tsx | 1 - .../collections/[collection]/page.tsx | 4 +- packages/next/src/pages/API/index.client.tsx | 8 +- .../pages/{CollectionList => List}/index.tsx | 29 +-- .../elements/DocumentDrawer/DrawerContent.tsx | 16 +- .../ui/src/elements/DocumentDrawer/index.tsx | 3 +- .../ui/src/elements/DocumentDrawer/types.ts | 6 +- .../src/elements/ListDrawer/DrawerContent.tsx | 199 +++++++++--------- .../ui/src/providers/DocumentInfo/types.ts | 5 +- .../ListInfo/SetDocumentInfo/index.tsx | 15 ++ packages/ui/src/providers/ListInfo/index.tsx | 32 +++ packages/ui/src/providers/ListInfo/types.ts | 30 +++ .../src/utilities/buildComponentMap/index.tsx | 14 +- .../src/utilities/buildComponentMap/types.ts | 1 + packages/ui/src/views/Edit/Upload/index.tsx | 3 - packages/ui/src/views/Edit/index.tsx | 13 +- packages/ui/src/views/List/index.tsx | 8 +- packages/ui/src/views/List/types.ts | 30 --- 19 files changed, 237 insertions(+), 183 deletions(-) rename packages/next/src/pages/{CollectionList => List}/index.tsx (73%) create mode 100644 packages/ui/src/providers/ListInfo/SetDocumentInfo/index.tsx create mode 100644 packages/ui/src/providers/ListInfo/index.tsx create mode 100644 packages/ui/src/providers/ListInfo/types.ts diff --git a/.gitignore b/.gitignore index 737c84170c..ce64855c0e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ dist test-results .devcontainer /migrations +/media # Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode # Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,windows,webstorm,sublimetext,visualstudiocode @@ -371,4 +372,4 @@ $RECYCLE.BIN/ # End of https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode -/build \ No newline at end of file +/build diff --git a/app/(payload)/admin/(dashboard)/collections/[collection]/[...segments]/page.tsx b/app/(payload)/admin/(dashboard)/collections/[collection]/[...segments]/page.tsx index d26929445e..59ddd6bb32 100644 --- a/app/(payload)/admin/(dashboard)/collections/[collection]/[...segments]/page.tsx +++ b/app/(payload)/admin/(dashboard)/collections/[collection]/[...segments]/page.tsx @@ -1,6 +1,5 @@ /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */ -// import { CollectionList } from '@payloadcms/next/pages/CollectionList' import { Document } from '@payloadcms/next/pages/Document' import config from 'payload-config' diff --git a/app/(payload)/admin/(dashboard)/collections/[collection]/page.tsx b/app/(payload)/admin/(dashboard)/collections/[collection]/page.tsx index 1dcc5c8dc3..746fb259a1 100644 --- a/app/(payload)/admin/(dashboard)/collections/[collection]/page.tsx +++ b/app/(payload)/admin/(dashboard)/collections/[collection]/page.tsx @@ -1,10 +1,10 @@ /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */ -import { CollectionList } from '@payloadcms/next/pages/CollectionList' +import { ListView } from '@payloadcms/next/pages/List' import config from 'payload-config' export default ({ params, searchParams }) => - CollectionList({ + ListView({ collectionSlug: params.collection, searchParams, config, diff --git a/packages/next/src/pages/API/index.client.tsx b/packages/next/src/pages/API/index.client.tsx index 479456c30a..511e33cc9c 100644 --- a/packages/next/src/pages/API/index.client.tsx +++ b/packages/next/src/pages/API/index.client.tsx @@ -9,12 +9,12 @@ import { Form, Select, Number as NumberInput, - EditViewProps, useConfig, MinimizeMaximize, useActions, useTranslation, useLocale, + ServerSideEditViewProps, } from '@payloadcms/ui' import { RenderJSON } from './RenderJSON' import { useSearchParams } from 'next/navigation' @@ -24,7 +24,7 @@ import './index.scss' const baseClass = 'query-inspector' -export const APIViewClient: React.FC = (props) => { +export const APIViewClient: React.FC = (props) => { const { data: initialData } = props const searchParams = useSearchParams() @@ -133,8 +133,8 @@ export const APIViewClient: React.FC = (props) => { > - - - + + + + + ) } diff --git a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx index ada67287f4..eee4f9331f 100644 --- a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx +++ b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx @@ -98,7 +98,7 @@ const Content: React.FC = ({ // (!isEditing && (docPermissions as CollectionPermission)?.create?.permission) useEffect(() => { - if (!hasInitializedState.current && data) { + if (!hasInitializedState.current && (!initialID.current || (initialID.current && data))) { const getInitialState = async () => { const result = await getFormState({ serverURL, @@ -106,7 +106,7 @@ const Content: React.FC = ({ body: { id, operation: isEditing ? 'update' : 'create', - data, + data: data || {}, docPreferences: null, // TODO: get this schemaPath, }, @@ -119,9 +119,7 @@ const Content: React.FC = ({ } }, [apiRoute, data, id, isEditing, schemaPath, serverURL]) - const isLoading = !initialState || isLoadingDocument - - if (isLoading) { + if (!initialState || isLoadingDocument) { return } @@ -155,7 +153,7 @@ const Content: React.FC = ({ } initialData={data} disableActions - // disableLeaveWithoutSaving + disableLeaveWithoutSaving // hasSavePermission={hasSavePermission} // isEditing={isEditing} // isLoading, @@ -164,7 +162,7 @@ const Content: React.FC = ({ collectionSlug={collectionConfig.slug} docPermissions={{} as CollectionPermission} // TODO; get this docPreferences={null} // TODO: get this - initialState + initialState={initialState} > {Edit} @@ -178,10 +176,10 @@ const Content: React.FC = ({ export const DocumentDrawerContent: React.FC = (props) => { const { id: idFromProps, collectionSlug, onSave: onSaveFromProps } = props const [collectionConfig] = useRelatedCollections(collectionSlug) - const [id, setId] = useState(idFromProps) + const [id, setId] = useState(idFromProps) const onSave = useCallback( - (args) => { + async (args) => { setId(args.doc.id) if (typeof onSaveFromProps === 'function') { diff --git a/packages/ui/src/elements/DocumentDrawer/index.tsx b/packages/ui/src/elements/DocumentDrawer/index.tsx index a4b9e96e0d..02c0e9c8a6 100644 --- a/packages/ui/src/elements/DocumentDrawer/index.tsx +++ b/packages/ui/src/elements/DocumentDrawer/index.tsx @@ -23,7 +23,7 @@ const formatDocumentDrawerSlug = ({ }: { collectionSlug: string depth: number - id: string + id: string | number uuid: string // supply when creating a new document and no id is available }) => `doc-drawer_${collectionSlug}_${depth}${id ? `_${id}` : ''}_${uuid}` @@ -69,6 +69,7 @@ export const useDocumentDrawer: UseDocumentDrawer = ({ id, collectionSlug }) => const uuid = useId() const { closeModal, modalState, openModal, toggleModal } = useModal() const [isOpen, setIsOpen] = useState(false) + const drawerSlug = formatDocumentDrawerSlug({ id, collectionSlug, diff --git a/packages/ui/src/elements/DocumentDrawer/types.ts b/packages/ui/src/elements/DocumentDrawer/types.ts index f9fa4d63fa..3955c6184f 100644 --- a/packages/ui/src/elements/DocumentDrawer/types.ts +++ b/packages/ui/src/elements/DocumentDrawer/types.ts @@ -1,14 +1,14 @@ import type React from 'react' import type { HTMLAttributes } from 'react' -import type { EditViewProps } from '../../views/types' import type { Props as DrawerProps } from '../Drawer/types' +import { DocumentInfoContext } from '../../providers/DocumentInfo/types' export type DocumentDrawerProps = Pick & { collectionSlug: string drawerSlug?: string - id?: string - onSave?: EditViewProps['onSave'] + id?: string | number + onSave?: DocumentInfoContext['onSave'] } export type DocumentTogglerProps = HTMLAttributes & { diff --git a/packages/ui/src/elements/ListDrawer/DrawerContent.tsx b/packages/ui/src/elements/ListDrawer/DrawerContent.tsx index e3d1696b1a..777cea2273 100644 --- a/packages/ui/src/elements/ListDrawer/DrawerContent.tsx +++ b/packages/ui/src/elements/ListDrawer/DrawerContent.tsx @@ -14,17 +14,15 @@ import Label from '../../forms/Label' import { X } from '../../icons/X' import { useAuth } from '../../providers/Auth' import { useConfig } from '../../providers/Config' -import { DocumentInfoProvider } from '../../providers/DocumentInfo' import { usePreferences } from '../../providers/Preferences' -import { RenderCustomComponent } from '../RenderCustomComponent' -import { DefaultList } from '../../views/List' -// import formatFields from '../../views/List/formatFields' import { useDocumentDrawer } from '../DocumentDrawer' import Pill from '../Pill' import ReactSelect from '../ReactSelect' import { TableColumnsProvider } from '../TableColumns' import ViewDescription from '../ViewDescription' -import { DefaultListViewProps } from '../../views/List/types' +import { useComponentMap } from '../..' +import { ListInfoProvider } from '../../providers/ListInfo' +import { LoadingOverlay } from '../Loading' const hoistQueryParamsToAnd = (where: Where, queryParams: Where) => { if ('and' in where) { @@ -55,10 +53,12 @@ export const ListDrawerContent: React.FC = ({ const { setPreference } = usePreferences() const { closeModal, isModalOpen } = useModal() const [limit, setLimit] = useState() - const [sort, setSort] = useState(null) - const [page, setPage] = useState(1) - const [where, setWhere] = useState(null) - const [search, setSearch] = useState('') + const [sort, setSort] = useState(null) + const [page, setPage] = useState(1) + const [where, setWhere] = useState(null) + const [search, setSearch] = useState('') + + const { componentMap } = useComponentMap() const { collections, @@ -78,6 +78,8 @@ export const ListDrawerContent: React.FC = ({ ) }) + const { List } = componentMap.collections?.[selectedCollectionConfig?.slug] || {} + const [selectedOption, setSelectedOption] = useState<{ label: string; value: string }>(() => selectedCollectionConfig ? { @@ -129,7 +131,7 @@ export const ListDrawerContent: React.FC = ({ const isOpen = isModalOpen(drawerSlug) const apiURL = isOpen ? `${serverURL}${api}/${selectedCollectionConfig.slug}` : null const [cacheBust, dispatchCacheBust] = useReducer((state) => state + 1, 0) // used to force a re-fetch even when apiURL is unchanged - const [{ data, isError }, { setParams }] = usePayloadAPI(apiURL, {}) + const [{ data, isError, isLoading: isLoadingList }, { setParams }] = usePayloadAPI(apiURL, {}) const moreThanOneAvailableCollection = enabledCollectionConfigs.length > 1 useEffect(() => { @@ -215,104 +217,93 @@ export const ListDrawerContent: React.FC = ({ return null } - const listComponent = selectedCollectionConfig?.admin?.components?.views?.List - let ListToRender = null - - if (listComponent && typeof listComponent === 'function') { - ListToRender = listComponent - } else if (typeof listComponent === 'object' && typeof listComponent.Component === 'function') { - ListToRender = listComponent.Component - } - - const componentProps: DefaultListViewProps = { - collectionSlug: selectedCollectionConfig.slug, - Header: ( -
-
-
-

- {!customHeader - ? getTranslation(selectedCollectionConfig?.labels?.plural, i18n) - : customHeader} -

- {hasCreatePermission && ( - - {t('general:createNew')} - - )} -
- -
- {selectedCollectionConfig?.admin?.description && ( -
- -
- )} - {moreThanOneAvailableCollection && ( -
-
- )} -
- ), - data, - handlePageChange: setPage, - handlePerPageChange: setLimit, - handleSearchChange: setSearch, - handleSortChange: setSort, - handleWhereChange: setWhere, - hasCreatePermission, - limit: limit || selectedCollectionConfig?.admin?.pagination?.defaultLimit, - modifySearchParams: false, - newDocumentURL: null, - setLimit, - setSort, - titleField, + if (isLoadingList) { + return } return ( - { - if (typeof onSelect === 'function') { - onSelect({ - collectionSlug: rowColl, - docID: rowData.id as string, - }) - } - }, - }, - ]} + +
+
+

+ {!customHeader + ? getTranslation(selectedCollectionConfig?.labels?.plural, i18n) + : customHeader} +

+ {hasCreatePermission && ( + + {t('general:createNew')} + + )} +
+ +
+ {selectedCollectionConfig?.admin?.description && ( +
+ +
+ )} + {moreThanOneAvailableCollection && ( +
+
+ )} + + } > - - - - -
+ { + if (typeof onSelect === 'function') { + onSelect({ + collectionSlug: rowColl, + docID: rowData.id as string, + }) + } + }, + }, + ]} + collectionSlug={selectedCollectionConfig.slug} + > + {List} + + + ) } diff --git a/packages/ui/src/providers/DocumentInfo/types.ts b/packages/ui/src/providers/DocumentInfo/types.ts index 4573777924..6384ee99e4 100644 --- a/packages/ui/src/providers/DocumentInfo/types.ts +++ b/packages/ui/src/providers/DocumentInfo/types.ts @@ -3,7 +3,6 @@ import type React from 'react' import type { SanitizedCollectionConfig, SanitizedGlobalConfig, - FormState, TypeWithTimestamps, TypeWithID, DocumentPermissions, @@ -12,6 +11,7 @@ import type { } from 'payload/types' import { PaginatedDocs, TypeWithVersion } from 'payload/database' +import { FormState } from '../../forms/Form/types' export type DocumentInfoProps = { AfterDocument?: React.ReactNode @@ -24,12 +24,13 @@ export type DocumentInfoProps = { id?: number | string initialData?: Data initialState?: FormState - onSave?: (data: Record) => Promise + onSave?: (data: Data) => Promise | void action?: string apiURL?: string docPreferences?: DocumentPreferences hasSavePermission?: boolean disableActions?: boolean + disableLeaveWithoutSaving?: boolean } export type DocumentInfo = DocumentInfoProps & { diff --git a/packages/ui/src/providers/ListInfo/SetDocumentInfo/index.tsx b/packages/ui/src/providers/ListInfo/SetDocumentInfo/index.tsx new file mode 100644 index 0000000000..f01a84beae --- /dev/null +++ b/packages/ui/src/providers/ListInfo/SetDocumentInfo/index.tsx @@ -0,0 +1,15 @@ +'use client' + +import { useEffect } from 'react' +import { useDocumentInfo } from '..' +import { DocumentInfo } from '../types' + +export const SetDocumentInfo: React.FC = (props) => { + const { setDocumentInfo } = useDocumentInfo() + + useEffect(() => { + setDocumentInfo(props) + }, [props]) + + return null +} diff --git a/packages/ui/src/providers/ListInfo/index.tsx b/packages/ui/src/providers/ListInfo/index.tsx new file mode 100644 index 0000000000..7cabfdc24c --- /dev/null +++ b/packages/ui/src/providers/ListInfo/index.tsx @@ -0,0 +1,32 @@ +'use client' +import React, { createContext, useContext, useState } from 'react' + +import type { ListInfo, ListInfoContext, ListInfoProps } from './types' +import { useConfig } from '../Config' + +const Context = createContext({} as ListInfoContext) + +export const useListInfo = (): ListInfoContext => useContext(Context) + +export const ListInfoProvider: React.FC< + ListInfoProps & { + children: React.ReactNode + } +> = ({ children, ...rest }) => { + const [listInfo, setListInfo] = useState({ + ...rest, + }) + + const { + routes: { api }, + serverURL, + collections, + globals, + } = useConfig() + + const value: ListInfoContext = { + ...listInfo, + } + + return {children} +} diff --git a/packages/ui/src/providers/ListInfo/types.ts b/packages/ui/src/providers/ListInfo/types.ts new file mode 100644 index 0000000000..8d8d944dda --- /dev/null +++ b/packages/ui/src/providers/ListInfo/types.ts @@ -0,0 +1,30 @@ +import { Data, FieldAffectingData, SanitizedCollectionConfig, Where } from 'payload/types' +import type React from 'react' + +export type ListInfoProps = { + Header?: React.ReactNode + collectionSlug: SanitizedCollectionConfig['slug'] + 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 | SanitizedCollectionConfig['admin']['pagination']['defaultLimit'] + modifySearchParams?: false + newDocumentURL: string + setLimit?: (limit: number) => void + setSort?: (sort: string) => void + titleField?: FieldAffectingData +} + +export type ListInfo = ListInfoProps & { + // add context properties here as needed + // see `DocumentInfo` for an example +} + +export type ListInfoContext = ListInfo & { + // add context methods here as needed + // see `DocumentInfoContext` for an example +} diff --git a/packages/ui/src/utilities/buildComponentMap/index.tsx b/packages/ui/src/utilities/buildComponentMap/index.tsx index 7aae907919..654ddf34d0 100644 --- a/packages/ui/src/utilities/buildComponentMap/index.tsx +++ b/packages/ui/src/utilities/buildComponentMap/index.tsx @@ -4,7 +4,8 @@ import type { SanitizedConfig } from 'payload/types' import { mapFields } from './mapFields' import { CollectionComponentMap, ComponentMap, GlobalComponentMap } from './types' import { DefaultEditView } from '../../views/Edit' -import { EditViewProps } from '../..' +import { DefaultList } from '../../views/List' +import { EditViewProps } from 'payload/config' export const buildComponentMap = (args: { config: SanitizedConfig @@ -23,6 +24,7 @@ export const buildComponentMap = (args: { const { fields, slug } = collectionConfig const editViewFromConfig = collectionConfig?.admin?.components?.views?.Edit + const listViewFromConfig = collectionConfig?.admin?.components?.views?.List const CustomEditView = typeof editViewFromConfig === 'function' @@ -35,7 +37,16 @@ export const buildComponentMap = (args: { ? (editViewFromConfig.Default.Component as React.FC) : undefined + const CustomListView = + typeof listViewFromConfig === 'function' + ? listViewFromConfig + : typeof listViewFromConfig === 'object' && + typeof listViewFromConfig.Component === 'function' + ? listViewFromConfig.Component + : undefined + const Edit = CustomEditView || DefaultEditView + const List = CustomListView || DefaultList const beforeList = collectionConfig?.admin?.components?.BeforeList @@ -75,6 +86,7 @@ export const buildComponentMap = (args: { BeforeListTable, AfterListTable, Edit: , + List: , fieldMap: mappedFields, } diff --git a/packages/ui/src/utilities/buildComponentMap/types.ts b/packages/ui/src/utilities/buildComponentMap/types.ts index 225270c21e..779b2f03e3 100644 --- a/packages/ui/src/utilities/buildComponentMap/types.ts +++ b/packages/ui/src/utilities/buildComponentMap/types.ts @@ -66,6 +66,7 @@ export type CollectionComponentMap = ConfigComponentMapBase & { AfterList: React.ReactNode BeforeListTable: React.ReactNode AfterListTable: React.ReactNode + List: React.ReactNode } export type GlobalComponentMap = ConfigComponentMapBase diff --git a/packages/ui/src/views/Edit/Upload/index.tsx b/packages/ui/src/views/Edit/Upload/index.tsx index 7ac80cdbe5..f97dd4e397 100644 --- a/packages/ui/src/views/Edit/Upload/index.tsx +++ b/packages/ui/src/views/Edit/Upload/index.tsx @@ -128,7 +128,6 @@ export const Upload: React.FC = (props) => { return (
- {doc.filename && !replacingFile && ( = (props) => { imageCacheTag={lastSubmittedTime} /> )} - {(!doc.filename || replacingFile) && (
{!value && ( @@ -184,7 +182,6 @@ export const Upload: React.FC = (props) => { )}
)} - {(value || doc.filename) && ( { +export const DefaultEditView: React.FC = () => { const { action, BeforeDocument, @@ -44,6 +44,9 @@ export const DefaultEditView: React.FC = (props) => { id, hasSavePermission, disableActions, + collectionSlug, + globalSlug, + disableLeaveWithoutSaving, } = useDocumentInfo() const { user } = useAuth() @@ -60,11 +63,9 @@ export const DefaultEditView: React.FC = (props) => { const { getFieldMap } = useComponentMap() const collectionConfig = - 'collectionSlug' in props && - collections.find((collection) => collection.slug === props.collectionSlug) + collectionSlug && collections.find((collection) => collection.slug === collectionSlug) - const globalConfig = - 'globalSlug' in props && globals.find((global) => global.slug === props.globalSlug) + const globalConfig = globalSlug && globals.find((global) => global.slug === globalSlug) const [schemaPath] = React.useState(collectionConfig?.slug || globalConfig?.slug) @@ -81,7 +82,7 @@ export const DefaultEditView: React.FC = (props) => { const preventLeaveWithoutSaving = (!(collectionConfig?.versions?.drafts && collectionConfig?.versions?.drafts?.autosave) || !(globalConfig?.versions?.drafts && globalConfig?.versions?.drafts?.autosave)) && - !('disableLeaveWithoutSaving' in props && props.disableLeaveWithoutSaving) + !disableLeaveWithoutSaving const classes = [baseClass, id && `${baseClass}--is-editing`].filter(Boolean).join(' ') diff --git a/packages/ui/src/views/List/index.tsx b/packages/ui/src/views/List/index.tsx index 000e80e28e..1c5bbddaf9 100644 --- a/packages/ui/src/views/List/index.tsx +++ b/packages/ui/src/views/List/index.tsx @@ -1,7 +1,6 @@ 'use client' import React, { Fragment, useEffect } from 'react' -import type { DefaultListViewProps } from './types' import { SelectionProvider } from './SelectionProvider' import { Gutter } from '../../elements/Gutter' import { getTranslation } from '@payloadcms/translations' @@ -17,12 +16,13 @@ import { useComponentMap } from '../../providers/ComponentMapProvider' import { Table } from '../../elements/Table' import { ListControls } from '../../elements/ListControls' import { useStepNav } from '../../elements/StepNav' +import { useListInfo } from '../../providers/ListInfo' import './index.scss' const baseClass = 'collection-list' -export const DefaultList: React.FC = (props) => { +export const DefaultList: React.FC = () => { const { Header, data, @@ -35,10 +35,10 @@ export const DefaultList: React.FC = (props) => { limit, modifySearchParams, newDocumentURL, - resetParams, + // resetParams, titleField, collectionSlug, - } = props + } = useListInfo() const config = useConfig() diff --git a/packages/ui/src/views/List/types.ts b/packages/ui/src/views/List/types.ts index c52d9119f2..3ac8687622 100644 --- a/packages/ui/src/views/List/types.ts +++ b/packages/ui/src/views/List/types.ts @@ -1,37 +1,7 @@ import type { SanitizedCollectionConfig } from 'payload/types' -import type { PaginatedDocs } from 'payload/database' -import type { FieldAffectingData, Where } from 'payload/types' -import type { Props as ListControlsProps } from '../../elements/ListControls/types' -import type { Props as PaginatorProps } from '../../elements/Pagination/types' -import type { Props as PerPageProps } from '../../elements/PerPage' export type DefaultListViewProps = { - Header?: React.ReactNode - data: PaginatedDocs - handleDelete?: () => void - handlePageChange?: PaginatorProps['onChange'] - handlePerPageChange?: PerPageProps['handleChange'] - handleSearchChange?: ListControlsProps['handleSearchChange'] - handleSortChange?: ListControlsProps['handleSortChange'] - handleWhereChange?: ListControlsProps['handleWhereChange'] - hasCreatePermission: boolean - limit: number - modifySearchParams?: boolean - newDocumentURL: string - onCreateNewClick?: () => void - resetParams?: (overrides?: { - page?: number - search?: string - sort?: string - where?: Where - }) => void - setLimit?: (limit: number) => void - setListControls?: (controls: unknown) => void - setSort?: (sort: string) => void - titleField?: FieldAffectingData - toggleColumn?: (column: string) => void collectionSlug: SanitizedCollectionConfig['slug'] - useAsTitle?: SanitizedCollectionConfig['admin']['useAsTitle'] } export type ListIndexProps = {