Although the `<FieldLabel />` component receives a `field` prop, it does
not use this prop to extract the `label` from the field. This is
currently only an issue when rendering this component directly, such as
within `admin.components.Label`. The label simply won't appear unless
explicitly provided, despite it being passed as `field.label`. This is
not an issue when rendering field components themselves, because they
properly thread this value through as a top-level prop.
Here's an example of the issue:
```tsx
import type { TextFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const MyCustomLabelComponent: TextFieldLabelServerComponent = ({ clientField }) => {
return (
<FieldLabel
field={clientField}
label={clientField.label} // this should not be needed!
/>
)
}
```
Here is the end result:
```tsx
import type { TextFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const MyCustomLabelComponent: TextFieldLabelServerComponent = ({ clientField }) => {
return <FieldLabel field={clientField} />
}
```
115 lines
3.2 KiB
TypeScript
115 lines
3.2 KiB
TypeScript
'use client'
|
|
import type { CodeFieldClientComponent } from 'payload'
|
|
|
|
import React, { useCallback } from 'react'
|
|
|
|
import { CodeEditor } from '../../elements/CodeEditor/index.js'
|
|
import { useFieldProps } from '../../forms/FieldPropsProvider/index.js'
|
|
import { useField } from '../../forms/useField/index.js'
|
|
import { withCondition } from '../../forms/withCondition/index.js'
|
|
import { RenderComponent } from '../../providers/Config/RenderComponent.js'
|
|
import { FieldDescription } from '../FieldDescription/index.js'
|
|
import { FieldError } from '../FieldError/index.js'
|
|
import { FieldLabel } from '../FieldLabel/index.js'
|
|
import { fieldBaseClass } from '../shared/index.js'
|
|
import './index.scss'
|
|
|
|
const prismToMonacoLanguageMap = {
|
|
js: 'javascript',
|
|
ts: 'typescript',
|
|
}
|
|
|
|
const baseClass = 'code-field'
|
|
|
|
const CodeFieldComponent: CodeFieldClientComponent = (props) => {
|
|
const {
|
|
descriptionProps,
|
|
errorProps,
|
|
field,
|
|
field: {
|
|
name,
|
|
_path: pathFromProps,
|
|
admin: {
|
|
className,
|
|
description,
|
|
editorOptions = {},
|
|
language = 'javascript',
|
|
readOnly: readOnlyFromAdmin,
|
|
style,
|
|
width,
|
|
} = {},
|
|
label,
|
|
required,
|
|
},
|
|
labelProps,
|
|
readOnly: readOnlyFromTopLevelProps,
|
|
|
|
validate,
|
|
} = props
|
|
|
|
const readOnlyFromProps = readOnlyFromTopLevelProps || readOnlyFromAdmin
|
|
|
|
const memoizedValidate = useCallback(
|
|
(value, options) => {
|
|
if (typeof validate === 'function') {
|
|
return validate(value, { ...options, required })
|
|
}
|
|
},
|
|
[validate, required],
|
|
)
|
|
|
|
const { path: pathFromContext, readOnly: readOnlyFromContext } = useFieldProps()
|
|
|
|
const { formInitializing, formProcessing, path, setValue, showError, value } = useField({
|
|
path: pathFromContext ?? pathFromProps ?? name,
|
|
validate: memoizedValidate,
|
|
})
|
|
|
|
const disabled = readOnlyFromProps || readOnlyFromContext || formProcessing || formInitializing
|
|
|
|
return (
|
|
<div
|
|
className={[
|
|
fieldBaseClass,
|
|
baseClass,
|
|
className,
|
|
showError && 'error',
|
|
disabled && 'read-only',
|
|
]
|
|
.filter(Boolean)
|
|
.join(' ')}
|
|
style={{
|
|
...style,
|
|
width,
|
|
}}
|
|
>
|
|
<FieldLabel field={field} Label={field?.admin?.components?.Label} {...(labelProps || {})} />
|
|
<div className={`${fieldBaseClass}__wrap`}>
|
|
<FieldError
|
|
CustomError={field?.admin?.components?.Error}
|
|
field={field}
|
|
path={path}
|
|
{...(errorProps || {})}
|
|
/>
|
|
<RenderComponent mappedComponent={field?.admin?.components?.beforeInput} />
|
|
<CodeEditor
|
|
defaultLanguage={prismToMonacoLanguageMap[language] || language}
|
|
onChange={disabled ? () => null : (val) => setValue(val)}
|
|
options={editorOptions}
|
|
readOnly={disabled}
|
|
value={(value as string) || ''}
|
|
/>
|
|
<RenderComponent mappedComponent={field?.admin?.components?.afterInput} />
|
|
</div>
|
|
<FieldDescription
|
|
Description={field?.admin?.components?.Description}
|
|
description={description}
|
|
field={field}
|
|
{...(descriptionProps || {})}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export const CodeField = withCondition(CodeFieldComponent)
|