diff --git a/packages/payload/src/fields/config/types.ts b/packages/payload/src/fields/config/types.ts index 9a47a8108b..a8e1bbdb09 100644 --- a/packages/payload/src/fields/config/types.ts +++ b/packages/payload/src/fields/config/types.ts @@ -382,7 +382,16 @@ export type NamedTab = TabBase & { export type UnnamedTab = Omit & { interfaceName?: never - label: Record | string + /** + * Can be either: + * - A string, which will be used as the tab's label. + * - An object, where the key is the language code and the value is the label. + */ + label: + | { + [selectedLanguage: string]: string + } + | string localized?: never } diff --git a/packages/ui/src/elements/DeleteDocument/index.tsx b/packages/ui/src/elements/DeleteDocument/index.tsx index a22f98c5d8..cecb2b53b8 100644 --- a/packages/ui/src/elements/DeleteDocument/index.tsx +++ b/packages/ui/src/elements/DeleteDocument/index.tsx @@ -73,8 +73,8 @@ export const DeleteDocument: React.FC = (props) => { setDeleting(false) toggleModal(modalSlug) toast.success( - json.message || - t('general:titleDeleted', { label: getTranslation(singularLabel, i18n), title }), + t('general:titleDeleted', { label: getTranslation(singularLabel, i18n), title }) || + json.message, ) return router.push(`${admin}/collections/${collectionSlug}`) diff --git a/packages/ui/src/elements/EditMany/index.tsx b/packages/ui/src/elements/EditMany/index.tsx index 3df2a672e9..48611df60f 100644 --- a/packages/ui/src/elements/EditMany/index.tsx +++ b/packages/ui/src/elements/EditMany/index.tsx @@ -101,7 +101,7 @@ const SaveDraft: React.FC<{ action: string; disabled: boolean }> = ({ action, di export const EditMany: React.FC = (props) => { const { useModal } = facelessUIImport - const { collection: { slug, fields, labels: { plural } } = {}, collection, fieldMap } = props + const { collection: { slug, labels: { plural } } = {}, collection, fieldMap } = props const { permissions } = useAuth() const { closeModal } = useModal() @@ -114,8 +114,6 @@ export const EditMany: React.FC = (props) => { const [selected, setSelected] = useState([]) const { stringifyParams } = useSearchParams() const router = useRouter() - const { componentMap } = useComponentMap() - const [reducedFieldMap, setReducedFieldMap] = useState([]) const [initialState, setInitialState] = useState() const hasInitializedState = React.useRef(false) const { clearRouteCache } = useRouteCache() @@ -125,21 +123,6 @@ export const EditMany: React.FC = (props) => { const drawerSlug = `edit-${slug}` - React.useEffect(() => { - if (componentMap?.collections?.[slug]?.fieldMap) { - const fieldMap = componentMap.collections[slug].fieldMap - const reducedFieldMap = [] - fieldMap.map((field) => { - selected.map((selectedField) => { - if ('name' in field && field.name === selectedField.name) { - reducedFieldMap.push(field) - } - }) - }) - setReducedFieldMap(reducedFieldMap) - } - }, [componentMap.collections, fields, slug, selected]) - React.useEffect(() => { if (!hasInitializedState.current) { const getInitialState = async () => { @@ -228,13 +211,8 @@ export const EditMany: React.FC = (props) => { onSuccess={onSuccess} > - {reducedFieldMap.length === 0 ? null : ( - + {selected.length === 0 ? null : ( + )}
diff --git a/packages/ui/src/elements/FieldSelect/index.tsx b/packages/ui/src/elements/FieldSelect/index.tsx index 0a64b53b31..6f6b76a197 100644 --- a/packages/ui/src/elements/FieldSelect/index.tsx +++ b/packages/ui/src/elements/FieldSelect/index.tsx @@ -18,32 +18,52 @@ export type FieldSelectProps = { setSelected: (fields: FieldWithPath[]) => void } -const combineLabel = (prefix: JSX.Element, field: MappedField): JSX.Element => { +const combineLabel = ({ + customLabel, + field, + prefix, +}: { + customLabel?: string + field?: MappedField + prefix?: JSX.Element | string +}): JSX.Element => { + const CustomLabelToRender = + field && + 'CustomLabel' in field.fieldComponentProps && + field.fieldComponentProps.CustomLabel !== undefined + ? field.fieldComponentProps.CustomLabel + : null + const DefaultLabelToRender = + field && 'labelProps' in field.fieldComponentProps && field.fieldComponentProps.labelProps ? ( + + ) : null + + const LabelToRender = CustomLabelToRender || DefaultLabelToRender || customLabel + + if (!LabelToRender) return null + return ( - {prefix} - {prefix ? {' > '} : ''} - - {'CustomLabel' in field.fieldComponentProps && - field.fieldComponentProps.CustomLabel !== undefined ? ( - field.fieldComponentProps.CustomLabel - ) : ( - - )} - + {prefix && ( + + {prefix} + {' > '} + + )} + {LabelToRender} ) } -const reduceFields = ( - fieldMap: FieldMap, +const reduceFields = ({ + fieldMap, + labelPrefix = null, path = '', - labelPrefix: JSX.Element = null, -): { Label: JSX.Element; value: FieldWithPath }[] => { +}: { + fieldMap: FieldMap + labelPrefix?: JSX.Element | string + path?: string +}): { Label: JSX.Element; value: FieldWithPath }[] => { if (!fieldMap) { return [] } @@ -67,26 +87,27 @@ const reduceFields = ( ) { return [ ...fieldsToUse, - ...reduceFields( - field.fieldComponentProps.fieldMap, - createNestedClientFieldPath(path, field), - combineLabel(labelPrefix, field), - ), + ...reduceFields({ + fieldMap: field.fieldComponentProps.fieldMap, + labelPrefix: combineLabel({ field, prefix: labelPrefix }), + path: createNestedClientFieldPath(path, field), + }), ] } - if (field.type === 'tabs' && 'fieldMap' in field.fieldComponentProps) { + if (field.type === 'tabs' && 'tabs' in field.fieldComponentProps) { return [ ...fieldsToUse, - ...field.fieldComponentProps.fieldMap.reduce((tabFields, tab) => { - if ('fieldMap' in tab.fieldComponentProps) { + ...field.fieldComponentProps.tabs.reduce((tabFields, tab) => { + if ('fieldMap' in tab) { + const isNamedTab = 'name' in tab && tab.name return [ ...tabFields, - ...reduceFields( - tab.fieldComponentProps.fieldMap, - 'name' in tab && tab.name ? createNestedClientFieldPath(path, field) : path, - combineLabel(labelPrefix, field), - ), + ...reduceFields({ + fieldMap: tab.fieldMap, + labelPrefix, + path: isNamedTab ? createNestedClientFieldPath(path, field) : path, + }), ] } }, []), @@ -94,7 +115,7 @@ const reduceFields = ( } const formattedField = { - label: combineLabel(labelPrefix, field), + label: combineLabel({ field, prefix: labelPrefix }), value: { ...field, path: createNestedClientFieldPath(path, field), @@ -107,7 +128,7 @@ const reduceFields = ( export const FieldSelect: React.FC = ({ fieldMap, setSelected }) => { const { t } = useTranslation() - const [options] = useState(() => reduceFields(fieldMap)) + const [options] = useState(() => reduceFields({ fieldMap })) const { dispatchFields, getFields } = useForm() @@ -140,7 +161,17 @@ export const FieldSelect: React.FC = ({ fieldMap, setSelected return (
- + { + if (typeof option.value === 'object' && 'path' in option.value) { + return String(option.value.path) + } + return String(option.value) + }} + isMulti + onChange={handleChange} + options={options} + />
) } diff --git a/packages/ui/src/elements/ReactSelect/MultiValue/index.tsx b/packages/ui/src/elements/ReactSelect/MultiValue/index.tsx index 670c3870d2..76c138f5eb 100644 --- a/packages/ui/src/elements/ReactSelect/MultiValue/index.tsx +++ b/packages/ui/src/elements/ReactSelect/MultiValue/index.tsx @@ -2,6 +2,7 @@ import type { MultiValueProps } from 'react-select' import React from 'react' import { components as SelectComponents } from 'react-select' +import { v4 as uuid } from 'uuid' import type { Option } from '../types.js' @@ -20,7 +21,7 @@ export const MultiValue: React.FC> = (props) => { } = props const { attributes, isDragging, listeners, setNodeRef, transform } = useDraggableSortable({ - id: value.toString(), + id: typeof value === 'object' && 'path' in value ? String(value.path) : uuid(), disabled: !isSortable, }) diff --git a/packages/ui/src/elements/ReactSelect/index.tsx b/packages/ui/src/elements/ReactSelect/index.tsx index 33c02cde7a..902de20d22 100644 --- a/packages/ui/src/elements/ReactSelect/index.tsx +++ b/packages/ui/src/elements/ReactSelect/index.tsx @@ -38,6 +38,7 @@ const SelectAdapter: React.FC = (props) => { customProps, disabled = false, filterOption = undefined, + getOptionValue, isClearable = true, isCreatable, isLoading, @@ -78,6 +79,7 @@ const SelectAdapter: React.FC = (props) => { ...components, }} filterOption={filterOption} + getOptionValue={getOptionValue} isClearable={isClearable} isDisabled={disabled} isSearchable={isSearchable} diff --git a/packages/ui/src/elements/ReactSelect/types.ts b/packages/ui/src/elements/ReactSelect/types.ts index 8d6b06f989..d548af9eb2 100644 --- a/packages/ui/src/elements/ReactSelect/types.ts +++ b/packages/ui/src/elements/ReactSelect/types.ts @@ -60,6 +60,11 @@ export type Props = { search: string, ) => boolean) | undefined + getOptionValue?: ReactSelectStateManagerProps< + Option, + boolean, + GroupBase