Files
payloadcms/packages/ui/src/fields/Group/index.tsx
Jarrod Flesch cedd916816 fix: corrects permission access reading for disabling fields (#6815)
Fixes issues where access control was not properly affecting the read-only setting on fields.
2024-06-17 18:33:45 -04:00

128 lines
4.1 KiB
TypeScript

'use client'
import { getTranslation } from '@payloadcms/translations'
import React, { Fragment } from 'react'
import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js'
import type { FormFieldBase } from '../shared/index.js'
import { useCollapsible } from '../../elements/Collapsible/provider.js'
import { ErrorPill } from '../../elements/ErrorPill/index.js'
import { useFieldProps } from '../../forms/FieldPropsProvider/index.js'
import {
useFormInitializing,
useFormProcessing,
useFormSubmitted,
} from '../../forms/Form/context.js'
import { RenderFields } from '../../forms/RenderFields/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { FieldDescription } from '../FieldDescription/index.js'
import { useRow } from '../Row/provider.js'
import { useTabs } from '../Tabs/provider.js'
import { fieldBaseClass } from '../shared/index.js'
import './index.scss'
import { GroupProvider, useGroup } from './provider.js'
const baseClass = 'group-field'
export type GroupFieldProps = FormFieldBase & {
fieldMap: FieldMap
forceRender?: boolean
hideGutter?: boolean
name?: string
width?: string
}
const GroupField: React.FC<GroupFieldProps> = (props) => {
const {
CustomDescription,
CustomLabel,
className,
descriptionProps,
fieldMap,
hideGutter,
label,
readOnly: readOnlyFromProps,
style,
width,
} = props
const { path, permissions, readOnly: readOnlyFromContext, schemaPath } = useFieldProps()
const { i18n } = useTranslation()
const { isWithinCollapsible } = useCollapsible()
const isWithinGroup = useGroup()
const isWithinRow = useRow()
const isWithinTab = useTabs()
const { errorPaths } = useField({ path })
const formInitializing = useFormInitializing()
const formProcessing = useFormProcessing()
const submitted = useFormSubmitted()
const errorCount = errorPaths.length
const fieldHasErrors = submitted && errorCount > 0
const disabled = readOnlyFromProps || readOnlyFromContext || formProcessing || formInitializing
const isTopLevel = !(isWithinCollapsible || isWithinGroup || isWithinRow)
return (
<Fragment>
<div
className={[
fieldBaseClass,
baseClass,
isTopLevel && `${baseClass}--top-level`,
isWithinCollapsible && `${baseClass}--within-collapsible`,
isWithinGroup && `${baseClass}--within-group`,
isWithinRow && `${baseClass}--within-row`,
isWithinTab && `${baseClass}--within-tab`,
!hideGutter && isWithinGroup && `${baseClass}--gutter`,
fieldHasErrors && `${baseClass}--has-error`,
className,
]
.filter(Boolean)
.join(' ')}
id={`field-${path?.replace(/\./g, '__')}`}
style={{
...style,
width,
}}
>
<GroupProvider>
<div className={`${baseClass}__wrap`}>
<div className={`${baseClass}__header`}>
{(CustomLabel || CustomDescription || label) && (
<header>
{CustomLabel !== undefined ? (
CustomLabel
) : label ? (
<h3 className={`${baseClass}__title`}>{getTranslation(label, i18n)}</h3>
) : null}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</header>
)}
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
</div>
<RenderFields
fieldMap={fieldMap}
margins="small"
path={path}
permissions={permissions?.fields}
readOnly={disabled}
schemaPath={schemaPath}
/>
</div>
</GroupProvider>
</div>
</Fragment>
)
}
export { GroupProvider, useGroup }
export const Group = withCondition(GroupField)