chore(next): properly instantiates table columns

This commit is contained in:
Jacob Fletcher
2024-02-08 09:35:13 -05:00
parent 3a0ea03a9c
commit 7970955c00
9 changed files with 107 additions and 111 deletions

View File

@@ -23,13 +23,13 @@ export default buildConfig({
globals: [Settings],
editor: lexicalEditor({}),
onInit: async (payload) => {
await payload.create({
collection: 'users',
data: {
email: 'dev@payloadcms.com',
password: 'test',
},
})
// await payload.create({
// collection: 'users',
// data: {
// email: 'dev@payloadcms.com',
// password: 'test',
// },
// })
// const page = await payload.create({
// collection: 'pages',
// data: {

View File

@@ -9,6 +9,7 @@ import {
} from '@payloadcms/ui'
import { initPage } from '../../utilities/initPage'
import { notFound } from 'next/navigation'
import { ListPreferences } from '../../../../ui/src/views/List/types'
export const CollectionList = async ({
collectionSlug,
@@ -25,6 +26,23 @@ export const CollectionList = async ({
collectionSlug,
})
let listPreferences: ListPreferences
try {
listPreferences = (await payload
.find({
collection: 'payload-preferences',
where: {
key: {
equals: `${collectionSlug}-list`,
},
},
limit: 1,
depth: 0,
})
?.then((res) => res?.docs?.[0]?.value)) as unknown as ListPreferences
} catch (error) {}
const {
routes: { admin },
} = config
@@ -62,7 +80,7 @@ export const CollectionList = async ({
return (
<Fragment>
<HydrateClientUser user={user} permissions={permissions} />
<TableColumnsProvider collectionSlug={collectionSlug}>
<TableColumnsProvider collectionSlug={collectionSlug} listPreferences={listPreferences}>
<RenderCustomComponent
CustomComponent={ListToRender}
DefaultComponent={DefaultList}

View File

@@ -1,21 +1,41 @@
import { CellProps } from 'payload/types'
import { useComponentMap } from '../../providers/ComponentMapProvider'
import { CellProps, SanitizedCollectionConfig } from 'payload/types'
import { Column } from '../../elements/Table/types'
import { FieldMap } from '../../utilities/buildComponentMap/types'
import { ListPreferences } from '../../views/List/types'
export const buildColumns = (args: {
columns: Pick<Column, 'accessor' | 'active'>[]
getMappedFieldByPath: ReturnType<typeof useComponentMap>['getMappedFieldByPath']
collectionSlug: string
useAsTitle: SanitizedCollectionConfig['admin']['useAsTitle']
fieldMap: FieldMap
cellProps: Partial<CellProps>[]
defaultColumns?: string[]
columnPreferences: ListPreferences['columns']
}): Column[] => {
const { columns, getMappedFieldByPath, collectionSlug, cellProps } = args
const { fieldMap, cellProps, defaultColumns, columnPreferences, useAsTitle } = args
let numberOfActiveColumns = 0
return fieldMap.reduce((acc, field, index) => {
const columnPreference = columnPreferences?.find(
(preference) => preference.accessor === field.name,
)
let active = false
if (columnPreference) {
active = columnPreference.active
} else if (defaultColumns && Array.isArray(defaultColumns) && defaultColumns.length > 0) {
active = defaultColumns.includes(field.name)
} else if (numberOfActiveColumns < 4) {
active = true
}
if (active) {
numberOfActiveColumns += 1
}
return columns.map(({ accessor, active }, index) => {
const field = getMappedFieldByPath({ path: accessor, collectionSlug })
if (!field) console.warn(`No field found for ${accessor} in ${collectionSlug}`)
if (field) {
const column: Column = {
accessor,
accessor: field.name,
active,
label: field.label,
name: field.name,
@@ -26,7 +46,9 @@ export const buildColumns = (args: {
cellProps: cellProps?.[index],
}
return column
acc.push(column)
}
})
return acc
}, [])
}

View File

@@ -29,12 +29,13 @@ export const TableColumnsProvider: React.FC<{
cellProps?: Partial<CellProps>[]
children: React.ReactNode
collectionSlug: string
}> = ({ cellProps, children, collectionSlug }) => {
listPreferences: ListPreferences
}> = ({ cellProps, children, collectionSlug, listPreferences }) => {
const config = useConfig()
const { componentMap, getMappedFieldByPath } = useComponentMap()
const { componentMap } = useComponentMap()
const { initialColumns } = componentMap.collections[collectionSlug]
const { fieldMap } = componentMap.collections[collectionSlug]
const collectionConfig = config.collections.find(
(collectionConfig) => collectionConfig.slug === collectionSlug,
@@ -45,60 +46,69 @@ export const TableColumnsProvider: React.FC<{
} = collectionConfig
const preferenceKey = `${collectionSlug}-list`
const prevCollection = useRef<SanitizedCollectionConfig['slug']>()
const prevCollection = useRef<SanitizedCollectionConfig['slug']>(collectionSlug)
const hasInitialized = useRef(false)
const { getPreference, setPreference } = usePreferences()
const [tableColumns, dispatchTableColumns] = useReducer(columnReducer, {}, () => {
return buildColumns({
columns: initialColumns,
getMappedFieldByPath,
collectionSlug,
useAsTitle,
defaultColumns,
columnPreferences: listPreferences?.columns,
fieldMap,
cellProps,
})
})
// // /////////////////////////////////////
// // Sync preferences on collectionConfig change
// // /////////////////////////////////////
// /////////////////////////////////////
// Get preferences on collection change
// /////////////////////////////////////
useEffect(() => {
const sync = async () => {
const collectionHasChanged = prevCollection.current !== collectionSlug
if (collectionHasChanged) {
hasInitialized.current = false
const currentPreferences = await getPreference<ListPreferences>(preferenceKey)
prevCollection.current = collectionSlug
const newCols = currentPreferences?.columns || initialColumns
dispatchTableColumns({
payload: {
columns: buildColumns({
columns: newCols,
getMappedFieldByPath,
collectionSlug,
cellProps,
}),
},
type: 'set',
})
hasInitialized.current = true
if (currentPreferences.columns) {
dispatchTableColumns({
payload: {
columns: buildColumns({
useAsTitle,
columnPreferences: currentPreferences?.columns,
defaultColumns,
fieldMap,
cellProps,
}),
},
type: 'set',
})
}
}
}
sync()
}, [preferenceKey, getPreference, collectionSlug, initialColumns, cellProps])
}, [
preferenceKey,
getPreference,
collectionSlug,
fieldMap,
cellProps,
defaultColumns,
useAsTitle,
])
// // /////////////////////////////////////
// // Set preferences on column change
// // /////////////////////////////////////
// /////////////////////////////////////
// Set preferences on column change
// /////////////////////////////////////
useEffect(() => {
if (!hasInitialized.current) return
if (!hasInitialized.current) {
hasInitialized.current = true
return
}
const columns = tableColumns.map((c) => ({
accessor: c?.accessor,

View File

@@ -1,48 +0,0 @@
import type { Field } from 'payload/types'
import { fieldAffectsData, fieldHasSubFields, tabHasName } from 'payload/types'
import { Column } from '../../elements/Table/types'
export const getInitialColumns = (
fields: Field[],
useAsTitle: string,
defaultColumns: string[],
): Pick<Column, 'accessor' | 'active'>[] => {
return fields.reduce((remaining, field, index) => {
if (
fieldAffectsData(field) &&
(field.name === useAsTitle ||
(defaultColumns && defaultColumns.includes(field.name)) ||
index < 4)
) {
return [...remaining, { accessor: field.name, active: true }]
}
if (!fieldAffectsData(field) && fieldHasSubFields(field)) {
return [...remaining, ...getInitialColumns(field.fields, useAsTitle, defaultColumns)]
}
if (field.type === 'tabs') {
return [
...remaining,
...field.tabs.reduce(
(tabFieldColumns, tab) => [
...tabFieldColumns,
...(tabHasName(tab)
? [tab.name]
: getInitialColumns(tab.fields, useAsTitle, defaultColumns)),
],
[],
),
]
}
return [
...remaining,
{
accessor: field.name,
active: false,
},
]
}, [])
}

View File

@@ -3,7 +3,6 @@ import type { FieldPermissions } from 'payload/auth'
import type { SanitizedConfig } from 'payload/types'
import { mapFields } from './mapFields'
import { CollectionComponentMap, ComponentMap, GlobalComponentMap } from './types'
import { getInitialColumns } from './getInitialColumns'
export const buildComponentMap = (args: {
config: SanitizedConfig
@@ -56,15 +55,12 @@ export const buildComponentMap = (args: {
readOnly: readOnlyOverride,
})
const initialColumns = getInitialColumns(fields, useAsTitle, defaultColumns)
const componentMap: CollectionComponentMap = {
BeforeList,
AfterList,
BeforeListTable,
AfterListTable,
fieldMap: mappedFields,
initialColumns,
}
return {

View File

@@ -7,7 +7,6 @@ import {
TabsField,
} from 'payload/types'
import { fieldTypes } from '../../forms/fields'
import { Column } from '../../elements/Table/types'
export type MappedTab = {
name?: string
@@ -54,7 +53,6 @@ export type CollectionComponentMap = {
BeforeListTable: React.ReactNode
AfterListTable: React.ReactNode
fieldMap: FieldMap
initialColumns: Pick<Column, 'accessor' | 'active'>[]
}
export type GlobalComponentMap = {

View File

@@ -16,12 +16,12 @@ export const BlocksCell: React.FC<BlocksCellProps> = ({ cellData, blocks, labels
const selectedBlocks = cellData ? cellData.map(({ blockType }) => blockType) : []
const translatedBlockLabels = blocks.map((b) => ({
const translatedBlockLabels = blocks?.map((b) => ({
label: getTranslation(b.labels.singular, i18n),
slug: b.slug,
}))
let label = `0 ${getTranslation(labels.plural, i18n)}`
let label = `0 ${getTranslation(labels?.plural, i18n)}`
const formatBlockList = (blocks) =>
blocks
@@ -35,13 +35,13 @@ export const BlocksCell: React.FC<BlocksCellProps> = ({ cellData, blocks, labels
if (selectedBlocks.length > itemsToShow) {
const more = selectedBlocks.length - itemsToShow
label = `${selectedBlocks.length} ${getTranslation(labels.plural, i18n)} - ${i18n.t(
label = `${selectedBlocks.length} ${getTranslation(labels?.plural, i18n)} - ${i18n.t(
'fields:itemsAndMore',
{ count: more, items: formatBlockList(selectedBlocks.slice(0, itemsToShow)) },
)}`
} else if (selectedBlocks.length > 0) {
label = `${selectedBlocks.length} ${getTranslation(
selectedBlocks.length === 1 ? labels.singular : labels.plural,
selectedBlocks.length === 1 ? labels?.singular : labels?.plural,
i18n,
)} - ${formatBlockList(selectedBlocks)}`
}

View File

@@ -15,9 +15,9 @@ import { useConfig } from '../../providers/Config'
import { useTranslation } from '../../providers/Translation'
import { useComponentMap } from '../../providers/ComponentMapProvider'
import { Table } from '../../elements/Table'
import { ListControls } from '../../elements/ListControls'
import './index.scss'
import { ListControls } from '../../elements/ListControls'
const baseClass = 'collection-list'