Compare commits

...

2 Commits

Author SHA1 Message Date
James
fff3c4c0ba 0.0.25 2020-07-23 12:08:07 -04:00
James
2692cdad20 implements a test of react-window to evaluate its performance impact 2020-07-23 12:07:59 -04:00
10 changed files with 246 additions and 192 deletions

View File

@@ -0,0 +1,48 @@
const fields = [];
let i = 0;
for (i; i < 30; i += 1) {
fields.push({
name: `array${i}`,
type: 'array',
label: `array${i}`,
fields: [
{
name: `number${i}`,
type: 'number',
label: 'Number Field',
},
{
name: `text${i}`,
type: 'text',
label: 'Text Field',
},
],
});
fields.push({
name: `text${i}`,
type: 'text',
label: `Text ${i}`,
});
}
// for (i; i < 1000; i += 1) {
// fields.push({
// name: `number${i}`,
// type: 'number',
// label: 'Number Field',
// });
// }
const LotsOfFields = {
slug: 'lots-of-fields',
labels: {
singular: 'Lots of Fields Test',
plural: 'Lots of Fields Tests',
},
fields,
};
module.exports = LotsOfFields;

View File

@@ -9,6 +9,7 @@ const Blocks = require('./collections/Blocks');
const HiddenFields = require('./collections/HiddenFields');
const Hooks = require('./collections/Hooks');
const Localized = require('./collections/Localized');
const LotsOfFields = require('./collections/LotsOfFields');
const LocalizedArray = require('./collections/LocalizedArray');
const Media = require('./collections/Media');
const NestedArrays = require('./collections/NestedArrays');
@@ -46,6 +47,7 @@ module.exports = {
Hooks,
Localized,
LocalizedArray,
LotsOfFields,
Media,
NestedArrays,
Preview,

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/payload",
"version": "0.0.24",
"version": "0.0.25",
"description": "CMS and Application Framework",
"license": "ISC",
"author": "Payload CMS LLC",
@@ -96,6 +96,7 @@
"react-router-dom": "^5.1.2",
"react-router-navigation-prompt": "^1.8.11",
"react-select": "^3.0.8",
"react-window": "^1.6.0-alpha.1",
"sanitize-filename": "^1.6.3",
"sass-loader": "7.1.0",
"sharp": "^0.25.2",

View File

@@ -1,35 +0,0 @@
RenderFieldsNotes
slides.0.meta.title
slides.1.heroInfo.title
fields: [
{
name: slides,
type: array,
fields: [
{
type: group,
name: meta,
fields: [
{
name: title,
type: text,
component: require.resolve('/aslifjawelifjaew)
}
]
},
{
type: group,
name: heroInfo,
fields: [
{
name: title,
type: text,
component: require.resolve('/aslifjawelifjaew)
}
]
}
]
}
]

View File

@@ -1,5 +1,6 @@
import React, { createContext, useContext } from 'react';
import PropTypes from 'prop-types';
import { DynamicSizeList as List } from 'react-window';
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
import './index.scss';
@@ -25,74 +26,88 @@ const RenderFields = (props) => {
const customComponentsPath = customComponentsPathFromProps || customComponentsPathFromContext;
const operation = operationFromProps || operationFromContext;
const renderRow = ({ index, style }) => {
const field = fieldSchema[index];
if (!field?.hidden && field?.admin?.disabled !== true) {
if ((filter && typeof filter === 'function' && filter(field)) || !filter) {
const FieldComponent = field?.admin?.hidden ? fieldTypes.hidden : fieldTypes[field.type];
let initialFieldData;
let fieldPermissions = permissions[field.name];
if (!field.name) {
initialFieldData = initialData;
fieldPermissions = permissions;
} else if (initialData?.[field.name] !== undefined) {
initialFieldData = initialData[field.name];
}
let { admin: { readOnly } = {} } = field;
if (readOnlyOverride) readOnly = true;
if (permissions?.[field?.name]?.read?.permission !== false) {
if (permissions?.[field?.name]?.[operation]?.permission === false) {
readOnly = true;
}
if (FieldComponent) {
return (
<div style={{ ...style, top: 200 * index }}>
<RenderCustomComponent
key={field.name || `field-${index}`}
path={`${customComponentsPath}${field.name ? `${field.name}.field` : ''}`}
DefaultComponent={FieldComponent}
componentProps={{
...field,
path: field.path || field.name,
fieldTypes,
initialData: initialFieldData,
admin: {
...(field.admin || {}),
readOnly,
},
permissions: fieldPermissions,
}}
/>
</div>
);
}
return (
<div
style={style}
className="missing-field"
key={index}
>
No matched field found for
{' '}
&quot;
{field.label}
&quot;
</div>
);
}
}
return null;
}
return null;
};
if (fieldSchema) {
return (
<RenderedFieldContext.Provider value={{ customComponentsPath, operation }}>
{fieldSchema.map((field, i) => {
if (!field?.hidden && field?.admin?.disabled !== true) {
if ((filter && typeof filter === 'function' && filter(field)) || !filter) {
const FieldComponent = field?.admin?.hidden ? fieldTypes.hidden : fieldTypes[field.type];
let initialFieldData;
let fieldPermissions = permissions[field.name];
if (!field.name) {
initialFieldData = initialData;
fieldPermissions = permissions;
} else if (initialData?.[field.name] !== undefined) {
initialFieldData = initialData[field.name];
}
let { admin: { readOnly } = {} } = field;
if (readOnlyOverride) readOnly = true;
if (permissions?.[field?.name]?.read?.permission !== false) {
if (permissions?.[field?.name]?.[operation]?.permission === false) {
readOnly = true;
}
if (FieldComponent) {
return (
<RenderCustomComponent
key={field.name || `field-${i}`}
path={`${customComponentsPath}${field.name ? `${field.name}.field` : ''}`}
DefaultComponent={FieldComponent}
componentProps={{
...field,
path: field.path || field.name,
fieldTypes,
initialData: initialFieldData,
admin: {
...(field.admin || {}),
readOnly,
},
permissions: fieldPermissions,
}}
/>
);
}
return (
<div
className="missing-field"
key={i}
>
No matched field found for
{' '}
&quot;
{field.label}
&quot;
</div>
);
}
}
return null;
}
return null;
})}
<List
data={fieldSchema}
itemCount={fieldSchema.length}
height={600}
width={750}
>
{renderRow}
</List>
</RenderedFieldContext.Provider>
);
}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { ScrollInfoProvider } from '@faceless-ui/scroll-info';

View File

@@ -18,6 +18,9 @@ import LeaveWithoutSaving from '../../../modals/LeaveWithoutSaving';
import './index.scss';
const mainFieldFilter = (field) => (!field?.admin?.position || (field?.admin?.position !== 'sidebar'));
const sidebarFieldFilter = (field) => field?.admin?.position === 'sidebar';
const { serverURL, routes: { api, admin } } = config;
const baseClass = 'collection-edit';
@@ -26,7 +29,7 @@ const DefaultEditView = (props) => {
const { params: { id } = {} } = useRouteMatch();
const {
collection, isEditing, data, onSave, permissions, isLoading,
collection, isEditing, data, onSave, permissions,
} = props;
const {
@@ -66,28 +69,23 @@ const DefaultEditView = (props) => {
<Eyebrow />
<LeaveWithoutSaving />
<div className={`${baseClass}__edit`}>
{isLoading && (
<Loading />
)}
{!isLoading && (
<React.Fragment>
<header className={`${baseClass}__header`}>
<h1>
<RenderTitle {...{ data, useAsTitle, fallback: '[Untitled]' }} />
</h1>
</header>
<RenderFields
operation={isEditing ? 'update' : 'create'}
readOnly={!hasSavePermission}
permissions={permissions.fields}
filter={(field) => (!field?.admin?.position || (field?.admin?.position !== 'sidebar'))}
fieldTypes={fieldTypes}
fieldSchema={fields}
initialData={data}
customComponentsPath={`${slug}.fields.`}
/>
</React.Fragment>
)}
<React.Fragment>
<header className={`${baseClass}__header`}>
<h1>
<RenderTitle {...{ data, useAsTitle, fallback: '[Untitled]' }} />
</h1>
</header>
<RenderFields
operation={isEditing ? 'update' : 'create'}
readOnly={!hasSavePermission}
permissions={permissions.fields}
filter={mainFieldFilter}
fieldTypes={fieldTypes}
fieldSchema={fields}
initialData={data}
customComponentsPath={`${slug}.fields.`}
/>
</React.Fragment>
</div>
</div>
<div className={`${baseClass}__sidebar`}>
@@ -134,48 +132,46 @@ const DefaultEditView = (props) => {
</a>
</div>
)}
{!isLoading && (
<React.Fragment>
<div className={`${baseClass}__sidebar-fields`}>
<RenderFields
operation={isEditing ? 'update' : 'create'}
readOnly={!hasSavePermission}
permissions={permissions.fields}
filter={(field) => field?.admin?.position === 'sidebar'}
position="sidebar"
fieldTypes={fieldTypes}
fieldSchema={fields}
initialData={data}
customComponentsPath={`${slug}.fields.`}
/>
</div>
{isEditing && (
<ul className={`${baseClass}__meta`}>
<li>
<div className={`${baseClass}__label`}>ID</div>
<div>{id}</div>
</li>
{timestamps && (
<React.Fragment>
{data.updatedAt && (
<li>
<div className={`${baseClass}__label`}>Last Modified</div>
<div>{format(new Date(data.updatedAt), 'MMMM do yyyy, h:mma')}</div>
</li>
)}
{data.createdAt && (
<li>
<div className={`${baseClass}__label`}>Created</div>
<div>{format(new Date(data.createdAt), 'MMMM do yyyy, h:mma')}</div>
</li>
)}
</React.Fragment>
)}
<React.Fragment>
<div className={`${baseClass}__sidebar-fields`}>
<RenderFields
operation={isEditing ? 'update' : 'create'}
readOnly={!hasSavePermission}
permissions={permissions.fields}
filter={sidebarFieldFilter}
position="sidebar"
fieldTypes={fieldTypes}
fieldSchema={fields}
initialData={data}
customComponentsPath={`${slug}.fields.`}
/>
</div>
{isEditing && (
<ul className={`${baseClass}__meta`}>
<li>
<div className={`${baseClass}__label`}>ID</div>
<div>{id}</div>
</li>
{timestamps && (
<React.Fragment>
{data.updatedAt && (
<li>
<div className={`${baseClass}__label`}>Last Modified</div>
<div>{format(new Date(data.updatedAt), 'MMMM do yyyy, h:mma')}</div>
</li>
)}
{data.createdAt && (
<li>
<div className={`${baseClass}__label`}>Created</div>
<div>{format(new Date(data.createdAt), 'MMMM do yyyy, h:mma')}</div>
</li>
)}
</React.Fragment>
)}
</ul>
)}
</React.Fragment>
)}
</ul>
)}
</React.Fragment>
</div>
</div>
</Form>
@@ -185,12 +181,10 @@ const DefaultEditView = (props) => {
DefaultEditView.defaultProps = {
isEditing: false,
isLoading: true,
data: undefined,
};
DefaultEditView.propTypes = {
isLoading: PropTypes.bool,
collection: PropTypes.shape({
labels: PropTypes.shape({
plural: PropTypes.string,

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import { useRouteMatch, useHistory, useLocation } from 'react-router-dom';
import config from 'payload/config';
@@ -6,7 +6,7 @@ import { useStepNav } from '../../../elements/StepNav';
import usePayloadAPI from '../../../../hooks/usePayloadAPI';
import { useUser } from '../../../data/User';
import formatFields from './formatFields';
import Loading from '../../../elements/Loading';
import RenderCustomComponent from '../../../utilities/RenderCustomComponent';
import DefaultEdit from './Default';
@@ -18,6 +18,7 @@ const EditView = (props) => {
const history = useHistory();
const { setStepNav } = useStepNav();
const [fields, setFields] = useState([]);
const [componentProps, setComponentProps] = useState(null);
const { permissions } = useUser();
const { collection, isEditing } = props;
@@ -32,7 +33,7 @@ const EditView = (props) => {
},
} = collection;
const onSave = (json) => {
const onSave = useCallback((json) => {
history.push(`${admin}/collections/${collection.slug}/${json?.doc?.id}`, {
status: {
message: json.message,
@@ -40,7 +41,7 @@ const EditView = (props) => {
},
data: json.doc,
});
};
}, [collection, history]);
const [{ data, isLoading }] = usePayloadAPI(
(isEditing ? `${serverURL}${api}/${slug}/${id}` : null),
@@ -74,20 +75,31 @@ const EditView = (props) => {
const collectionPermissions = permissions?.[slug];
return (
<RenderCustomComponent
DefaultComponent={DefaultEdit}
path={`${slug}.views.Edit`}
componentProps={{
isLoading,
data: dataToRender,
collection: { ...collection, fields },
permissions: collectionPermissions,
isEditing,
onSave,
}}
/>
);
useEffect(() => {
setComponentProps({
data: dataToRender,
collection: { ...collection, fields },
permissions: collectionPermissions,
isEditing,
onSave,
});
}, [dataToRender, collection, fields, collectionPermissions, isEditing, onSave]);
if (isLoading) {
return <Loading />;
}
if (componentProps) {
return (
<RenderCustomComponent
DefaultComponent={DefaultEdit}
path={`${slug}.views.Edit`}
componentProps={componentProps}
/>
);
}
return null;
};
EditView.defaultProps = {

View File

@@ -16,6 +16,8 @@ import Cell from './Cell';
import './index.scss';
const apiOptions = { initialParams: { depth: 0 } };
const { serverURL, routes: { api, admin } } = config;
const baseClass = 'collection-list';
@@ -44,7 +46,7 @@ const DefaultList = (props) => {
const apiURL = `${serverURL}${api}/${slug}`;
const [{ data }, { setParams }] = usePayloadAPI(apiURL, { initialParams: { depth: 0 } });
const [{ data }, { setParams }] = usePayloadAPI(apiURL, apiOptions);
useEffect(() => {
const params = {};

View File

@@ -875,6 +875,13 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.0.0":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c"
integrity sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.6":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.4.tgz#a6724f1a6b8d2f6ea5236dbfe58c7d7ea9c5eb99"
@@ -6965,6 +6972,11 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
memoize-one@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-3.1.1.tgz#ef609811e3bc28970eac2884eece64d167830d17"
integrity sha512-YqVh744GsMlZu6xkhGslPSqSurOv6P+kLN2J3ysBZfagLcL5FdRK/0UpgLoL8hwjjEvvAVkjJZyFP+1T6p1vgA==
memoize-one@^5.0.0, memoize-one@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
@@ -7252,11 +7264,6 @@ mongodb@3.5.9, mongodb@^3.5.4:
optionalDependencies:
saslprep "^1.0.0"
mongoose-autopopulate@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/mongoose-autopopulate/-/mongoose-autopopulate-0.11.0.tgz#9b5e9c9a36f63a39f8b205d12693e1d8a9260225"
integrity sha512-pbL6w8Yt3KF0yxHZqP498CHelygOSCvYo6SvO0Pz7UW3Mzu4/xIDdoazwnmodDeI81dlKNvpWTS8fVKSTM8sDg==
mongoose-hidden@^1.8.1:
version "1.9.0"
resolved "https://registry.yarnpkg.com/mongoose-hidden/-/mongoose-hidden-1.9.0.tgz#6cc7c0ce6e8134885f73ea7874061e88880f8d50"
@@ -9301,6 +9308,14 @@ react-use@^15.1.0:
ts-easing "^0.2.0"
tslib "^2.0.0"
react-window@^1.6.0-alpha.1:
version "1.6.0-alpha.1"
resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.6.0-alpha.1.tgz#6ffbf8168a1325512fe0127582ee2672ecadb67a"
integrity sha512-Q4Yg/rnVHTSjHdqVuTnZFTD3cwEzXFxFMyEZ5ihK3qu/NLiiFA67M1WM/Q558VmGUQumtJAW5oxKoo5JcdqoOQ==
dependencies:
"@babel/runtime" "^7.0.0"
memoize-one "^3.1.1"
react@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"