Merge branch 'feat/doc-permissions' into alpha

This commit is contained in:
James
2024-03-14 13:03:13 -04:00
60 changed files with 563 additions and 529 deletions

View File

@@ -4,13 +4,12 @@ import { translations } from '@payloadcms/translations/client'
import { RootProvider, buildComponentMap } from '@payloadcms/ui'
import '@payloadcms/ui/scss/app.scss'
import { headers as getHeaders } from 'next/headers.js'
import { parseCookies } from 'payload/auth'
import { createClientConfig } from 'payload/config'
import { deepMerge } from 'payload/utilities'
import React from 'react'
import 'react-toastify/dist/ReactToastify.css'
import { auth } from '../../utilities/auth.js'
import { getPayloadHMR } from '../../utilities/getPayloadHMR.js'
import { getRequestLanguage } from '../../utilities/getRequestLanguage.js'
import { DefaultEditView } from '../../views/Edit/Default/index.js'
import { DefaultCell } from '../../views/List/Default/Cell/index.js'
@@ -34,13 +33,7 @@ export const RootLayout = async ({
const clientConfig = await createClientConfig(config)
const headers = getHeaders()
const payload = await getPayloadHMR({ config: configPromise })
const { cookies, permissions } = await auth({
headers,
payload,
})
const cookies = parseCookies(headers)
const lang =
getRequestLanguage({
@@ -63,7 +56,6 @@ export const RootLayout = async ({
DefaultListView,
children,
config,
permissions,
})
return (

View File

@@ -16,6 +16,9 @@ export const CreateFirstUserFields: React.FC<{
<RenderFields
fieldMap={[...(fieldMap || []), ...(createFirstUserFieldMap || [])]}
operation="create"
path=""
readOnly={false}
schemaPath={userSlug}
/>
)
}

View File

@@ -4,7 +4,6 @@ import type { FormProps } from '@payloadcms/ui'
import {
DocumentControls,
DocumentFields,
FieldPathProvider,
Form,
FormLoadingOverlayToggle,
OperationProvider,
@@ -140,89 +139,90 @@ export const DefaultEditView: React.FC = () => {
return (
<main className={classes}>
<FieldPathProvider path="" schemaPath={entitySlug}>
<OperationProvider operation={operation}>
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={initialState}
method={id ? 'PATCH' : 'POST'}
onChange={[onChange]}
onSuccess={onSave}
>
<FormLoadingOverlayToggle
action={operation}
// formIsLoading={isLoading}
// loadingSuffix={getTranslation(collectionConfig.labels.singular, i18n)}
name={`collection-edit--${
typeof collectionConfig?.labels?.singular === 'string'
? collectionConfig.labels.singular
: 'document'
}`}
type="withoutNav"
/>
{BeforeDocument}
{preventLeaveWithoutSaving && <LeaveWithoutSaving />}
<SetStepNav
collectionSlug={collectionConfig?.slug}
globalSlug={globalConfig?.slug}
id={id}
pluralLabel={collectionConfig?.labels?.plural}
useAsTitle={collectionConfig?.admin?.useAsTitle}
/>
<SetDocumentTitle
collectionConfig={collectionConfig}
config={config}
fallback={depth <= 1 ? id?.toString() : undefined}
globalConfig={globalConfig}
/>
<DocumentControls
apiURL={apiURL}
data={data}
disableActions={disableActions}
hasSavePermission={hasSavePermission}
id={id}
isEditing={Boolean(id)}
permissions={docPermissions}
slug={collectionConfig?.slug}
/>
<DocumentFields
AfterFields={AfterFields}
BeforeFields={
BeforeFields || (
<Fragment>
{auth && (
<Auth
className={`${baseClass}__auth`}
<OperationProvider operation={operation}>
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={initialState}
method={id ? 'PATCH' : 'POST'}
onChange={[onChange]}
onSuccess={onSave}
>
<FormLoadingOverlayToggle
action={operation}
// formIsLoading={isLoading}
// loadingSuffix={getTranslation(collectionConfig.labels.singular, i18n)}
name={`collection-edit--${
typeof collectionConfig?.labels?.singular === 'string'
? collectionConfig.labels.singular
: 'document'
}`}
type="withoutNav"
/>
{BeforeDocument}
{preventLeaveWithoutSaving && <LeaveWithoutSaving />}
<SetStepNav
collectionSlug={collectionConfig?.slug}
globalSlug={globalConfig?.slug}
id={id}
pluralLabel={collectionConfig?.labels?.plural}
useAsTitle={collectionConfig?.admin?.useAsTitle}
/>
<SetDocumentTitle
collectionConfig={collectionConfig}
config={config}
fallback={depth <= 1 ? id?.toString() : undefined}
globalConfig={globalConfig}
/>
<DocumentControls
apiURL={apiURL}
data={data}
disableActions={disableActions}
hasSavePermission={hasSavePermission}
id={id}
isEditing={Boolean(id)}
permissions={docPermissions}
slug={collectionConfig?.slug}
/>
<DocumentFields
AfterFields={AfterFields}
BeforeFields={
BeforeFields || (
<Fragment>
{auth && (
<Auth
className={`${baseClass}__auth`}
collectionSlug={collectionConfig.slug}
email={data?.email}
operation={operation}
readOnly={!hasSavePermission}
requirePassword={!id}
useAPIKey={auth.useAPIKey}
verify={auth.verify}
/>
)}
{upload && (
<React.Fragment>
{RegisterGetThumbnailFunction && <RegisterGetThumbnailFunction />}
<Upload
collectionSlug={collectionConfig.slug}
email={data?.email}
operation={operation}
readOnly={!hasSavePermission}
requirePassword={!id}
useAPIKey={auth.useAPIKey}
verify={auth.verify}
initialState={initialState}
uploadConfig={upload}
/>
)}
{upload && (
<React.Fragment>
{RegisterGetThumbnailFunction && <RegisterGetThumbnailFunction />}
<Upload
collectionSlug={collectionConfig.slug}
initialState={initialState}
uploadConfig={upload}
/>
</React.Fragment>
)}
</Fragment>
)
}
fieldMap={fieldMap}
/>
{AfterDocument}
</Form>
</OperationProvider>
</FieldPathProvider>
</React.Fragment>
)}
</Fragment>
)
}
docPermissions={docPermissions}
fieldMap={fieldMap}
readOnly={!hasSavePermission}
schemaPath={entitySlug}
/>
{AfterDocument}
</Form>
</OperationProvider>
</main>
)
}

View File

@@ -6,7 +6,6 @@ import type { Data } from 'payload/types'
import {
DocumentControls,
DocumentFields,
FieldPathProvider,
Form,
LoadingOverlay,
OperationProvider,
@@ -127,73 +126,72 @@ const PreviewView: React.FC = (props) => {
return (
<Fragment>
<FieldPathProvider path="" schemaPath={schemaPath}>
<OperationProvider operation={operation}>
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={initialState}
method={id ? 'PATCH' : 'POST'}
onChange={[onChange]}
onSuccess={onSave}
<OperationProvider operation={operation}>
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={initialState}
method={id ? 'PATCH' : 'POST'}
onChange={[onChange]}
onSuccess={onSave}
>
{((collectionConfig &&
!(collectionConfig.versions?.drafts && collectionConfig.versions?.drafts?.autosave)) ||
(global && !(global.versions?.drafts && global.versions?.drafts?.autosave))) &&
!disableLeaveWithoutSaving && <LeaveWithoutSaving />}
<SetStepNav
collectionSlug={collectionSlug}
globalLabel={globalConfig?.label}
globalSlug={globalSlug}
id={id}
view={t('general:livePreview')}
/>
<SetDocumentTitle
collectionConfig={collectionConfig}
config={config}
fallback={id?.toString() || ''}
globalConfig={globalConfig}
/>
<DocumentControls
apiURL={apiURL}
data={data}
disableActions={disableActions}
hasSavePermission={hasSavePermission}
id={id}
isEditing={Boolean(id)}
permissions={docPermissions}
slug={collectionConfig?.slug}
/>
<div
className={[baseClass, previewWindowType === 'popup' && `${baseClass}--detached`]
.filter(Boolean)
.join(' ')}
>
{((collectionConfig &&
!(
collectionConfig.versions?.drafts && collectionConfig.versions?.drafts?.autosave
)) ||
(global && !(global.versions?.drafts && global.versions?.drafts?.autosave))) &&
!disableLeaveWithoutSaving && <LeaveWithoutSaving />}
<SetStepNav
collectionSlug={collectionSlug}
globalLabel={globalConfig?.label}
globalSlug={globalSlug}
id={id}
view={t('general:livePreview')}
/>
<SetDocumentTitle
collectionConfig={collectionConfig}
config={config}
fallback={id?.toString() || ''}
globalConfig={globalConfig}
/>
<DocumentControls
apiURL={apiURL}
data={data}
disableActions={disableActions}
hasSavePermission={hasSavePermission}
id={id}
isEditing={Boolean(id)}
permissions={docPermissions}
slug={collectionConfig?.slug}
/>
<div
className={[baseClass, previewWindowType === 'popup' && `${baseClass}--detached`]
className={[
`${baseClass}__main`,
previewWindowType === 'popup' && `${baseClass}__main--popup-open`,
]
.filter(Boolean)
.join(' ')}
>
<div
className={[
`${baseClass}__main`,
previewWindowType === 'popup' && `${baseClass}__main--popup-open`,
]
.filter(Boolean)
.join(' ')}
>
{BeforeDocument}
<DocumentFields
AfterFields={AfterFields}
BeforeFields={BeforeFields}
fieldMap={fieldMap}
forceSidebarWrap
/>
{AfterDocument}
</div>
<LivePreview {...props} />
{BeforeDocument}
<DocumentFields
AfterFields={AfterFields}
BeforeFields={BeforeFields}
docPermissions={docPermissions}
fieldMap={fieldMap}
forceSidebarWrap
readOnly={!hasSavePermission}
schemaPath={collectionSlug}
/>
{AfterDocument}
</div>
</Form>
</OperationProvider>
</FieldPathProvider>
<LivePreview {...props} />
</div>
</Form>
</OperationProvider>
</Fragment>
)
}

View File

@@ -2,7 +2,7 @@ export * from './../types/index.js'
export type * from '../admin/types.js'
export type * from '../uploads/types.js'
export type { DocumentPermissions } from '../auth/index.js'
export type { DocumentPermissions, FieldPermissions } from '../auth/index.js'
export type {
AfterChangeHook as CollectionAfterChangeHook,

View File

@@ -65,7 +65,7 @@ export const BlockContent: React.FC<Props> = (props) => {
const [collapsed, setCollapsed] = React.useState<boolean>(() => {
let initialState = false
getDocPreferences().then((currentDocPreferences) => {
void getDocPreferences().then((currentDocPreferences) => {
const currentFieldPreferences = currentDocPreferences?.fields[field.name]
const collapsedMap: { [key: string]: boolean } = currentFieldPreferences?.collapsed
@@ -154,7 +154,7 @@ export const BlockContent: React.FC<Props> = (props) => {
)
const onCollapsedChange = useCallback(() => {
getDocPreferences().then((currentDocPreferences) => {
void getDocPreferences().then((currentDocPreferences) => {
const currentFieldPreferences = currentDocPreferences?.fields[field.name]
const collapsedMap: { [key: string]: boolean } = currentFieldPreferences?.collapsed
@@ -223,6 +223,9 @@ export const BlockContent: React.FC<Props> = (props) => {
fieldMap={Array.isArray(formSchema) ? formSchema : []}
forceRender
margins="small"
path=""
readOnly={false}
schemaPath=""
/>
</Collapsible>

View File

@@ -1,6 +1,5 @@
'use client'
import {
FieldPathProvider,
Form,
type FormProps,
getFormState,
@@ -124,25 +123,23 @@ export const BlockComponent: React.FC<Props> = (props) => {
return (
reducedBlock &&
initialState !== false && (
<FieldPathProvider path="" schemaPath="">
<Form
// @ts-expect-error // TODO: Fix this type. Is this correct?
fields={fieldMap}
initialState={initialState}
onChange={[onChange]}
submitted={submitted}
uuid={uuid()}
>
<BlockContent
baseClass={baseClass}
field={parentLexicalRichTextField}
formData={formData}
formSchema={Array.isArray(fieldMap) ? fieldMap : []}
nodeKey={nodeKey}
reducedBlock={reducedBlock}
/>
</Form>
</FieldPathProvider>
<Form
// @ts-expect-error // TODO: Fix this type. Is this correct?
fields={fieldMap}
initialState={initialState}
onChange={[onChange]}
submitted={submitted}
uuid={uuid()}
>
<BlockContent
baseClass={baseClass}
field={parentLexicalRichTextField}
formData={formData}
formSchema={Array.isArray(fieldMap) ? fieldMap : []}
nodeKey={nodeKey}
reducedBlock={reducedBlock}
/>
</Form>
)
)
}, [

View File

@@ -78,20 +78,24 @@ export const LinkDrawer: React.FC<Props> = ({ drawerSlug, handleModalSubmit, sta
return (
<Drawer className={baseClass} slug={drawerSlug} title={t('fields:editLink') ?? ''}>
{initialState !== false && (
<FieldPathProvider path="" schemaPath="">
<Form
// @ts-expect-error // TODO: Fix this type. Is this correct?
fields={Array.isArray(fieldMap) ? fieldMap : []}
initialState={initialState}
onChange={[onChange]}
onSubmit={handleModalSubmit}
uuid={uuid()}
>
<RenderFields fieldMap={Array.isArray(fieldMap) ? fieldMap : []} forceRender />
<Form
// @ts-expect-error // TODO: Fix this type. Is this correct?
fields={Array.isArray(fieldMap) ? fieldMap : []}
initialState={initialState}
onChange={[onChange]}
onSubmit={handleModalSubmit}
uuid={uuid()}
>
<RenderFields
fieldMap={Array.isArray(fieldMap) ? fieldMap : []}
forceRender
path=""
readOnly={false}
schemaPath=""
/>
<FormSubmit>{t('general:submit')}</FormSubmit>
</Form>
</FieldPathProvider>
<FormSubmit>{t('general:submit')}</FormSubmit>
</Form>
)}
</Drawer>
)

View File

@@ -130,19 +130,23 @@ export const ExtraFieldsUploadDrawer: React.FC<
})}
>
{initialState !== false && (
<FieldPathProvider path="" schemaPath="">
<Form
// @ts-expect-error // TODO: Fix this type. Is this correct?
fields={fieldMap}
initialState={initialState}
onChange={[onChange]}
onSubmit={handleUpdateEditData}
uuid={uuid()}
>
<RenderFields fieldMap={Array.isArray(fieldMap) ? fieldMap : []} forceRender />
<FormSubmit>{t('fields:saveChanges')}</FormSubmit>
</Form>
</FieldPathProvider>
<Form
// @ts-expect-error // TODO: Fix this type. Is this correct?
fields={fieldMap}
initialState={initialState}
onChange={[onChange]}
onSubmit={handleUpdateEditData}
uuid={uuid()}
>
<RenderFields
fieldMap={Array.isArray(fieldMap) ? fieldMap : []}
forceRender
path=""
readOnly={false}
schemaPath=""
/>
<FormSubmit>{t('fields:saveChanges')}</FormSubmit>
</Form>
)}
</Drawer>
)

View File

@@ -82,7 +82,6 @@ export const getGenerateComponentMap =
const mappedFields = mapFields({
config,
fieldSchema: sanitizedFields,
permissions: {},
readOnly: false,
})

View File

@@ -54,14 +54,12 @@ export const LinkDrawer: React.FC<Props> = ({
)
return (
<FieldPathProvider path="" schemaPath="">
<Drawer className={baseClass} slug={drawerSlug} title={t('fields:editLink')}>
<Form initialState={initialState} onChange={[onChange]} onSubmit={handleModalSubmit}>
<RenderFields fieldMap={fieldMap} forceRender />
<LinkSubmit />
</Form>
</Drawer>
</FieldPathProvider>
<Drawer className={baseClass} slug={drawerSlug} title={t('fields:editLink')}>
<Form initialState={initialState} onChange={[onChange]} onSubmit={handleModalSubmit}>
<RenderFields fieldMap={fieldMap} forceRender path="" readOnly={false} schemaPath="" />
<LinkSubmit />
</Form>
</Drawer>
)
}

View File

@@ -109,12 +109,15 @@ export const UploadDrawer: React.FC<{
label: getTranslation(relatedCollection.labels.singular, i18n),
})}
>
<FieldPathProvider path="" schemaPath="">
<Form initialState={initialState} onSubmit={handleUpdateEditData}>
<RenderFields fieldMap={Array.isArray(fieldMap) ? fieldMap : []} />
<FormSubmit>{t('fields:saveChanges')}</FormSubmit>
</Form>
</FieldPathProvider>
<Form initialState={initialState} onSubmit={handleUpdateEditData}>
<RenderFields
fieldMap={Array.isArray(fieldMap) ? fieldMap : []}
path=""
readOnly={false}
schemaPath=""
/>
<FormSubmit>{t('fields:saveChanges')}</FormSubmit>
</Form>
</Drawer>
)
}

View File

@@ -78,7 +78,6 @@ export const getGenerateComponentMap =
const mappedFields = mapFields({
config,
fieldSchema: linkFields,
permissions: {},
readOnly: false,
})
@@ -109,7 +108,6 @@ export const getGenerateComponentMap =
const mappedFields = mapFields({
config,
fieldSchema: uploadFields,
permissions: {},
readOnly: false,
})

View File

@@ -9,7 +9,7 @@ import { toast } from 'react-toastify'
import type { DocumentDrawerProps } from './types.js'
import { FieldPathProvider, useFieldPath } from '../../forms/FieldPathProvider/index.js'
import { useFieldProps } from '../../forms/FieldPropsProvider/index.js'
import { useRelatedCollections } from '../../forms/fields/Relationship/AddNew/useRelatedCollections.js'
import usePayloadAPI from '../../hooks/usePayloadAPI.js'
import { X } from '../../icons/X/index.js'
@@ -56,7 +56,7 @@ const Content: React.FC<DocumentDrawerProps> = ({
const { permissions } = useAuth()
const { schemaPath } = useFieldPath()
const { schemaPath } = useFieldProps()
const { componentMap } = useComponentMap()
@@ -197,9 +197,5 @@ export const DocumentDrawerContent: React.FC<DocumentDrawerProps> = (props) => {
[onSaveFromProps, collectionConfig],
)
return (
<FieldPathProvider path="" schemaPath={collectionSlug}>
<Content {...props} id={idFromProps || doc?.id} initialData={doc} onSave={onSave} />
</FieldPathProvider>
)
return <Content {...props} id={idFromProps || doc?.id} initialData={doc} onSave={onSave} />
}

View File

@@ -1,25 +1,37 @@
'use client'
import type { Description, Operation } from 'payload/types'
import type { Description, DocumentPermissions } from 'payload/types'
import React from 'react'
import type { FieldMap } from '../../utilities/buildComponentMap/types.js'
import RenderFields from '../../forms/RenderFields/index.js'
import { RenderFields } from '../../forms/RenderFields/index.js'
import { Gutter } from '../Gutter/index.js'
import './index.scss'
const baseClass = 'document-fields'
export const DocumentFields: React.FC<{
type Args = {
AfterFields?: React.ReactNode
BeforeFields?: React.ReactNode
description?: Description
docPermissions: DocumentPermissions
fieldMap: FieldMap
forceSidebarWrap?: boolean
}> = (props) => {
const { AfterFields, BeforeFields, description, fieldMap, forceSidebarWrap } = props
readOnly: boolean
schemaPath: string
}
export const DocumentFields: React.FC<Args> = ({
AfterFields,
BeforeFields,
description,
docPermissions,
fieldMap,
forceSidebarWrap,
readOnly,
schemaPath,
}) => {
const mainFields = fieldMap.filter(({ isSidebar }) => !isSidebar)
const sidebarFields = fieldMap.filter(({ isSidebar }) => isSidebar)
@@ -47,7 +59,14 @@ export const DocumentFields: React.FC<{
)}
</header>
{BeforeFields}
<RenderFields className={`${baseClass}__fields`} fieldMap={mainFields} />
<RenderFields
className={`${baseClass}__fields`}
fieldMap={mainFields}
path=""
permissions={docPermissions?.['fields']}
readOnly={readOnly}
schemaPath={schemaPath}
/>
{AfterFields}
</Gutter>
</div>
@@ -55,7 +74,13 @@ export const DocumentFields: React.FC<{
<div className={`${baseClass}__sidebar-wrap`}>
<div className={`${baseClass}__sidebar`}>
<div className={`${baseClass}__sidebar-fields`}>
<RenderFields fieldMap={sidebarFields} />
<RenderFields
fieldMap={sidebarFields}
path=""
permissions={docPermissions?.fields}
readOnly={readOnly}
schemaPath={schemaPath}
/>
</div>
</div>
</div>

View File

@@ -8,10 +8,9 @@ import React, { useCallback, useState } from 'react'
import type { Props } from './types.js'
import { FieldPathProvider } from '../../forms/FieldPathProvider/index.js'
import { useForm } from '../../forms/Form/context.js'
import Form from '../../forms/Form/index.js'
import RenderFields from '../../forms/RenderFields/index.js'
import { RenderFields } from '../../forms/RenderFields/index.js'
import FormSubmit from '../../forms/Submit/index.js'
import { X } from '../../icons/X/index.js'
import { useAuth } from '../../providers/Auth/index.js'
@@ -34,7 +33,7 @@ const Submit: React.FC<{ action: string; disabled: boolean }> = ({ action, disab
const { t } = useTranslation()
const save = useCallback(() => {
submit({
void submit({
action,
method: 'PATCH',
skipValidation: true,
@@ -52,7 +51,7 @@ const Publish: React.FC<{ action: string; disabled: boolean }> = ({ action, disa
const { t } = useTranslation()
const save = useCallback(() => {
submit({
void submit({
action,
method: 'PATCH',
overrides: {
@@ -73,7 +72,7 @@ const SaveDraft: React.FC<{ action: string; disabled: boolean }> = ({ action, di
const { t } = useTranslation()
const save = useCallback(() => {
submit({
void submit({
action,
method: 'PATCH',
overrides: {
@@ -90,7 +89,7 @@ const SaveDraft: React.FC<{ action: string; disabled: boolean }> = ({ action, di
)
}
export const EditMany: React.FC<Props> = (props) => {
const { Modal, useModal } = facelessUIImport
const { useModal } = facelessUIImport
const { collection: { slug, fields, labels: { plural } } = {}, collection } = props
@@ -195,43 +194,46 @@ export const EditMany: React.FC<Props> = (props) => {
<X />
</button>
</div>
<FieldPathProvider path="" schemaPath={slug}>
<Form
className={`${baseClass}__form`}
initialState={initialState}
onSuccess={onSuccess}
>
<FieldSelect fields={fields} setSelected={setSelected} />
{reducedFieldMap.length === 0 ? null : (
<RenderFields fieldMap={reducedFieldMap} />
)}
<div className={`${baseClass}__sidebar-wrap`}>
<div className={`${baseClass}__sidebar`}>
<div className={`${baseClass}__sidebar-sticky-wrap`}>
<div className={`${baseClass}__document-actions`}>
{collection?.versions?.drafts ? (
<React.Fragment>
<Publish
action={`${serverURL}${apiRoute}/${slug}${getQueryParams()}`}
disabled={selected.length === 0}
/>
<SaveDraft
action={`${serverURL}${apiRoute}/${slug}${getQueryParams()}`}
disabled={selected.length === 0}
/>
</React.Fragment>
) : (
<Submit
<Form
className={`${baseClass}__form`}
initialState={initialState}
onSuccess={onSuccess}
>
<FieldSelect fields={fields} setSelected={setSelected} />
{reducedFieldMap.length === 0 ? null : (
<RenderFields
fieldMap={reducedFieldMap}
path=""
readOnly={false}
schemaPath={slug}
/>
)}
<div className={`${baseClass}__sidebar-wrap`}>
<div className={`${baseClass}__sidebar`}>
<div className={`${baseClass}__sidebar-sticky-wrap`}>
<div className={`${baseClass}__document-actions`}>
{collection?.versions?.drafts ? (
<React.Fragment>
<Publish
action={`${serverURL}${apiRoute}/${slug}${getQueryParams()}`}
disabled={selected.length === 0}
/>
)}
</div>
<SaveDraft
action={`${serverURL}${apiRoute}/${slug}${getQueryParams()}`}
disabled={selected.length === 0}
/>
</React.Fragment>
) : (
<Submit
action={`${serverURL}${apiRoute}/${slug}${getQueryParams()}`}
disabled={selected.length === 0}
/>
)}
</div>
</div>
</div>
</Form>
</FieldPathProvider>
</div>
</Form>
</div>
</OperationContext.Provider>
</DocumentInfoProvider>

View File

@@ -8,7 +8,7 @@ import Error from '../../forms/Error/index.js'
import { useFormSubmitted } from '../../forms/Form/context.js'
import reduceFieldsToValues from '../../forms/Form/reduceFieldsToValues.js'
import { fieldBaseClass } from '../../forms/fields/shared.js'
import useField from '../../forms/useField/index.js'
import { useField } from '../../forms/useField/index.js'
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { Button } from '../Button/index.js'

View File

@@ -1,6 +1,9 @@
export { default as Error } from '../forms/Error/index.js'
export { default as FieldDescription } from '../forms/FieldDescription/index.js'
export { FieldPathProvider, useFieldPath } from '../forms/FieldPathProvider/index.js'
export {
FieldPropsProvider as FieldPathProvider,
useFieldProps as useFieldPath,
} from '../forms/FieldPropsProvider/index.js'
export {
useAllFormFields,
useForm,
@@ -14,7 +17,7 @@ export { default as Form } from '../forms/Form/index.js'
export { default as reduceFieldsToValues } from '../forms/Form/reduceFieldsToValues.js'
export type { Props as FormProps } from '../forms/Form/types.js'
export { default as Label } from '../forms/Label/index.js'
export { default as RenderFields } from '../forms/RenderFields/index.js'
export { RenderFields } from '../forms/RenderFields/index.js'
export { default as FormSubmit } from '../forms/Submit/index.js'
export { default as Submit } from '../forms/Submit/index.js'
export { default as SectionTitle } from '../forms/fields/Blocks/SectionTitle/index.js'
@@ -40,7 +43,7 @@ export { default as UploadField } from '../forms/fields/Upload/index.js'
export { fieldTypes } from '../forms/fields/index.js'
export { fieldBaseClass } from '../forms/fields/shared.js'
export { default as useField } from '../forms/useField/index.js'
export { useField } from '../forms/useField/index.js'
export type { FieldType, Options } from '../forms/useField/types.js'
export { default as buildStateFromSchema } from '../forms/utilities/buildStateFromSchema/index.js'
export type { BuildFormStateArgs } from '../forms/utilities/buildStateFromSchema/index.js'

View File

@@ -4,7 +4,7 @@ import type { ErrorProps } from 'payload/types'
import React from 'react'
import { Tooltip } from '../../elements/Tooltip/index.js'
import { useFieldPath } from '../FieldPathProvider/index.js'
import { useFieldProps } from '../FieldPropsProvider/index.js'
import { useFormFields, useFormSubmitted } from '../Form/context.js'
import './index.scss'
@@ -18,7 +18,7 @@ const Error: React.FC<ErrorProps> = (props) => {
showError: showErrorFromProps,
} = props
const { path: pathFromContext } = useFieldPath()
const { path: pathFromContext } = useFieldProps()
const path = pathFromProps || pathFromContext
const hasSubmitted = useFormSubmitted()

View File

@@ -1,36 +0,0 @@
'use client'
import React from 'react'
type FieldPathContextType = {
path: string
schemaPath: string
}
const FieldPathContext = React.createContext<FieldPathContextType>({
path: '',
schemaPath: '',
})
export const FieldPathProvider: React.FC<{
children: React.ReactNode
path: string
schemaPath: string
}> = (props) => {
const { children, path, schemaPath } = props
return (
<FieldPathContext.Provider
value={{
path,
schemaPath,
}}
>
{children}
</FieldPathContext.Provider>
)
}
export const useFieldPath = () => {
const path = React.useContext(FieldPathContext)
return path
}

View File

@@ -0,0 +1,61 @@
'use client'
import type { FieldPermissions } from 'payload/types'
import React from 'react'
type FieldPropsContextType = {
path: string
permissions?: FieldPermissions
readOnly: boolean
schemaPath: string
siblingPermissions: {
[fieldName: string]: FieldPermissions
}
}
const FieldPropsContext = React.createContext<FieldPropsContextType>({
path: '',
permissions: {} as FieldPermissions,
readOnly: false,
schemaPath: '',
siblingPermissions: {},
})
type Props = {
children: React.ReactNode
path: string
permissions?: FieldPermissions
readOnly: boolean
schemaPath: string
siblingPermissions: {
[fieldName: string]: FieldPermissions
}
}
export const FieldPropsProvider: React.FC<Props> = ({
children,
path,
permissions,
readOnly,
schemaPath,
siblingPermissions,
}) => {
return (
<FieldPropsContext.Provider
value={{
path,
permissions,
readOnly,
schemaPath,
siblingPermissions,
}}
>
{children}
</FieldPropsContext.Provider>
)
}
export const useFieldProps = () => {
const path = React.useContext(FieldPropsContext)
return path
}

View File

@@ -6,14 +6,14 @@ import React from 'react'
import { useTranslation } from '../../providers/Translation/index.js'
import { generateFieldID } from '../../utilities/generateFieldID.js'
import { useFieldPath } from '../FieldPathProvider/index.js'
import { useFieldProps } from '../FieldPropsProvider/index.js'
import { useForm } from '../Form/context.js'
import './index.scss'
const Label: React.FC<LabelProps> = (props) => {
const { htmlFor: htmlForFromProps, label, required = false } = props
const { uuid } = useForm()
const { path } = useFieldPath()
const { path } = useFieldProps()
const htmlFor = htmlForFromProps || generateFieldID(path, uuid)
const { i18n } = useTranslation()

View File

@@ -1,18 +0,0 @@
'use client'
import React from 'react'
const ReadOnlyContext = React.createContext<boolean | undefined>(undefined)
export const ReadOnlyProvider: React.FC<{
children: React.ReactNode
readOnly?: boolean
}> = (props) => {
const { children, readOnly } = props
return <ReadOnlyContext.Provider value={readOnly}>{children}</ReadOnlyContext.Provider>
}
export const useReadOnly = () => {
const path = React.useContext(ReadOnlyContext)
return path
}

View File

@@ -1,32 +1,44 @@
'use client'
import type { FieldPermissions } from 'payload/auth'
import type { FieldPermissions } from 'payload/types'
import React from 'react'
import { useOperation } from '../../providers/OperationProvider/index.js'
import { FieldPathProvider, useFieldPath } from '../FieldPathProvider/index.js'
import { ReadOnlyProvider, useReadOnly } from '../ReadOnlyProvider/index.js'
import { FieldPropsProvider, useFieldProps } from '../FieldPropsProvider/index.js'
export const RenderField: React.FC<{
type Props = {
Field: React.ReactNode
fieldPermissions: FieldPermissions
disabled: boolean
name?: string
path: string
permissions?: FieldPermissions
readOnly?: boolean
}> = (props) => {
const { name, Field, fieldPermissions, readOnly: readOnlyFromProps } = props
const { path: pathFromContext, schemaPath: schemaPathFromContext } = useFieldPath()
const readOnlyFromContext = useReadOnly()
schemaPath: string
siblingPermissions: {
[fieldName: string]: FieldPermissions
}
}
export const RenderField: React.FC<Props> = ({
name,
Field,
disabled,
path: pathFromProps,
permissions,
readOnly: readOnlyFromProps,
schemaPath: schemaPathFromProps,
siblingPermissions,
}) => {
const operation = useOperation()
const { readOnly: readOnlyFromContext } = useFieldProps()
const path = `${pathFromContext ? `${pathFromContext}.` : ''}${name ? `${name}` : ''}`
const schemaPath = `${schemaPathFromContext ? `${schemaPathFromContext}` : ''}${name ? `.${name}` : ''}`
const path = `${pathFromProps ? `${pathFromProps}.` : ''}${name ? `${name}` : ''}`
const schemaPath = `${schemaPathFromProps ? `${schemaPathFromProps}` : ''}${name ? `.${name}` : ''}`
// if the user cannot read the field, then filter it out
// this is different from `admin.readOnly` which is executed based on `operation`
if (fieldPermissions?.read?.permission === false) {
if (permissions?.read?.permission === false || disabled) {
return null
}
@@ -37,15 +49,19 @@ export const RenderField: React.FC<{
if (readOnlyFromContext && readOnly !== false) readOnly = true
// if the user does not have access control to begin with, force it to be read-only
if (fieldPermissions?.[operation]?.permission === false) {
if (permissions?.[operation]?.permission === false) {
readOnly = true
}
return (
<ReadOnlyProvider readOnly={readOnly}>
<FieldPathProvider path={path} schemaPath={schemaPath}>
{Field}
</FieldPathProvider>
</ReadOnlyProvider>
<FieldPropsProvider
path={path}
permissions={permissions}
readOnly={readOnly}
schemaPath={schemaPath}
siblingPermissions={siblingPermissions}
>
{Field}
</FieldPropsProvider>
)
}

View File

@@ -10,8 +10,8 @@ import './index.scss'
const baseClass = 'render-fields'
const RenderFields: React.FC<Props> = (props) => {
const { className, fieldMap, forceRender, margins } = props
export const RenderFields: React.FC<Props> = (props) => {
const { className, fieldMap, forceRender, margins, path, permissions, schemaPath } = props
const { i18n } = useTranslation()
const [hasRendered, setHasRendered] = React.useState(Boolean(forceRender))
@@ -53,20 +53,24 @@ const RenderFields: React.FC<Props> = (props) => {
ref={intersectionRef}
>
{hasRendered &&
fieldMap?.map(({ name, Field, fieldPermissions, readOnly }, fieldIndex) => (
<RenderField
Field={Field}
fieldPermissions={fieldPermissions}
key={fieldIndex}
name={name}
readOnly={readOnly}
/>
))}
fieldMap?.map(({ name, Field, disabled, readOnly }, fieldIndex) => {
return (
<RenderField
Field={Field}
disabled={disabled}
key={fieldIndex}
name={name}
path={path}
permissions={permissions?.[name]}
readOnly={readOnly}
schemaPath={schemaPath}
siblingPermissions={permissions}
/>
)
})}
</div>
)
}
return null
}
export default RenderFields

View File

@@ -1,4 +1,4 @@
import type { Operation } from 'payload/types'
import type { FieldPermissions, Operation } from 'payload/types'
import type { FieldMap } from '../../utilities/buildComponentMap/types.js'
@@ -8,4 +8,10 @@ export type Props = {
forceRender?: boolean
margins?: 'small' | false
operation?: Operation
path: string
permissions?: {
[fieldName: string]: FieldPermissions
}
readOnly: boolean
schemaPath: string
}

View File

@@ -1,5 +1,5 @@
import type { FieldPermissions } from 'payload/auth'
import type { ArrayField, Operation, Row, RowLabel as RowLabelType } from 'payload/types'
import type { ArrayField, Row, RowLabel as RowLabelType } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import React from 'react'
@@ -11,9 +11,9 @@ import { ArrayAction } from '../../../elements/ArrayAction/index.js'
import { Collapsible } from '../../../elements/Collapsible/index.js'
import { ErrorPill } from '../../../elements/ErrorPill/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import { FieldPathProvider } from '../../FieldPathProvider/index.js'
import { FieldPropsProvider } from '../../FieldPropsProvider/index.js'
import { useFormSubmitted } from '../../Form/context.js'
import RenderFields from '../../RenderFields/index.js'
import { RenderFields } from '../../RenderFields/index.js'
import HiddenInput from '../HiddenInput/index.js'
import './index.scss'
@@ -50,6 +50,7 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
listeners,
moveRow,
path: parentPath,
permissions,
readOnly,
removeRow,
row,
@@ -122,17 +123,16 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
onToggle={(collapsed) => setCollapse(row.id, collapsed)}
>
<HiddenInput name={`${path}.id`} value={row.id} />
<FieldPathProvider path={path} schemaPath={parentPath}>
<RenderFields
className={`${baseClass}__fields`}
fieldMap={fieldMap}
forceRender={forceRender}
// indexPath={indexPath}
margins="small"
// permissions={permissions?.fields}
// readOnly={readOnly}
/>
</FieldPathProvider>
<RenderFields
className={`${baseClass}__fields`}
fieldMap={fieldMap}
forceRender={forceRender}
margins="small"
path={path}
permissions={permissions?.fields}
readOnly={readOnly}
schemaPath={parentPath}
/>
</Collapsible>
</div>
)

View File

@@ -17,7 +17,7 @@ import { scrollToID } from '../../../utilities/scrollToID.js'
import { useForm, useFormSubmitted } from '../../Form/context.js'
import LabelComp from '../../Label/index.js'
import { NullifyLocaleField } from '../../NullifyField/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { fieldBaseClass } from '../shared.js'
import { ArrayRow } from './ArrayRow.js'
import './index.scss'

View File

@@ -12,9 +12,9 @@ import { Collapsible } from '../../../elements/Collapsible/index.js'
import { ErrorPill } from '../../../elements/ErrorPill/index.js'
import Pill from '../../../elements/Pill/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import { FieldPathProvider } from '../../FieldPathProvider/index.js'
import { FieldPropsProvider } from '../../FieldPropsProvider/index.js'
import { useFormSubmitted } from '../../Form/context.js'
import RenderFields from '../../RenderFields/index.js'
import { RenderFields } from '../../RenderFields/index.js'
import HiddenInput from '../HiddenInput/index.js'
import { RowActions } from './RowActions.js'
import SectionTitle from './SectionTitle/index.js'
@@ -54,6 +54,7 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
listeners,
moveRow,
path: parentPath,
permissions,
readOnly,
removeRow,
row,
@@ -132,17 +133,16 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
onToggle={(collapsed) => setCollapse(row.id, collapsed)}
>
<HiddenInput name={`${path}.id`} value={row.id} />
<FieldPathProvider path={path} schemaPath={`${schemaPath}.${block.slug}`}>
<RenderFields
className={`${baseClass}__fields`}
fieldMap={block.subfields}
forceRender={forceRender}
// indexPath={indexPath}
margins="small"
// permissions={permissions?.blocks?.[row.blockType]?.fields}
// readOnly={readOnly}
/>
</FieldPathProvider>
<RenderFields
className={`${baseClass}__fields`}
fieldMap={block.subfields}
forceRender={forceRender}
margins="small"
path={path}
permissions={permissions?.blocks?.[block.slug]?.fields}
readOnly={readOnly}
schemaPath={`${schemaPath}.${block.slug}`}
/>
</Collapsible>
</div>
)

View File

@@ -3,7 +3,7 @@ import React from 'react'
import type { Props } from './types.js'
import { useTranslation } from '../../../../providers/Translation/index.js'
import useField from '../../../useField/index.js'
import { useField } from '../../../useField/index.js'
import './index.scss'
const baseClass = 'section-title'

View File

@@ -19,7 +19,7 @@ import { scrollToID } from '../../../utilities/scrollToID.js'
import { useForm, useFormSubmitted } from '../../Form/context.js'
import LabelComp from '../../Label/index.js'
import { NullifyLocaleField } from '../../NullifyField/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { fieldBaseClass } from '../shared.js'
import { BlockRow } from './BlockRow.js'
import { BlocksDrawer } from './BlocksDrawer/index.js'
@@ -41,7 +41,6 @@ const BlocksField: React.FC<Props> = (props) => {
label,
localized,
path: pathFromProps,
permissions,
readOnly,
required,
validate,
@@ -91,6 +90,7 @@ const BlocksField: React.FC<Props> = (props) => {
const {
path,
permissions,
rows = [],
schemaPath,
showError,

View File

@@ -8,7 +8,7 @@ import type { Props } from './types.js'
import { generateFieldID } from '../../../utilities/generateFieldID.js'
import { useForm } from '../../Form/context.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import { CheckboxInput } from './Input.js'

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/destructuring-assignment */
'use client'
import React, { useCallback } from 'react'
@@ -5,7 +6,7 @@ import type { Props } from './types.js'
import { CodeEditor } from '../../../elements/CodeEditor/index.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/destructuring-assignment */
'use client'
import type { DocumentPreferences } from 'payload/types'
@@ -10,9 +11,9 @@ import { ErrorPill } from '../../../elements/ErrorPill/index.js'
import { useDocumentInfo } from '../../../providers/DocumentInfo/index.js'
import { usePreferences } from '../../../providers/Preferences/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import { useFieldPath } from '../../FieldPathProvider/index.js'
import { useFieldProps } from '../../FieldPropsProvider/index.js'
import LabelComp from '../../Label/index.js'
import RenderFields from '../../RenderFields/index.js'
import { RenderFields } from '../../RenderFields/index.js'
import { WatchChildErrors } from '../../WatchChildErrors/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
@@ -23,20 +24,17 @@ const baseClass = 'collapsible-field'
const CollapsibleField: React.FC<Props> = (props) => {
const {
Description,
Error,
Label: LabelFromProps,
className,
fieldMap,
label,
path: pathFromProps,
permissions,
readOnly,
required,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { path: pathFromContext } = useFieldPath()
const { path: pathFromContext, readOnly, schemaPath, siblingPermissions } = useFieldProps()
const path = pathFromProps || pathFromContext
const { i18n } = useTranslation()
@@ -52,7 +50,7 @@ const CollapsibleField: React.FC<Props> = (props) => {
async (newCollapsedState: boolean) => {
const existingPreferences: DocumentPreferences = await getPreference(preferencesKey)
setPreference(preferencesKey, {
void setPreference(preferencesKey, {
...existingPreferences,
...(path
? {
@@ -91,7 +89,7 @@ const CollapsibleField: React.FC<Props> = (props) => {
}
}
fetchInitialState()
void fetchInitialState()
}, [getPreference, preferencesKey, fieldPreferencesKey, initCollapsed, path])
if (typeof collapsedOnMount !== 'boolean') return null
@@ -125,10 +123,11 @@ const CollapsibleField: React.FC<Props> = (props) => {
<RenderFields
fieldMap={fieldMap}
forceRender
// indexPath={path}
margins="small"
// permissions={permissions}
// readOnly={readOnly}
path={path}
permissions={siblingPermissions}
readOnly={readOnly}
schemaPath={schemaPath}
/>
</Collapsible>
{Description}

View File

@@ -9,7 +9,7 @@ import { useTranslation } from '../../../providers/Translation/index.js'
import Error from '../../Error/index.js'
import { useFormFields } from '../../Form/context.js'
import Label from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/destructuring-assignment */
'use client'
import type { ClientValidate } from 'payload/types'
@@ -8,7 +9,7 @@ import type { Props } from './types.js'
import { DatePickerField } from '../../../elements/DatePicker/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'

View File

@@ -8,7 +8,7 @@ import type { Props } from './types.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'

View File

@@ -6,9 +6,9 @@ import type { Props } from './types.js'
import { useCollapsible } from '../../../elements/Collapsible/provider.js'
import { ErrorPill } from '../../../elements/ErrorPill/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import { FieldPathProvider, useFieldPath } from '../../FieldPathProvider/index.js'
import { useFieldProps } from '../../FieldPropsProvider/index.js'
import LabelComp from '../../Label/index.js'
import RenderFields from '../../RenderFields/index.js'
import { RenderFields } from '../../RenderFields/index.js'
import { WatchChildErrors } from '../../WatchChildErrors/index.js'
import { withCondition } from '../../withCondition/index.js'
import { useRow } from '../Row/provider.js'
@@ -21,7 +21,6 @@ const baseClass = 'group-field'
const Group: React.FC<Props> = (props) => {
const {
name,
Description,
Label: LabelFromProps,
className,
@@ -35,7 +34,7 @@ const Group: React.FC<Props> = (props) => {
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { path, schemaPath } = useFieldPath()
const { path, permissions, readOnly, schemaPath } = useFieldProps()
const { i18n } = useTranslation()
const isWithinCollapsible = useCollapsible()
const isWithinGroup = useGroup()
@@ -70,22 +69,26 @@ const Group: React.FC<Props> = (props) => {
width,
}}
>
<FieldPathProvider path={path} schemaPath={schemaPath}>
<GroupProvider>
<div className={`${baseClass}__wrap`}>
<div className={`${baseClass}__header`}>
{(Label || Description) && (
<header>
{Label}
{Description}
</header>
)}
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
</div>
<RenderFields fieldMap={fieldMap} />
<GroupProvider>
<div className={`${baseClass}__wrap`}>
<div className={`${baseClass}__header`}>
{(Label || Description) && (
<header>
{Label}
{Description}
</header>
)}
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
</div>
</GroupProvider>
</FieldPathProvider>
<RenderFields
fieldMap={fieldMap}
path={path}
permissions={permissions?.fields}
readOnly={readOnly}
schemaPath={schemaPath}
/>
</div>
</GroupProvider>
</div>
</Fragment>
)

View File

@@ -3,7 +3,7 @@ import React, { useEffect } from 'react'
import type { Props } from './types.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
/**

View File

@@ -7,7 +7,7 @@ import type { Props } from './types.js'
import { CodeEditor } from '../../../elements/CodeEditor/index.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/destructuring-assignment */
'use client'
import { getTranslation } from '@payloadcms/translations'
import { isNumber } from 'payload/utilities'
@@ -9,7 +10,7 @@ import type { Props } from './types.js'
import ReactSelect from '../../../elements/ReactSelect/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'

View File

@@ -6,7 +6,7 @@ import React, { useCallback } from 'react'
import type { Props } from './types.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/destructuring-assignment */
'use client'
import type { ClientValidate } from 'payload/types'
@@ -8,7 +9,7 @@ import type { Props } from './types.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'

View File

@@ -1,3 +1,5 @@
/* eslint-disable react/destructuring-assignment */
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import { optionIsObject } from 'payload/types'
import React, { useCallback } from 'react'
@@ -6,7 +8,7 @@ import type { Props } from './types.js'
import { useForm } from '../../Form/context.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import { Radio } from './Radio/index.js'

View File

@@ -17,7 +17,7 @@ import { useConfig } from '../../../providers/Config/index.js'
import { useLocale } from '../../../providers/Locale/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import { useFormProcessing } from '../../Form/context.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import { AddNewRelation } from './AddNew/index.js'
@@ -122,7 +122,7 @@ const Relationship: React.FC<Props> = (props) => {
})
if (!errorLoading) {
relationsToFetch.reduce(async (priorRelation, relation) => {
await relationsToFetch.reduce(async (priorRelation, relation) => {
const relationFilterOption = filterOptionsResult?.[relation]
let lastLoadedPageToUse
if (search !== searchArg) {
@@ -208,7 +208,7 @@ const Relationship: React.FC<Props> = (props) => {
type: 'ADD',
collection,
// TODO: fix this
// @ts-ignore-next-line
// @ts-expect-error-next-line
config,
docs: data.docs,
i18n,
@@ -221,7 +221,7 @@ const Relationship: React.FC<Props> = (props) => {
type: 'ADD',
collection,
// TODO: fix this
// @ts-ignore-next-line
// @ts-expect-error-next-line
config,
docs: [],
i18n,
@@ -257,7 +257,7 @@ const Relationship: React.FC<Props> = (props) => {
)
const updateSearch = useDebouncedCallback((searchArg: string, valueArg: Value | Value[]) => {
getResults({ search: searchArg, sort: true, value: valueArg })
void getResults({ search: searchArg, sort: true, value: valueArg })
setSearch(searchArg)
}, 300)
@@ -282,7 +282,7 @@ const Relationship: React.FC<Props> = (props) => {
value,
})
Object.entries(relationMap).reduce(async (priorRelation, [relation, ids]) => {
void Object.entries(relationMap).reduce(async (priorRelation, [relation, ids]) => {
await priorRelation
const idsToLoad = ids.filter((id) => {
@@ -326,7 +326,7 @@ const Relationship: React.FC<Props> = (props) => {
type: 'ADD',
collection,
// TODO: fix this
// @ts-ignore-next-line
// @ts-expect-error-next-line
config,
docs,
i18n,
@@ -382,7 +382,7 @@ const Relationship: React.FC<Props> = (props) => {
type: 'UPDATE',
collection: args.collectionConfig,
// TODO: fix this
// @ts-ignore-next-line
// @ts-expect-error-next-line
config,
doc: args.doc,
i18n,
@@ -498,7 +498,7 @@ const Relationship: React.FC<Props> = (props) => {
onMenuOpen={() => {
if (!hasLoadedFirstPage) {
setIsLoading(true)
getResults({
void getResults({
onSuccess: () => {
setHasLoadedFirstPage(true)
setIsLoading(false)
@@ -508,7 +508,7 @@ const Relationship: React.FC<Props> = (props) => {
}
}}
onMenuScrollToBottom={() => {
getResults({
void getResults({
lastFullyLoadedRelation,
search,
sort: false,

View File

@@ -3,7 +3,8 @@ import React from 'react'
import type { Props } from './types.js'
import RenderFields from '../../RenderFields/index.js'
import { useFieldProps } from '../../FieldPropsProvider/index.js'
import { RenderFields } from '../../RenderFields/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'
@@ -12,19 +13,20 @@ import { RowProvider } from './provider.js'
const baseClass = 'row'
const Row: React.FC<Props> = (props) => {
const { className, fieldMap, forceRender = false, indexPath, permissions, readOnly } = props
const { className, fieldMap, forceRender = false } = props
const { path, readOnly, schemaPath, siblingPermissions } = useFieldProps()
return (
<RowProvider>
<div className={[fieldBaseClass, baseClass, className].filter(Boolean).join(' ')}>
<RenderFields
{...{ fieldMap, forceRender, path, readOnly, schemaPath }}
className={`${baseClass}__fields`}
fieldMap={fieldMap}
forceRender={forceRender}
// indexPath={indexPath}
margins={false}
// permissions={permissions}
// readOnly={readOnly}
permissions={siblingPermissions}
/>
</div>
</RowProvider>

View File

@@ -8,5 +8,5 @@ export type Props = FormFieldBase & {
forceRender?: boolean
indexPath: string
path?: string
permissions: FieldPermissions
permissions?: FieldPermissions
}

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/destructuring-assignment */
'use client'
import type { ClientValidate, Option, OptionObject } from 'payload/types'
@@ -9,7 +10,7 @@ import type { Props } from './types.js'
import ReactSelect from '../../../elements/ReactSelect/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import './index.scss'

View File

@@ -11,8 +11,8 @@ import { useCollapsible } from '../../../elements/Collapsible/provider.js'
import { useDocumentInfo } from '../../../providers/DocumentInfo/index.js'
import { usePreferences } from '../../../providers/Preferences/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import { FieldPathProvider, useFieldPath } from '../../FieldPathProvider/index.js'
import RenderFields from '../../RenderFields/index.js'
import { useFieldProps } from '../../FieldPropsProvider/index.js'
import { RenderFields } from '../../RenderFields/index.js'
import { withCondition } from '../../withCondition/index.js'
import { fieldBaseClass } from '../shared.js'
import { TabComponent } from './Tab/index.js'
@@ -26,16 +26,14 @@ const TabsField: React.FC<Props> = (props) => {
name,
Description,
className,
fieldMap,
forceRender = false,
indexPath,
path: pathFromProps,
permissions,
readOnly,
tabs = [],
} = props
const { path: pathFromContext, schemaPath } = useFieldPath()
const { path: pathFromContext, permissions, schemaPath } = useFieldProps()
const path = pathFromContext || pathFromProps || name
const { getPreference, setPreference } = usePreferences()
const { preferencesKey } = useDocumentInfo()
@@ -131,28 +129,24 @@ const TabsField: React.FC<Props> = (props) => {
.join(' ')}
>
{Description}
<FieldPathProvider
<RenderFields
fieldMap={activeTabConfig.subfields}
forceRender={forceRender}
key={
activeTabConfig.label
? getTranslation(activeTabConfig.label, i18n)
: activeTabConfig['name']
}
margins="small"
path={`${path ? `${path}.` : ''}${activeTabConfig.name ? `${activeTabConfig.name}` : ''}`}
permissions={
'name' in activeTabConfig && permissions?.fields?.[activeTabConfig.name]?.fields
? permissions?.fields?.[activeTabConfig.name]?.fields
: permissions?.fields
}
readOnly={readOnly}
schemaPath={`${schemaPath ? `${schemaPath}` : ''}${activeTabConfig.name ? `.${activeTabConfig.name}` : ''}`}
>
<RenderFields
fieldMap={activeTabConfig.subfields}
forceRender={forceRender}
// indexPath={indexPath}
key={
activeTabConfig.label
? getTranslation(activeTabConfig.label, i18n)
: activeTabConfig['name']
}
margins="small"
// permissions={
// 'name' in activeTabConfig && permissions?.[activeTabConfig.name]
// ? permissions[activeTabConfig.name].fields
// : permissions
// }
// readOnly={readOnly}
/>
</FieldPathProvider>
/>
</div>
</React.Fragment>
)}

View File

@@ -9,7 +9,7 @@ import type { Props } from './types.js'
import { useConfig } from '../../../providers/Config/index.js'
import { useLocale } from '../../../providers/Locale/index.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { isFieldRTL } from '../shared.js'
import { TextInput } from './Input.js'

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/destructuring-assignment */
'use client'
import type { ClientValidate } from 'payload/types'
@@ -9,7 +10,7 @@ import type { Props } from './types.js'
import { useConfig } from '../../../providers/Config/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { withCondition } from '../../withCondition/index.js'
import { isFieldRTL } from '../shared.js'
import { TextareaInput } from './Input.js'

View File

@@ -5,7 +5,7 @@ import type { Props } from './types.js'
import { useConfig } from '../../../providers/Config/index.js'
import LabelComp from '../../Label/index.js'
import useField from '../../useField/index.js'
import { useField } from '../../useField/index.js'
import { UploadInput } from './Input.js'
import './index.scss'

View File

@@ -31,9 +31,9 @@ export type FormFieldBase = {
Error?: React.ReactNode
Label?: React.ReactNode
className?: string
disabled?: boolean
docPreferences?: DocumentPreferences
fieldMap?: FieldMap
fieldPermissions?: FieldPermissions
initialSubfieldState?: FormState
label?: string
locale?: Locale

View File

@@ -10,21 +10,18 @@ import { useConfig } from '../../providers/Config/index.js'
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
import { useOperation } from '../../providers/OperationProvider/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { useFieldPath } from '../FieldPathProvider/index.js'
import { useFieldProps } from '../FieldPropsProvider/index.js'
import { useForm, useFormFields, useFormProcessing, useFormSubmitted } from '../Form/context.js'
import { useReadOnly } from '../ReadOnlyProvider/index.js'
/**
* Get and set the value of a form field.
*
* @see https://payloadcms.com/docs/admin/hooks#usefield
*/
const useField = <T,>(options: Options): FieldType<T> => {
export const useField = <T,>(options: Options): FieldType<T> => {
const { disableFormData = false, hasRows, validate } = options
const { path: pathFromContext, schemaPath } = useFieldPath()
const readOnly = useReadOnly()
const { path: pathFromContext, permissions, readOnly, schemaPath } = useFieldProps()
const path = options.path || pathFromContext
@@ -86,6 +83,7 @@ const useField = <T,>(options: Options): FieldType<T> => {
formSubmitted: submitted,
initialValue,
path,
permissions,
readOnly: readOnly || false,
rows: field?.rows,
schemaPath,
@@ -107,6 +105,7 @@ const useField = <T,>(options: Options): FieldType<T> => {
path,
schemaPath,
readOnly,
permissions,
],
)
@@ -190,5 +189,3 @@ const useField = <T,>(options: Options): FieldType<T> => {
return result
}
export default useField

View File

@@ -1,4 +1,4 @@
import type { ClientValidate, Row } from 'payload/types'
import type { ClientValidate, FieldPermissions, Row } from 'payload/types'
export type Options = {
disableFormData?: boolean
@@ -16,6 +16,7 @@ export type FieldType<T> = {
formSubmitted: boolean
initialValue?: T
path: string
permissions: FieldPermissions
readOnly?: boolean
rows?: Row[]
schemaPath: string

View File

@@ -1,7 +1,7 @@
'use client'
import React from 'react'
import { useFieldPath } from '../FieldPathProvider/index.js'
import { useFieldProps } from '../FieldPropsProvider/index.js'
import { WatchCondition } from './WatchCondition.js'
export const withCondition = <P extends Record<string, unknown>>(
@@ -9,7 +9,7 @@ export const withCondition = <P extends Record<string, unknown>>(
): React.FC<P> => {
const CheckForCondition: React.FC<P> = (props) => {
const { name } = props
const { path: pathFromContext } = useFieldPath()
const { path: pathFromContext } = useFieldProps()
const path = pathFromContext || name
return (

View File

@@ -1,4 +1,3 @@
import type { CollectionPermission, GlobalPermission, Permissions } from 'payload/auth'
import type { EditViewProps, SanitizedConfig } from 'payload/types'
import React from 'react'
@@ -14,7 +13,6 @@ export const buildComponentMap = (args: {
DefaultListView: React.FC<EditViewProps>
children: React.ReactNode
config: SanitizedConfig
permissions?: Permissions
readOnly?: boolean
}): {
componentMap: ComponentMap
@@ -26,18 +24,13 @@ export const buildComponentMap = (args: {
DefaultListView,
children,
config,
permissions,
readOnly: readOnlyOverride,
} = args
let entityPermissions: CollectionPermission | GlobalPermission
// Collections
const collections = config.collections.reduce((acc, collectionConfig) => {
const { slug, fields } = collectionConfig
entityPermissions = permissions?.collections?.[collectionConfig.slug]
const editViewFromConfig = collectionConfig?.admin?.components?.views?.Edit
const listViewFromConfig = collectionConfig?.admin?.components?.views?.List
@@ -113,7 +106,6 @@ export const buildComponentMap = (args: {
DefaultCell,
config,
fieldSchema: fields,
permissions: entityPermissions?.fields,
readOnly: readOnlyOverride,
}),
}
@@ -128,8 +120,6 @@ export const buildComponentMap = (args: {
const globals = config.globals.reduce((acc, globalConfig) => {
const { slug, fields } = globalConfig
entityPermissions = permissions?.globals?.[globalConfig.slug]
const editViewFromConfig = globalConfig?.admin?.components?.views?.Edit
const CustomEditView =
@@ -154,7 +144,6 @@ export const buildComponentMap = (args: {
DefaultCell,
config,
fieldSchema: fields,
permissions: entityPermissions?.fields,
readOnly: readOnlyOverride,
}),
}

View File

@@ -1,4 +1,3 @@
import type { CollectionPermission, FieldPermissions, GlobalPermission } from 'payload/auth'
import type { CellProps, Field, FieldWithPath, LabelProps, SanitizedConfig } from 'payload/types'
import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types'
@@ -22,18 +21,9 @@ export const mapFields = (args: {
fieldSchema: FieldWithPath[]
filter?: (field: Field) => boolean
parentPath?: string
permissions?: CollectionPermission['fields'] | GlobalPermission['fields']
readOnly?: boolean
}): FieldMap => {
const {
DefaultCell,
config,
fieldSchema,
filter,
parentPath,
permissions,
readOnly: readOnlyOverride,
} = args
const { DefaultCell, config, fieldSchema, filter, parentPath, readOnly: readOnlyOverride } = args
const result: FieldMap = fieldSchema.reduce((acc, field): FieldMap => {
const fieldIsPresentational = fieldIsPresentationalOnly(field)
@@ -51,8 +41,6 @@ export const mapFields = (args: {
field.path || (isFieldAffectingData && 'name' in field ? field.name : '')
}`
const fieldPermissions = isFieldAffectingData ? permissions?.[field.name] : undefined
const labelProps: LabelProps = {
// @ts-expect-error-next-line
label: 'label' in field ? field.label : null,
@@ -78,7 +66,6 @@ export const mapFields = (args: {
fieldSchema: field.fields,
filter,
parentPath: path,
permissions,
readOnly: readOnlyOverride,
})
@@ -94,7 +81,6 @@ export const mapFields = (args: {
fieldSchema: tab.fields,
filter,
parentPath: path,
permissions,
readOnly: readOnlyOverride,
})
@@ -119,7 +105,6 @@ export const mapFields = (args: {
fieldSchema: block.fields,
filter,
parentPath: `${path}.${block.slug}`,
permissions,
readOnly: readOnlyOverride,
})
@@ -199,8 +184,8 @@ export const mapFields = (args: {
className:
'admin' in field && 'className' in field.admin ? field?.admin?.className : undefined,
date: 'admin' in field && 'date' in field.admin ? field.admin.date : undefined,
disabled: field?.admin && 'disabled' in field.admin ? field.admin?.disabled : false,
fieldMap: nestedFieldMap,
fieldPermissions,
hasMany: 'hasMany' in field ? field.hasMany : undefined,
label: 'label' in field && typeof field.label === 'string' ? field.label : undefined,
max: 'max' in field ? field.max : undefined,
@@ -318,8 +303,8 @@ export const mapFields = (args: {
/>
),
blocks,
disabled: field?.admin && 'disabled' in field.admin ? field.admin?.disabled : false,
fieldIsPresentational,
fieldPermissions,
hasMany: 'hasMany' in field ? field.hasMany : undefined,
isFieldAffectingData,
isSidebar: 'admin' in field && field.admin?.position === 'sidebar',
@@ -354,7 +339,6 @@ export const mapFields = (args: {
Field: <HiddenInput name="id" />,
Heading: <SortColumn label="ID" name="id" />,
fieldIsPresentational: false,
fieldPermissions: {} as FieldPermissions,
isFieldAffectingData: true,
isSidebar: false,
label: 'ID',

View File

@@ -1,4 +1,3 @@
import type { FieldPermissions } from 'payload/auth'
import type {
BlockField,
FieldBase,
@@ -35,13 +34,13 @@ export type MappedField = {
* On `block` fields only
*/
blocks?: ReducedBlock[]
disabled?: boolean
/**
* On `richText` fields only
*/
editor?: RichTextField['editor']
fieldIsPresentational: boolean
fieldMap?: FieldMap
fieldPermissions: FieldPermissions
hasMany?: boolean
isFieldAffectingData: boolean
isSidebar: boolean

View File

@@ -88,7 +88,7 @@
"./packages/graphql/src"
],
"@payload-config": [
"./test/admin/config.ts"
"./test/access-control/config.ts"
]
}
},