feat: specifies component names for arrays/collapsibles, simplifies threaded data
This commit is contained in:
@@ -1,23 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Props } from './types';
|
|
||||||
import { useWatchForm } from '../Form/context';
|
|
||||||
|
|
||||||
export const CollapsibleLabel: React.FC<Props> = (props) => {
|
|
||||||
const {
|
|
||||||
path,
|
|
||||||
fallback,
|
|
||||||
label,
|
|
||||||
rowNumber,
|
|
||||||
} = props;
|
|
||||||
const { getDataByPath, getSiblingData, getData } = useWatchForm();
|
|
||||||
const formData = getData();
|
|
||||||
const collapsibleData = getDataByPath(path) || getSiblingData(path);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
{(typeof label === 'function')
|
|
||||||
? label({ collapsibleData, index: rowNumber, formData, fallback })
|
|
||||||
: (label || fallback)}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Data } from '../Form/types';
|
|
||||||
|
|
||||||
export type CollapsibleLabel = (props: {
|
|
||||||
formData: Data,
|
|
||||||
collapsibleData: Data,
|
|
||||||
index?: number,
|
|
||||||
fallback: string,
|
|
||||||
}) => React.ReactNode;
|
|
||||||
|
|
||||||
export type Props = {
|
|
||||||
fallback: string;
|
|
||||||
path: string;
|
|
||||||
label?: CollapsibleLabel;
|
|
||||||
rowNumber?: number;
|
|
||||||
}
|
|
||||||
60
src/admin/components/forms/RowLabel/index.tsx
Normal file
60
src/admin/components/forms/RowLabel/index.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { isComponent, Props } from './types';
|
||||||
|
import { useWatchForm } from '../Form/context';
|
||||||
|
|
||||||
|
const baseClass = 'row-label';
|
||||||
|
|
||||||
|
export const RowLabel: React.FC<Props> = (props) => {
|
||||||
|
const {
|
||||||
|
path,
|
||||||
|
fallback,
|
||||||
|
label,
|
||||||
|
rowNumber,
|
||||||
|
className,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const { getDataByPath, getSiblingData } = useWatchForm();
|
||||||
|
const collapsibleData = getSiblingData(path);
|
||||||
|
const arrayData = getDataByPath(path);
|
||||||
|
const data = arrayData || collapsibleData;
|
||||||
|
|
||||||
|
if (isComponent(label)) {
|
||||||
|
const Label = label;
|
||||||
|
return (
|
||||||
|
<Label
|
||||||
|
data={data}
|
||||||
|
path={path}
|
||||||
|
fallback={fallback}
|
||||||
|
index={rowNumber}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={[
|
||||||
|
baseClass,
|
||||||
|
className,
|
||||||
|
].filter(Boolean).join(' ')}
|
||||||
|
>
|
||||||
|
{typeof label === 'function' ? label({
|
||||||
|
data,
|
||||||
|
path,
|
||||||
|
fallback,
|
||||||
|
index: rowNumber,
|
||||||
|
}) : label}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallback) {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{fallback}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
27
src/admin/components/forms/RowLabel/types.ts
Normal file
27
src/admin/components/forms/RowLabel/types.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Data } from '../Form/types';
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
fallback: string;
|
||||||
|
path: string;
|
||||||
|
label?: RowLabel;
|
||||||
|
rowNumber?: number;
|
||||||
|
className?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RowLabelArgs = {
|
||||||
|
data: Data,
|
||||||
|
path: string,
|
||||||
|
index: number,
|
||||||
|
fallback: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RowLabelFunction = (args: RowLabelArgs) => string
|
||||||
|
|
||||||
|
export type RowLabelComponent = React.ComponentType<RowLabelArgs>
|
||||||
|
|
||||||
|
export type RowLabel = string | RowLabelFunction | RowLabelComponent
|
||||||
|
|
||||||
|
export function isComponent(label: RowLabel): label is RowLabelComponent {
|
||||||
|
return React.isValidElement(label);
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ import { usePreferences } from '../../../utilities/Preferences';
|
|||||||
import { ArrayAction } from '../../../elements/ArrayAction';
|
import { ArrayAction } from '../../../elements/ArrayAction';
|
||||||
import { scrollToID } from '../../../../utilities/scrollToID';
|
import { scrollToID } from '../../../../utilities/scrollToID';
|
||||||
import HiddenInput from '../HiddenInput';
|
import HiddenInput from '../HiddenInput';
|
||||||
import { CollapsibleLabel } from '../../CollapsibleLabel';
|
import { RowLabel } from '../../RowLabel';
|
||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
|||||||
},
|
},
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const CollapsibleLabelFromProps = components?.CollapsibleLabel || undefined;
|
const RowLabelFromProps = components?.RowLabel || undefined;
|
||||||
|
|
||||||
const path = pathFromProps || name;
|
const path = pathFromProps || name;
|
||||||
|
|
||||||
@@ -287,9 +287,9 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
|||||||
key={row.id}
|
key={row.id}
|
||||||
dragHandleProps={providedDrag.dragHandleProps}
|
dragHandleProps={providedDrag.dragHandleProps}
|
||||||
header={(
|
header={(
|
||||||
<CollapsibleLabel
|
<RowLabel
|
||||||
path={`${path}.${i}`}
|
path={`${path}.${i}`}
|
||||||
label={CollapsibleLabelFromProps}
|
label={RowLabelFromProps}
|
||||||
fallback={`${labels.singular} ${String(rowNumber).padStart(2, '0')}`}
|
fallback={`${labels.singular} ${String(rowNumber).padStart(2, '0')}`}
|
||||||
rowNumber={rowNumber}
|
rowNumber={rowNumber}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { DocumentPreferences } from '../../../../../preferences/types';
|
|||||||
import { useDocumentInfo } from '../../../utilities/DocumentInfo';
|
import { useDocumentInfo } from '../../../utilities/DocumentInfo';
|
||||||
import FieldDescription from '../../FieldDescription';
|
import FieldDescription from '../../FieldDescription';
|
||||||
import { getFieldPath } from '../getFieldPath';
|
import { getFieldPath } from '../getFieldPath';
|
||||||
import { CollapsibleLabel } from '../../CollapsibleLabel';
|
import { RowLabel } from '../../RowLabel';
|
||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ const CollapsibleField: React.FC<Props> = (props) => {
|
|||||||
className,
|
className,
|
||||||
].filter(Boolean).join(' ')}
|
].filter(Boolean).join(' ')}
|
||||||
header={(
|
header={(
|
||||||
<CollapsibleLabel
|
<RowLabel
|
||||||
path={path}
|
path={path}
|
||||||
fallback={label}
|
fallback={label}
|
||||||
label={CollapsibleLabelFromProps}
|
label={CollapsibleLabelFromProps}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import joi from 'joi';
|
import joi from 'joi';
|
||||||
import { componentSchema } from '../../utilities/componentSchema';
|
import { componentSchema } from '../../utilities/componentSchema';
|
||||||
|
|
||||||
|
export const baseAdminComponentFields = joi.object().keys({
|
||||||
|
Cell: componentSchema,
|
||||||
|
Field: componentSchema,
|
||||||
|
Filter: componentSchema,
|
||||||
|
}).default({});
|
||||||
|
|
||||||
export const baseAdminFields = joi.object().keys({
|
export const baseAdminFields = joi.object().keys({
|
||||||
description: joi.alternatives().try(
|
description: joi.alternatives().try(
|
||||||
joi.string(),
|
joi.string(),
|
||||||
@@ -15,11 +21,7 @@ export const baseAdminFields = joi.object().keys({
|
|||||||
hidden: joi.boolean().default(false),
|
hidden: joi.boolean().default(false),
|
||||||
disabled: joi.boolean().default(false),
|
disabled: joi.boolean().default(false),
|
||||||
condition: joi.func(),
|
condition: joi.func(),
|
||||||
components: joi.object().keys({
|
components: baseAdminComponentFields,
|
||||||
Cell: componentSchema,
|
|
||||||
Field: componentSchema,
|
|
||||||
Filter: componentSchema,
|
|
||||||
}).default({}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const baseField = joi.object().keys({
|
export const baseField = joi.object().keys({
|
||||||
@@ -185,7 +187,7 @@ export const collapsible = baseField.keys({
|
|||||||
type: joi.string().valid('collapsible').required(),
|
type: joi.string().valid('collapsible').required(),
|
||||||
fields: joi.array().items(joi.link('#field')),
|
fields: joi.array().items(joi.link('#field')),
|
||||||
admin: baseAdminFields.keys({
|
admin: baseAdminFields.keys({
|
||||||
components: joi.object().keys({
|
components: baseAdminComponentFields.keys({
|
||||||
CollapsibleLabel: componentSchema,
|
CollapsibleLabel: componentSchema,
|
||||||
}).default({}),
|
}).default({}),
|
||||||
}).default({}),
|
}).default({}),
|
||||||
@@ -241,8 +243,8 @@ export const array = baseField.keys({
|
|||||||
joi.func(),
|
joi.func(),
|
||||||
),
|
),
|
||||||
admin: baseAdminFields.keys({
|
admin: baseAdminFields.keys({
|
||||||
components: joi.object().keys({
|
components: baseAdminComponentFields.keys({
|
||||||
CollapsibleLabel: componentSchema,
|
RowLabel: componentSchema,
|
||||||
}).default({}),
|
}).default({}),
|
||||||
}).default({}),
|
}).default({}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { ConditionalDateProps } from '../../admin/components/elements/DatePicker
|
|||||||
import { Description } from '../../admin/components/forms/FieldDescription/types';
|
import { Description } from '../../admin/components/forms/FieldDescription/types';
|
||||||
import { User } from '../../auth';
|
import { User } from '../../auth';
|
||||||
import { Payload } from '../..';
|
import { Payload } from '../..';
|
||||||
import { CollapsibleLabel } from '../../admin/components/forms/CollapsibleLabel/types';
|
import { RowLabel } from '../../admin/components/forms/RowLabel/types';
|
||||||
|
|
||||||
export type FieldHookArgs<T extends TypeWithID = any, P = any, S = any> = {
|
export type FieldHookArgs<T extends TypeWithID = any, P = any, S = any> = {
|
||||||
/** The data passed to update the document within create and update operations, and the full document itself in the afterRead hook. */
|
/** The data passed to update the document within create and update operations, and the full document itself in the afterRead hook. */
|
||||||
@@ -193,8 +193,8 @@ export type CollapsibleField = Omit<FieldBase, 'name'> & {
|
|||||||
admin?: Admin & {
|
admin?: Admin & {
|
||||||
initCollapsed?: boolean | false;
|
initCollapsed?: boolean | false;
|
||||||
components?: {
|
components?: {
|
||||||
CollapsibleLabel?: CollapsibleLabel
|
CollapsibleLabel?: RowLabel
|
||||||
}
|
} & Admin['components']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,8 +342,8 @@ export type ArrayField = FieldBase & {
|
|||||||
admin?: Admin & {
|
admin?: Admin & {
|
||||||
initCollapsed?: boolean | false;
|
initCollapsed?: boolean | false;
|
||||||
components?: {
|
components?: {
|
||||||
CollapsibleLabel?: CollapsibleLabel
|
RowLabel?: RowLabel
|
||||||
}
|
} & Admin['components']
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CollapsibleLabel } from '../../../../src/admin/components/forms/CollapsibleLabel/types';
|
import { RowLabelComponent } from '../../../../src/admin/components/forms/RowLabel/types';
|
||||||
|
|
||||||
export const ArrayCollapsibleLabel: CollapsibleLabel = (props) => {
|
export const ArrayRowLabel: RowLabelComponent = (props) => {
|
||||||
const { collapsibleData, fallback } = props;
|
const { data, fallback } = props;
|
||||||
return <span style={{ color: 'hotpink' }}>{collapsibleData.title || fallback}</span>;
|
return <span style={{ color: 'hotpink' }}>{data.title || fallback}</span>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CollectionConfig } from '../../../../src/collections/config/types';
|
import type { CollectionConfig } from '../../../../src/collections/config/types';
|
||||||
import { ArrayCollapsibleLabel } from './HeaderComponent';
|
import { ArrayRowLabel } from './HeaderComponent';
|
||||||
|
|
||||||
export const arrayDefaultValue = [
|
export const arrayDefaultValue = [
|
||||||
{ text: 'row one' },
|
{ text: 'row one' },
|
||||||
@@ -85,7 +85,7 @@ const ArrayFields: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'array',
|
type: 'array',
|
||||||
name: 'collapsibleLabelAsFunction',
|
name: 'rowLabelAsFunction',
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'title',
|
name: 'title',
|
||||||
@@ -93,15 +93,15 @@ const ArrayFields: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
admin: {
|
admin: {
|
||||||
description: 'Collapsible labels rendered from a function.',
|
description: 'Row labels rendered from a function.',
|
||||||
components: {
|
components: {
|
||||||
CollapsibleLabel: ({ collapsibleData, fallback }) => collapsibleData.title || fallback,
|
RowLabel: ({ data, fallback }) => data.title || fallback,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'array',
|
type: 'array',
|
||||||
name: 'rowHeaderAsComponent',
|
name: 'rowLabelAsComponent',
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'title',
|
name: 'title',
|
||||||
@@ -109,9 +109,9 @@ const ArrayFields: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
admin: {
|
admin: {
|
||||||
description: 'Collapsible labels rendered as components.',
|
description: 'Row labels rendered as react components.',
|
||||||
components: {
|
components: {
|
||||||
CollapsibleLabel: ArrayCollapsibleLabel,
|
RowLabel: ArrayRowLabel,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CollapsibleLabel } from '../../../../src/admin/components/forms/CollapsibleLabel/types';
|
import { RowLabel } from '../../../../src/admin/components/forms/RowLabel/types';
|
||||||
|
|
||||||
export const CollapsibleLabelComponent: CollapsibleLabel = (props) => {
|
export const CollapsibleLabelComponent: RowLabel = (props) => {
|
||||||
const { collapsibleData, fallback } = props;
|
const { data, fallback } = props;
|
||||||
return <span style={{ color: 'coral' }}>{collapsibleData.componentTitleField || fallback}</span>;
|
return <span style={{ color: 'coral' }}>{data.componentTitleField || fallback}</span>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const CollapsibleFields: CollectionConfig = {
|
|||||||
description: 'Collapsible label rendered from a function.',
|
description: 'Collapsible label rendered from a function.',
|
||||||
initCollapsed: true,
|
initCollapsed: true,
|
||||||
components: {
|
components: {
|
||||||
CollapsibleLabel: ({ collapsibleData }) => collapsibleData.functionTitleField || 'Untitled',
|
CollapsibleLabel: ({ data }) => data.functionTitleField || 'Untitled',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
|
|||||||
Reference in New Issue
Block a user