From 30947d217365d76751281aa671eff564b994d29f Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Mon, 18 Nov 2024 10:03:26 -0500 Subject: [PATCH] perf!: removes unnecessary field styles from initial page response (#9286) Optimizes initial page responses by removing unnecessary inline field styles that were being sent through the HTML response. The Client Config contains a large number of duplicates of the string: `"style\":{\"flex\":\"1 1 auto\"}`, one for every single field within the entirely of the config. This leads to hundreds or potentially thousands of instances of this same string, depending on the number of fields within the config itself. This is regardless of custom field widths being defined. Instead, we can do this entirely client-side, preventing this string from ever being transmitted over the network in the first place. ## Breaking Changes This only effects those who are importing Payload's field components into your own Custom Components or front-end application. The `width` prop no longer exists. It has been consolidated into the existing `style` prop. To migrate, simply move this prop as follows: ```diff import { TextInput } from '@payloadcms/ui export const MyCustomComponent = () => { return ( ) } ``` --- packages/payload/src/fields/config/client.ts | 19 --------------- packages/richtext-lexical/src/field/Field.tsx | 17 ++++++-------- .../richtext-slate/src/field/RichText.tsx | 16 ++++++------- packages/ui/src/exports/shared/index.ts | 1 + packages/ui/src/fields/Checkbox/index.tsx | 21 +++++++---------- packages/ui/src/fields/Code/index.tsx | 23 +++++++------------ packages/ui/src/fields/Collapsible/index.tsx | 16 ++++++------- packages/ui/src/fields/DateTime/index.tsx | 16 ++++++------- packages/ui/src/fields/Email/index.tsx | 16 ++++++------- packages/ui/src/fields/Group/index.tsx | 20 +++++++--------- packages/ui/src/fields/JSON/index.tsx | 16 ++++++------- packages/ui/src/fields/Join/index.tsx | 1 - packages/ui/src/fields/Number/index.tsx | 16 ++++++------- packages/ui/src/fields/Password/index.tsx | 14 +++++------ packages/ui/src/fields/Point/index.tsx | 16 ++++++------- packages/ui/src/fields/RadioGroup/index.tsx | 15 ++++++------ packages/ui/src/fields/Relationship/index.tsx | 16 ++++++------- packages/ui/src/fields/Select/Input.tsx | 7 +----- packages/ui/src/fields/Select/index.tsx | 11 +++++---- packages/ui/src/fields/Text/Input.tsx | 6 +---- packages/ui/src/fields/Text/index.tsx | 14 ++++++----- packages/ui/src/fields/Text/types.ts | 1 - packages/ui/src/fields/Textarea/Input.tsx | 6 +---- packages/ui/src/fields/Textarea/index.tsx | 14 ++++++----- packages/ui/src/fields/Textarea/types.ts | 1 - packages/ui/src/fields/Upload/Input.tsx | 9 ++------ packages/ui/src/fields/Upload/index.tsx | 12 ++++++---- packages/ui/src/fields/mergeFieldStyles.ts | 20 ++++++++++++++++ 28 files changed, 159 insertions(+), 201 deletions(-) create mode 100644 packages/ui/src/fields/mergeFieldStyles.ts diff --git a/packages/payload/src/fields/config/client.ts b/packages/payload/src/fields/config/client.ts index ded2b90fe..be2de5426 100644 --- a/packages/payload/src/fields/config/client.ts +++ b/packages/payload/src/fields/config/client.ts @@ -96,25 +96,6 @@ export const createClientField = ({ clientField.label = incomingField.label({ t: i18n.t }) } - if (!(clientField.admin instanceof Object)) { - clientField.admin = {} as AdminClient - } - - if ('admin' in incomingField && 'width' in incomingField.admin) { - clientField.admin.style = { - ...clientField.admin.style, - '--field-width': clientField.admin.width, - } - - delete clientField.admin.style.width // avoid needlessly adding this to the element's style attribute - } else { - if (!(clientField.admin.style instanceof Object)) { - clientField.admin.style = {} - } - - clientField.admin.style.flex = '1 1 auto' - } - switch (incomingField.type) { case 'array': case 'collapsible': diff --git a/packages/richtext-lexical/src/field/Field.tsx b/packages/richtext-lexical/src/field/Field.tsx index d629615d2..64455d543 100644 --- a/packages/richtext-lexical/src/field/Field.tsx +++ b/packages/richtext-lexical/src/field/Field.tsx @@ -2,7 +2,8 @@ import type { EditorState, SerializedEditorState } from 'lexical' import { FieldLabel, useEditDepth, useField, withCondition } from '@payloadcms/ui' -import React, { useCallback } from 'react' +import { mergeFieldStyles } from '@payloadcms/ui/shared' +import React, { useCallback, useMemo } from 'react' import { ErrorBoundary } from 'react-error-boundary' import type { SanitizedClientEditorConfig } from '../lexical/config/types.js' @@ -22,9 +23,10 @@ const RichTextComponent: React.FC< > = (props) => { const { editorConfig, + field, field: { name, - admin: { className, readOnly: readOnlyFromAdmin, style, width } = {}, + admin: { className, readOnly: readOnlyFromAdmin } = {}, label, localized, required, @@ -87,15 +89,10 @@ const RichTextComponent: React.FC< [setValue], ) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return ( -
+
{Error} {Label || }
diff --git a/packages/richtext-slate/src/field/RichText.tsx b/packages/richtext-slate/src/field/RichText.tsx index 79538812e..705178aaa 100644 --- a/packages/richtext-slate/src/field/RichText.tsx +++ b/packages/richtext-slate/src/field/RichText.tsx @@ -7,6 +7,7 @@ import type { ReactEditor } from 'slate-react' import { getTranslation } from '@payloadcms/translations' import { FieldLabel, useEditDepth, useField, useTranslation, withCondition } from '@payloadcms/ui' +import { mergeFieldStyles } from '@payloadcms/ui/shared' import { isHotkey } from 'is-hotkey' import React, { useCallback, useEffect, useMemo, useRef } from 'react' import { createEditor, Node, Element as SlateElement, Text, Transforms } from 'slate' @@ -19,8 +20,8 @@ import type { LoadedSlateFieldProps } from './types.js' import { defaultRichTextValue } from '../data/defaultValue.js' import { richTextValidate } from '../data/validation.js' import { listTypes } from './elements/listTypes.js' -import { hotkeys } from './hotkeys.js' import './index.scss' +import { hotkeys } from './hotkeys.js' import { toggleLeaf } from './leaves/toggle.js' import { withEnterBreakOut } from './plugins/withEnterBreakOut.js' import { withHTML } from './plugins/withHTML.js' @@ -42,9 +43,10 @@ declare module 'slate' { const RichTextField: React.FC = (props) => { const { elements, + field, field: { name, - admin: { className, placeholder, readOnly: readOnlyFromAdmin, style, width } = {}, + admin: { className, placeholder, readOnly: readOnlyFromAdmin } = {}, label, required, }, @@ -274,6 +276,8 @@ const RichTextField: React.FC = (props) => { // } // }, [path, editor]); + const styles = useMemo(() => mergeFieldStyles(field), [field]) + const classes = [ baseClass, 'field-type', @@ -300,13 +304,7 @@ const RichTextField: React.FC = (props) => { } return ( -
+
{Label || }
{Error} diff --git a/packages/ui/src/exports/shared/index.ts b/packages/ui/src/exports/shared/index.ts index 8ac53f14c..33cad82d3 100644 --- a/packages/ui/src/exports/shared/index.ts +++ b/packages/ui/src/exports/shared/index.ts @@ -4,6 +4,7 @@ export { getInitialColumns } from '../../elements/TableColumns/getInitialColumns export { Translation } from '../../elements/Translation/index.js' export { withMergedProps } from '../../elements/withMergedProps/index.js' // cannot be within a 'use client', thus we export this from shared export { WithServerSideProps } from '../../elements/WithServerSideProps/index.js' +export { mergeFieldStyles } from '../../fields/mergeFieldStyles.js' export { reduceToSerializableFields } from '../../forms/Form/reduceToSerializableFields.js' export { PayloadIcon } from '../../graphics/Icon/index.js' export { PayloadLogo } from '../../graphics/Logo/index.js' diff --git a/packages/ui/src/fields/Checkbox/index.tsx b/packages/ui/src/fields/Checkbox/index.tsx index ca6421775..336a7285b 100644 --- a/packages/ui/src/fields/Checkbox/index.tsx +++ b/packages/ui/src/fields/Checkbox/index.tsx @@ -5,7 +5,7 @@ import type { CheckboxFieldValidation, } from 'payload' -import React, { useCallback } from 'react' +import React, { useCallback, useMemo } from 'react' import type { CheckboxInputProps } from './Input.js' @@ -17,8 +17,9 @@ import { useField } from '../../forms/useField/index.js' import { withCondition } from '../../forms/withCondition/index.js' import { useEditDepth } from '../../providers/EditDepth/index.js' import { generateFieldID } from '../../utilities/generateFieldID.js' -import { fieldBaseClass } from '../shared/index.js' +import { mergeFieldStyles } from '../mergeFieldStyles.js' import './index.scss' +import { fieldBaseClass } from '../shared/index.js' import { CheckboxInput } from './Input.js' const baseClass = 'checkbox' @@ -30,14 +31,9 @@ const CheckboxFieldComponent: CheckboxFieldClientComponent = (props) => { id, checked: checkedFromProps, disableFormData, + field, field: { - name, - admin: { - className, - description, - style, - width, - } = {} as CheckboxFieldClientProps['field']['admin'], + admin: { className, description } = {} as CheckboxFieldClientProps['field']['admin'], label, required, } = {} as CheckboxFieldClientProps['field'], @@ -85,6 +81,8 @@ const CheckboxFieldComponent: CheckboxFieldClientComponent = (props) => { const fieldID = id || generateFieldID(path, editDepth, uuid) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ ] .filter(Boolean) .join(' ')} - style={{ - ...style, - width, - }} + style={styles} > { const { + field, field: { - name, - admin: { - className, - description, - editorOptions = {}, - language = 'javascript', - style, - width, - } = {}, + admin: { className, description, editorOptions = {}, language = 'javascript' } = {}, label, localized, required, @@ -60,6 +54,8 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => { validate: memoizedValidate, }) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ ] .filter(Boolean) .join(' ')} - style={{ - ...style, - width, - }} + style={styles} > { void fetchInitialState() }, [getPreference, preferencesKey, fieldPreferencesKey, initCollapsed, path]) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + if (typeof collapsedOnMount !== 'boolean') { return null } - const style: AdminClient['style'] = { - ...field.admin?.style, - '--field-width': field.admin.width, - } - return ( @@ -120,7 +118,7 @@ const CollapsibleFieldComponent: CollapsibleFieldClientComponent = (props) => { .filter(Boolean) .join(' ')} id={`field-${fieldPreferencesKey}`} - style={style} + style={styles} > { const { + field, field: { - name, - admin: { className, date: datePickerProps, description, placeholder, style, width } = {}, + admin: { className, date: datePickerProps, description, placeholder } = {}, label, localized, required, @@ -52,6 +53,8 @@ const DateTimeFieldComponent: DateFieldClientComponent = (props) => { validate: memoizedValidate, }) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ ] .filter(Boolean) .join(' ')} - style={{ - ...style, - width, - }} + style={styles} > { const { + field, field: { - name, admin: { autoComplete, className, description, placeholder, - style, - width, } = {} as EmailFieldClientProps['field']['admin'], label, localized, @@ -60,15 +59,14 @@ const EmailFieldComponent: EmailFieldClientComponent = (props) => { validate: memoizedValidate, }) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ const { - field: { - name, - admin: { className, description, hideGutter, style, width } = {}, - fields, - label, - }, + field, + field: { name, admin: { className, description, hideGutter } = {}, fields, label }, path, permissions, readOnly, @@ -50,6 +47,8 @@ export const GroupFieldComponent: GroupFieldClientComponent = (props) => { const isTopLevel = !(isWithinCollapsible || isWithinGroup || isWithinRow) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ .filter(Boolean) .join(' ')} id={`field-${path?.replace(/\./g, '__')}`} - style={{ - ...style, - width, - }} + style={styles} >
diff --git a/packages/ui/src/fields/JSON/index.tsx b/packages/ui/src/fields/JSON/index.tsx index 235d48e40..38ab2f8f8 100644 --- a/packages/ui/src/fields/JSON/index.tsx +++ b/packages/ui/src/fields/JSON/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { JSONFieldClientComponent } from 'payload' -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { CodeEditor } from '../../elements/CodeEditor/index.js' import { RenderCustomComponent } from '../../elements/RenderCustomComponent/index.js' @@ -10,16 +10,17 @@ import { withCondition } from '../../forms/withCondition/index.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 { mergeFieldStyles } from '../mergeFieldStyles.js' import './index.scss' +import { fieldBaseClass } from '../shared/index.js' const baseClass = 'json-field' const JSONFieldComponent: JSONFieldClientComponent = (props) => { const { + field, field: { - name, - admin: { className, description, editorOptions, maxHeight, style, width } = {}, + admin: { className, description, editorOptions, maxHeight } = {}, jsonSchema, label, localized, @@ -105,6 +106,8 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => { setHasLoadedValue(true) }, [initialValue, value, hasLoadedValue]) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ ] .filter(Boolean) .join(' ')} - style={{ - ...style, - width, - }} + style={styles} > { const { field, field: { - name, admin: { allowCreate }, collection, label, diff --git a/packages/ui/src/fields/Number/index.tsx b/packages/ui/src/fields/Number/index.tsx index 18a8d5cbd..6e320a83b 100644 --- a/packages/ui/src/fields/Number/index.tsx +++ b/packages/ui/src/fields/Number/index.tsx @@ -3,7 +3,7 @@ import type { NumberFieldClientComponent, NumberFieldClientProps } from 'payload import { getTranslation } from '@payloadcms/translations' import { isNumber } from 'payload/shared' -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import type { Option } from '../../elements/ReactSelect/types.js' @@ -15,20 +15,19 @@ import { useTranslation } from '../../providers/Translation/index.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 { mergeFieldStyles } from '../mergeFieldStyles.js' import './index.scss' +import { fieldBaseClass } from '../shared/index.js' const NumberFieldComponent: NumberFieldClientComponent = (props) => { const { + field, field: { - name, admin: { className, description, placeholder, step = 1, - style, - width, } = {} as NumberFieldClientProps['field']['admin'], hasMany = false, label, @@ -123,6 +122,8 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => { } }, [value, hasMany]) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ ] .filter(Boolean) .join(' ')} - style={{ - ...style, - width, - }} + style={styles} > = (props) => { const { autoComplete, + field, field: { - name, admin: { className, disabled: disabledFromProps, placeholder, rtl, - style, - width, } = {} as PasswordFieldProps['field']['admin'], label, localized, @@ -86,6 +85,8 @@ const PasswordFieldComponent: React.FC = (props) => { localizationConfig: config.localization || undefined, }) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return ( = (props) => { required={required} rtl={renderRTL} showError={showError} - style={style} + style={styles} value={(value as string) || ''} - width={width} /> ) } diff --git a/packages/ui/src/fields/Point/index.tsx b/packages/ui/src/fields/Point/index.tsx index 3d75a3857..37f79b0cd 100644 --- a/packages/ui/src/fields/Point/index.tsx +++ b/packages/ui/src/fields/Point/index.tsx @@ -2,7 +2,7 @@ import type { PointFieldClientComponent, PointFieldValidation } from 'payload' import { getTranslation } from '@payloadcms/translations' -import React, { useCallback } from 'react' +import React, { useCallback, useMemo } from 'react' import { RenderCustomComponent } from '../../elements/RenderCustomComponent/index.js' import { FieldDescription } from '../../fields/FieldDescription/index.js' @@ -11,16 +11,17 @@ import { FieldLabel } from '../../fields/FieldLabel/index.js' import { useField } from '../../forms/useField/index.js' import { withCondition } from '../../forms/withCondition/index.js' import { useTranslation } from '../../providers/Translation/index.js' -import { fieldBaseClass } from '../shared/index.js' +import { mergeFieldStyles } from '../mergeFieldStyles.js' import './index.scss' +import { fieldBaseClass } from '../shared/index.js' const baseClass = 'point' export const PointFieldComponent: PointFieldClientComponent = (props) => { const { + field, field: { - name, - admin: { className, description, placeholder, step, style, width } = {}, + admin: { className, description, placeholder, step } = {}, label, localized, required, @@ -71,6 +72,8 @@ export const PointFieldComponent: PointFieldClientComponent = (props) => { return `${fieldLabel}${fieldLabel ? ' - ' : ''}${suffix}` } + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ ] .filter(Boolean) .join(' ')} - style={{ - ...style, - width, - }} + style={styles} >
  • diff --git a/packages/ui/src/fields/RadioGroup/index.tsx b/packages/ui/src/fields/RadioGroup/index.tsx index c4a2db185..66b8cddc7 100644 --- a/packages/ui/src/fields/RadioGroup/index.tsx +++ b/packages/ui/src/fields/RadioGroup/index.tsx @@ -2,7 +2,7 @@ import type { RadioFieldClientComponent, RadioFieldClientProps } from 'payload' import { optionIsObject } from 'payload/shared' -import React, { useCallback } from 'react' +import React, { useCallback, useMemo } from 'react' import { RenderCustomComponent } from '../../elements/RenderCustomComponent/index.js' import { FieldDescription } from '../../fields/FieldDescription/index.js' @@ -11,8 +11,9 @@ import { FieldLabel } from '../../fields/FieldLabel/index.js' import { useForm } from '../../forms/Form/context.js' import { useField } from '../../forms/useField/index.js' import { withCondition } from '../../forms/withCondition/index.js' -import { fieldBaseClass } from '../shared/index.js' +import { mergeFieldStyles } from '../mergeFieldStyles.js' import './index.scss' +import { fieldBaseClass } from '../shared/index.js' import { Radio } from './Radio/index.js' const baseClass = 'radio-group' @@ -20,13 +21,12 @@ const baseClass = 'radio-group' const RadioGroupFieldComponent: RadioFieldClientComponent = (props) => { const { disableModifyingForm: disableModifyingFormFromProps, + field, field: { admin: { className, description, layout = 'horizontal', - style, - width, } = {} as RadioFieldClientProps['field']['admin'], label, localized, @@ -63,6 +63,8 @@ const RadioGroupFieldComponent: RadioFieldClientComponent = (props) => { const value = valueFromContext || valueFromProps + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
    { ] .filter(Boolean) .join(' ')} - style={{ - ...style, - width, - }} + style={styles} > { const { + field, field: { - name, admin: { allowCreate = true, allowEdit = true, @@ -47,8 +48,6 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) => description, isSortable = true, sortOptions, - style, - width, } = {}, hasMany, label, @@ -575,6 +574,8 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) => valueToRender.value = null } + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
    .filter(Boolean) .join(' ')} id={`field-${path.replace(/\./g, '__')}`} - style={{ - ...style, - width, - }} + style={styles} > = (props) => { @@ -60,7 +59,6 @@ export const SelectInput: React.FC = (props) => { showError, style, value, - width, } = props const { i18n } = useTranslation() @@ -95,10 +93,7 @@ export const SelectInput: React.FC = (props) => { .filter(Boolean) .join(' ')} id={`field-${path.replace(/\./g, '__')}`} - style={{ - ...style, - width, - }} + style={style} > @@ -29,6 +30,7 @@ const formatOptions = (options: Option[]): OptionObject[] => const SelectFieldComponent: SelectFieldClientComponent = (props) => { const { + field, field: { name, admin: { @@ -36,8 +38,6 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => { description, isClearable = true, isSortable = true, - style, - width, } = {} as SelectFieldClientProps['field']['admin'], hasMany = false, label, @@ -96,6 +96,8 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => { [readOnly, hasMany, setValue, onChangeFromProps], ) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return ( { path={path} readOnly={readOnly} showError={showError} - style={style} + style={styles} value={value as string | string[]} - width={width} /> ) } diff --git a/packages/ui/src/fields/Text/Input.tsx b/packages/ui/src/fields/Text/Input.tsx index 09ea5650c..bc03c85bf 100644 --- a/packages/ui/src/fields/Text/Input.tsx +++ b/packages/ui/src/fields/Text/Input.tsx @@ -40,7 +40,6 @@ export const TextInput: React.FC = (props) => { style, value, valueToRender, - width, } = props const { i18n, t } = useTranslation() @@ -57,10 +56,7 @@ export const TextInput: React.FC = (props) => { ] .filter(Boolean) .join(' ')} - style={{ - ...style, - width, - }} + style={style} > { const { + field, field: { - name, - admin: { className, description, placeholder, rtl, style, width } = {}, + admin: { className, description, placeholder, rtl } = {}, hasMany, label, localized, @@ -109,6 +110,8 @@ const TextFieldComponent: TextFieldClientComponent = (props) => { } }, [value, hasMany]) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return ( { required={required} rtl={renderRTL} showError={showError} - style={style} + style={styles} value={(value as string) || ''} valueToRender={valueToRender as Option[]} - width={width} /> ) } diff --git a/packages/ui/src/fields/Text/types.ts b/packages/ui/src/fields/Text/types.ts index f36a94f76..aee602325 100644 --- a/packages/ui/src/fields/Text/types.ts +++ b/packages/ui/src/fields/Text/types.ts @@ -37,5 +37,4 @@ export type TextInputProps = { readonly style?: React.CSSProperties readonly value?: string readonly valueToRender?: Option[] - readonly width?: React.CSSProperties['width'] } & SharedTextFieldProps diff --git a/packages/ui/src/fields/Textarea/Input.tsx b/packages/ui/src/fields/Textarea/Input.tsx index 9156c1f13..765aa7602 100644 --- a/packages/ui/src/fields/Textarea/Input.tsx +++ b/packages/ui/src/fields/Textarea/Input.tsx @@ -33,7 +33,6 @@ export const TextareaInput: React.FC = (props) => { showError, style, value, - width, } = props const { i18n } = useTranslation() @@ -49,10 +48,7 @@ export const TextareaInput: React.FC = (props) => { ] .filter(Boolean) .join(' ')} - style={{ - ...style, - width, - }} + style={style} > { const { + field, field: { - name, - admin: { className, description, placeholder, rows, rtl, style, width } = {}, + admin: { className, description, placeholder, rows, rtl } = {}, label, localized, maxLength, @@ -67,6 +68,8 @@ const TextareaFieldComponent: TextareaFieldClientComponent = (props) => { validate: memoizedValidate, }) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return ( { rows={rows} rtl={isRTL} showError={showError} - style={style} + style={styles} value={value} - width={width} /> ) } diff --git a/packages/ui/src/fields/Textarea/types.ts b/packages/ui/src/fields/Textarea/types.ts index 1d1258dbb..e53c92a7b 100644 --- a/packages/ui/src/fields/Textarea/types.ts +++ b/packages/ui/src/fields/Textarea/types.ts @@ -26,5 +26,4 @@ export type TextAreaInputProps = { readonly style?: React.CSSProperties readonly value?: string readonly valueToRender?: string - readonly width?: React.CSSProperties['width'] } diff --git a/packages/ui/src/fields/Upload/Input.tsx b/packages/ui/src/fields/Upload/Input.tsx index 5f3c12047..6fd0e87df 100644 --- a/packages/ui/src/fields/Upload/Input.tsx +++ b/packages/ui/src/fields/Upload/Input.tsx @@ -73,7 +73,6 @@ export type UploadInputProps = { readonly showError?: boolean readonly style?: React.CSSProperties readonly value?: (number | string)[] | (number | string) - readonly width?: React.CSSProperties['width'] } export function UploadInput(props: UploadInputProps) { @@ -102,7 +101,6 @@ export function UploadInput(props: UploadInputProps) { showError, style, value, - width, } = props const [populatedDocs, setPopulatedDocs] = React.useState< @@ -169,7 +167,7 @@ export function UploadInput(props: UploadInputProps) { } return false - }, [activeRelationTo, permissions, readOnly, allowCreate]) + }, [activeRelationTo, permissions, allowCreate]) const onChange = React.useCallback( (newValue) => { @@ -447,10 +445,7 @@ export function UploadInput(props: UploadInputProps) { .filter(Boolean) .join(' ')} id={`field-${path?.replace(/\./g, '__')}`} - style={{ - ...style, - width, - }} + style={style} > mergeFieldStyles(field), [field]) + return ( ) } diff --git a/packages/ui/src/fields/mergeFieldStyles.ts b/packages/ui/src/fields/mergeFieldStyles.ts new file mode 100644 index 000000000..c7885c5fc --- /dev/null +++ b/packages/ui/src/fields/mergeFieldStyles.ts @@ -0,0 +1,20 @@ +import type { ClientField } from 'payload' + +export const mergeFieldStyles = ( + field: ClientField | Omit, +): React.CSSProperties => ({ + ...(field?.admin?.style || {}), + ...(field?.admin?.width + ? { + '--field-width': field.admin.width, + } + : { + flex: '1 1 auto', + }), + // allow flex overrides to still take precedence over the fallback + ...(field?.admin?.style?.flex + ? { + flex: field.admin.style.flex, + } + : {}), +})