feat: enhances rich text upload with custom field API

* feat: adds admin.upload.collections[collection-name].fields to the RTE to save specific data on upload elements

* chore: renames flatten to unflatten in reduceFieldsToValues, disables automatic arrow function return in eslint

* docs: adds documentation for upload.collections[collection-name].fields feature

* feat: adds recursion to richText field to populate relationship and upload nested fields

* chore: removes unused css

* fix: import path for createRichTextRelationshipPromise

* docs: updates docs to include images for the RTE upload docs
This commit is contained in:
Jarrod Flesch
2022-01-21 10:15:51 -05:00
committed by GitHub
parent d07bb932ca
commit 0e4eb906f2
23 changed files with 885 additions and 259 deletions

View File

@@ -0,0 +1,54 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import { Collection } from '../../collections/config/types';
import { Payload } from '../..';
import { RichTextField, Field } from '../config/types';
import { PayloadRequest } from '../../express/types';
type Arguments = {
data: unknown
overrideAccess?: boolean
depth: number
currentDepth?: number
payload: Payload
field: RichTextField
req: PayloadRequest
showHiddenFields: boolean
}
export const populate = async ({
id,
collection,
data,
overrideAccess,
depth,
currentDepth,
payload,
req,
showHiddenFields,
}: Omit<Arguments, 'field'> & {
id: string,
field: Field
collection: Collection
}): Promise<void> => {
let dataRef = data as Record<string, unknown>;
const doc = await payload.operations.collections.findByID({
req: {
...req,
payloadAPI: 'local',
},
collection,
id,
currentDepth: currentDepth + 1,
overrideAccess,
disableErrors: true,
depth,
showHiddenFields,
});
if (doc) {
dataRef = doc;
} else {
dataRef = null;
}
};

View File

@@ -0,0 +1,183 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import { Payload } from '../..';
import { Field, fieldHasSubFields, fieldIsArrayType, fieldAffectsData } from '../config/types';
import { PayloadRequest } from '../../express/types';
import { populate } from './populate';
import { recurseRichText } from './relationshipPromise';
type NestedRichTextFieldsArgs = {
promises: Promise<void>[]
data: unknown
fields: Field[]
req: PayloadRequest
payload: Payload
overrideAccess: boolean
depth: number
currentDepth?: number
showHiddenFields: boolean
}
export const recurseNestedFields = ({
promises,
data,
fields,
req,
payload,
overrideAccess = false,
depth,
currentDepth = 0,
showHiddenFields,
}: NestedRichTextFieldsArgs): void => {
fields.forEach((field) => {
if (field.type === 'relationship' || field.type === 'upload') {
if (field.type === 'relationship') {
if (field.hasMany && Array.isArray(data[field.name])) {
if (Array.isArray(field.relationTo)) {
data[field.name].forEach(({ relationTo, value }, i) => {
const collection = payload.collections[relationTo];
if (collection) {
promises.push(populate({
id: value,
field,
collection,
data: data[field.name][i],
overrideAccess,
depth,
currentDepth,
payload,
req,
showHiddenFields,
}));
}
});
} else {
data[field.name].forEach((id, i) => {
const collection = payload.collections[field.relationTo as string];
if (collection) {
promises.push(populate({
id,
field,
collection,
data: data[field.name][i],
overrideAccess,
depth,
currentDepth,
payload,
req,
showHiddenFields,
}));
}
});
}
} else if (Array.isArray(field.relationTo) && data[field.name]?.value && data[field.name]?.relationTo) {
const collection = payload.collections[data[field.name].relationTo];
promises.push(populate({
id: data[field.name].value,
field,
collection,
data: data[field.name].value,
overrideAccess,
depth,
currentDepth,
payload,
req,
showHiddenFields,
}));
}
} else if (typeof data[field.name] !== undefined) {
const collection = payload.collections[field.relationTo];
promises.push(populate({
id: data[field.name],
field,
collection,
data: data[field.name],
overrideAccess,
depth,
currentDepth,
payload,
req,
showHiddenFields,
}));
}
} else if (fieldHasSubFields(field) && !fieldIsArrayType(field)) {
if (fieldAffectsData(field) && typeof data[field.name] === 'object') {
recurseNestedFields({
promises,
data: data[field.name],
fields: field.fields,
req,
payload,
overrideAccess,
depth,
currentDepth,
showHiddenFields,
});
} else {
recurseNestedFields({
promises,
data,
fields: field.fields,
req,
payload,
overrideAccess,
depth,
currentDepth,
showHiddenFields,
});
}
} else if (Array.isArray(data[field.name])) {
if (field.type === 'blocks') {
data[field.name].forEach((row, i) => {
const block = field.blocks.find(({ slug }) => slug === row?.blockType);
if (block) {
recurseNestedFields({
promises,
data: data[field.name][i],
fields: block.fields,
req,
payload,
overrideAccess,
depth,
currentDepth,
showHiddenFields,
});
}
});
}
if (field.type === 'array') {
data[field.name].forEach((_, i) => {
recurseNestedFields({
promises,
data: data[field.name][i],
fields: field.fields,
req,
payload,
overrideAccess,
depth,
currentDepth,
showHiddenFields,
});
});
}
}
if (field.type === 'richText' && Array.isArray(data[field.name])) {
data[field.name].forEach((node) => {
if (Array.isArray(node.children)) {
recurseRichText({
req,
children: node.children,
payload,
overrideAccess,
depth,
currentDepth,
field,
promises,
showHiddenFields,
});
}
});
}
});
};

View File

@@ -0,0 +1,120 @@
import { Payload } from '../..';
import { RichTextField } from '../config/types';
import { PayloadRequest } from '../../express/types';
import { recurseNestedFields } from './recurseNestedFields';
import { populate } from './populate';
type Arguments = {
data: unknown
overrideAccess?: boolean
depth: number
currentDepth?: number
payload: Payload
field: RichTextField
req: PayloadRequest
showHiddenFields: boolean
}
type RecurseRichTextArgs = {
children: unknown[]
overrideAccess: boolean
depth: number
currentDepth: number
payload: Payload
field: RichTextField
req: PayloadRequest
promises: Promise<void>[]
showHiddenFields: boolean
}
export const recurseRichText = ({
req,
children,
payload,
overrideAccess = false,
depth,
currentDepth = 0,
field,
promises,
showHiddenFields,
}: RecurseRichTextArgs): void => {
if (Array.isArray(children)) {
(children as any[]).forEach((element) => {
const collection = payload.collections[element?.relationTo];
if ((element.type === 'relationship' || element.type === 'upload')
&& element?.value?.id
&& collection
&& (depth && currentDepth <= depth)) {
if (element.type === 'upload' && Array.isArray(field.admin?.upload?.collections?.[element?.relationTo]?.fields)) {
recurseNestedFields({
promises,
data: element.fields || {},
fields: field.admin.upload.collections[element.relationTo].fields,
req,
payload,
overrideAccess,
depth,
currentDepth,
showHiddenFields,
});
}
promises.push(populate({
req,
id: element.value.id,
data: element.value,
overrideAccess,
depth,
currentDepth,
payload,
field,
collection,
showHiddenFields,
}));
}
if (element?.children) {
recurseRichText({
req,
children: element.children,
payload,
overrideAccess,
depth,
currentDepth,
field,
promises,
showHiddenFields,
});
}
});
}
};
const richTextRelationshipPromise = ({
req,
data,
payload,
overrideAccess,
depth,
currentDepth,
field,
showHiddenFields,
}: Arguments) => async (): Promise<void> => {
const promises = [];
recurseRichText({
req,
children: data[field.name],
payload,
overrideAccess,
depth,
currentDepth,
field,
promises,
showHiddenFields,
});
await Promise.all(promises);
};
export default richTextRelationshipPromise;