payload 1.1.24 → 1.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/components/Routes.js +4 -3
- package/dist/admin/components/elements/Logout/index.d.ts +3 -0
- package/dist/admin/components/elements/Logout/index.js +19 -0
- package/dist/admin/components/elements/Nav/index.js +3 -4
- package/dist/admin/components/elements/UploadGallery/index.js +1 -1
- package/dist/admin/components/forms/RenderFields/index.js +6 -5
- package/dist/admin/components/forms/RenderFields/types.d.ts +1 -0
- package/dist/admin/components/forms/field-types/Array/index.js +2 -2
- package/dist/admin/components/forms/field-types/Array/types.d.ts +1 -0
- package/dist/admin/components/forms/field-types/Blocks/index.js +2 -2
- package/dist/admin/components/forms/field-types/Blocks/types.d.ts +1 -0
- package/dist/admin/components/forms/field-types/Collapsible/index.js +2 -2
- package/dist/admin/components/forms/field-types/Collapsible/types.d.ts +1 -0
- package/dist/admin/components/forms/field-types/Group/index.js +2 -2
- package/dist/admin/components/forms/field-types/Group/types.d.ts +1 -0
- package/dist/admin/components/forms/field-types/Row/index.js +2 -2
- package/dist/admin/components/forms/field-types/Row/types.d.ts +1 -0
- package/dist/admin/components/forms/field-types/Tabs/index.js +55 -14
- package/dist/admin/components/forms/field-types/Tabs/types.d.ts +1 -0
- package/dist/admin/components/forms/field-types/Textarea/Input.js +4 -1
- package/dist/admin/components/forms/field-types/Textarea/index.scss +72 -3
- package/dist/admin/components/forms/useField/index.js +5 -1
- package/dist/admin/components/modals/StayLoggedIn/index.js +3 -2
- package/dist/admin/components/utilities/Auth/index.js +3 -3
- package/dist/admin/components/utilities/DocumentInfo/types.d.ts +2 -0
- package/dist/admin/components/views/Login/index.js +3 -2
- package/dist/admin/components/views/ResetPassword/index.js +3 -2
- package/dist/admin/components/views/Unauthorized/index.js +3 -2
- package/dist/config/defaults.js +3 -1
- package/dist/config/schema.js +6 -1
- package/dist/config/types.d.ts +5 -0
- package/dist/globals/operations/update.js +2 -3
- package/dist/preferences/types.d.ts +4 -0
- package/docs/admin/components.mdx +90 -81
- package/docs/admin/overview.mdx +14 -12
- package/docs/fields/ui.mdx +2 -2
- package/docs/getting-started/what-is-payload.mdx +1 -1
- package/docs/graphql/overview.mdx +1 -1
- package/package.json +1 -1
|
@@ -26,7 +26,8 @@ const Routes = () => {
|
|
|
26
26
|
const [initialized, setInitialized] = useState(null);
|
|
27
27
|
const { user, permissions, refreshCookie } = useAuth();
|
|
28
28
|
const canAccessAdmin = permissions === null || permissions === void 0 ? void 0 : permissions.canAccessAdmin;
|
|
29
|
-
const
|
|
29
|
+
const config = useConfig();
|
|
30
|
+
const { admin: { user: userSlug, logoutRoute, inactivityRoute: logoutInactivityRoute, components: { routes: customRoutes, } = {}, }, routes, collections, globals, } = config;
|
|
30
31
|
const userCollection = collections.find(({ slug }) => slug === userSlug);
|
|
31
32
|
useEffect(() => {
|
|
32
33
|
const { slug } = userCollection;
|
|
@@ -59,9 +60,9 @@ const Routes = () => {
|
|
|
59
60
|
React.createElement(Component, { user: user, canAccessAdmin: canAccessAdmin })))),
|
|
60
61
|
React.createElement(Route, { path: `${match.url}/login` },
|
|
61
62
|
React.createElement(Login, null)),
|
|
62
|
-
React.createElement(Route, { path: `${match.url}
|
|
63
|
+
React.createElement(Route, { path: `${match.url}${logoutRoute}` },
|
|
63
64
|
React.createElement(Logout, null)),
|
|
64
|
-
React.createElement(Route, { path: `${match.url}
|
|
65
|
+
React.createElement(Route, { path: `${match.url}${logoutInactivityRoute}` },
|
|
65
66
|
React.createElement(Logout, { inactivity: true })),
|
|
66
67
|
!userCollection.auth.disableLocalStrategy && (React.createElement(Route, { path: `${match.url}/forgot` },
|
|
67
68
|
React.createElement(ForgotPassword, null))),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Link } from 'react-router-dom';
|
|
3
|
+
import { useConfig } from '../../utilities/Config';
|
|
4
|
+
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
|
|
5
|
+
import LogOut from '../../icons/LogOut';
|
|
6
|
+
const baseClass = 'nav';
|
|
7
|
+
const DefaultLogout = () => {
|
|
8
|
+
const config = useConfig();
|
|
9
|
+
const { routes: { admin }, admin: { logoutRoute, components: { logout } } } = config;
|
|
10
|
+
return (React.createElement(Link, { to: `${admin}${logoutRoute}`, className: `${baseClass}__log-out` },
|
|
11
|
+
React.createElement(LogOut, null)));
|
|
12
|
+
};
|
|
13
|
+
const Logout = () => {
|
|
14
|
+
const { admin: { components: { logout: { Button: CustomLogout } = {
|
|
15
|
+
Button: undefined,
|
|
16
|
+
}, } = {}, } = {}, } = useConfig();
|
|
17
|
+
return (React.createElement(RenderCustomComponent, { CustomComponent: CustomLogout, DefaultComponent: DefaultLogout }));
|
|
18
|
+
};
|
|
19
|
+
export default Logout;
|
|
@@ -4,7 +4,6 @@ import { useConfig } from '../../utilities/Config';
|
|
|
4
4
|
import { useAuth } from '../../utilities/Auth';
|
|
5
5
|
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
|
|
6
6
|
import Chevron from '../../icons/Chevron';
|
|
7
|
-
import LogOut from '../../icons/LogOut';
|
|
8
7
|
import Menu from '../../icons/Menu';
|
|
9
8
|
import CloseMenu from '../../icons/CloseMenu';
|
|
10
9
|
import Icon from '../../graphics/Icon';
|
|
@@ -13,13 +12,14 @@ import Localizer from '../Localizer';
|
|
|
13
12
|
import NavGroup from '../NavGroup';
|
|
14
13
|
import { groupNavItems, EntityType } from '../../../utilities/groupNavItems';
|
|
15
14
|
import './index.scss';
|
|
15
|
+
import Logout from '../Logout';
|
|
16
16
|
const baseClass = 'nav';
|
|
17
17
|
const DefaultNav = () => {
|
|
18
18
|
const { permissions } = useAuth();
|
|
19
19
|
const [menuActive, setMenuActive] = useState(false);
|
|
20
20
|
const [groups, setGroups] = useState([]);
|
|
21
21
|
const history = useHistory();
|
|
22
|
-
const { collections, globals, routes: { admin, }, admin: { components: { beforeNavLinks, afterNavLinks
|
|
22
|
+
const { collections, globals, routes: { admin, }, admin: { components: { beforeNavLinks, afterNavLinks }, }, } = useConfig();
|
|
23
23
|
const classes = [
|
|
24
24
|
baseClass,
|
|
25
25
|
menuActive && `${baseClass}--menu-active`,
|
|
@@ -81,8 +81,7 @@ const DefaultNav = () => {
|
|
|
81
81
|
React.createElement(Localizer, null),
|
|
82
82
|
React.createElement(Link, { to: `${admin}/account`, className: `${baseClass}__account` },
|
|
83
83
|
React.createElement(Account, null)),
|
|
84
|
-
React.createElement(
|
|
85
|
-
React.createElement(LogOut, null)))))));
|
|
84
|
+
React.createElement(Logout, null))))));
|
|
86
85
|
};
|
|
87
86
|
const Nav = () => {
|
|
88
87
|
const { admin: { components: { Nav: CustomNav, } = {
|
|
@@ -5,7 +5,7 @@ const baseClass = 'upload-gallery';
|
|
|
5
5
|
const UploadGallery = (props) => {
|
|
6
6
|
const { docs, onCardClick, collection } = props;
|
|
7
7
|
if (docs && docs.length > 0) {
|
|
8
|
-
return (React.createElement("ul", { className: baseClass }, docs.map((doc
|
|
8
|
+
return (React.createElement("ul", { className: baseClass }, docs.map((doc) => (React.createElement("li", { key: String(doc.id) },
|
|
9
9
|
React.createElement(UploadCard, { doc: doc, ...{ collection }, onClick: () => onCardClick(doc) }))))));
|
|
10
10
|
}
|
|
11
11
|
return null;
|
|
@@ -9,7 +9,7 @@ const intersectionObserverOptions = {
|
|
|
9
9
|
};
|
|
10
10
|
const RenderFields = (props) => {
|
|
11
11
|
var _a;
|
|
12
|
-
const { fieldSchema, fieldTypes, filter, permissions, readOnly: readOnlyOverride, className, forceRender, } = props;
|
|
12
|
+
const { fieldSchema, fieldTypes, filter, permissions, readOnly: readOnlyOverride, className, forceRender, indexPath: incomingIndexPath } = props;
|
|
13
13
|
const [hasRendered, setHasRendered] = useState(Boolean(forceRender));
|
|
14
14
|
const [intersectionRef, entry] = useIntersect(intersectionObserverOptions);
|
|
15
15
|
const operation = useOperation();
|
|
@@ -26,14 +26,14 @@ const RenderFields = (props) => {
|
|
|
26
26
|
className,
|
|
27
27
|
].filter(Boolean).join(' ');
|
|
28
28
|
if (fieldSchema) {
|
|
29
|
-
return (React.createElement("div", { ref: intersectionRef, className: classes }, hasRendered && (fieldSchema.map((field,
|
|
29
|
+
return (React.createElement("div", { ref: intersectionRef, className: classes }, hasRendered && (fieldSchema.map((field, fieldIndex) => {
|
|
30
30
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
31
31
|
const fieldIsPresentational = fieldIsPresentationalOnly(field);
|
|
32
32
|
let FieldComponent = fieldTypes[field.type];
|
|
33
33
|
if (fieldIsPresentational || (!(field === null || field === void 0 ? void 0 : field.hidden) && ((_a = field === null || field === void 0 ? void 0 : field.admin) === null || _a === void 0 ? void 0 : _a.disabled) !== true)) {
|
|
34
34
|
if ((filter && typeof filter === 'function' && filter(field)) || !filter) {
|
|
35
35
|
if (fieldIsPresentational) {
|
|
36
|
-
return (React.createElement(FieldComponent, { ...field, key:
|
|
36
|
+
return (React.createElement(FieldComponent, { ...field, key: fieldIndex }));
|
|
37
37
|
}
|
|
38
38
|
if ((_b = field === null || field === void 0 ? void 0 : field.admin) === null || _b === void 0 ? void 0 : _b.hidden) {
|
|
39
39
|
FieldComponent = fieldTypes.hidden;
|
|
@@ -48,10 +48,11 @@ const RenderFields = (props) => {
|
|
|
48
48
|
readOnly = true;
|
|
49
49
|
}
|
|
50
50
|
if (FieldComponent) {
|
|
51
|
-
return (React.createElement(RenderCustomComponent, { key:
|
|
51
|
+
return (React.createElement(RenderCustomComponent, { key: fieldIndex, CustomComponent: (_h = (_g = field === null || field === void 0 ? void 0 : field.admin) === null || _g === void 0 ? void 0 : _g.components) === null || _h === void 0 ? void 0 : _h.Field, DefaultComponent: FieldComponent, componentProps: {
|
|
52
52
|
...field,
|
|
53
53
|
path: field.path || (isFieldAffectingData ? field.name : ''),
|
|
54
54
|
fieldTypes,
|
|
55
|
+
indexPath: incomingIndexPath ? `${incomingIndexPath}.${fieldIndex}` : `${fieldIndex}`,
|
|
55
56
|
admin: {
|
|
56
57
|
...(field.admin || {}),
|
|
57
58
|
readOnly,
|
|
@@ -59,7 +60,7 @@ const RenderFields = (props) => {
|
|
|
59
60
|
permissions: fieldPermissions,
|
|
60
61
|
} }));
|
|
61
62
|
}
|
|
62
|
-
return (React.createElement("div", { className: "missing-field", key:
|
|
63
|
+
return (React.createElement("div", { className: "missing-field", key: fieldIndex },
|
|
63
64
|
"No matched field found for",
|
|
64
65
|
' ',
|
|
65
66
|
"\"",
|
|
@@ -25,7 +25,7 @@ import './index.scss';
|
|
|
25
25
|
const baseClass = 'array-field';
|
|
26
26
|
const ArrayFieldType = (props) => {
|
|
27
27
|
var _a, _b;
|
|
28
|
-
const { name, path: pathFromProps, fields, fieldTypes, validate = array, required, maxRows, minRows, permissions, admin: { readOnly, description, condition, initCollapsed, className, }, } = props;
|
|
28
|
+
const { name, path: pathFromProps, fields, fieldTypes, validate = array, required, maxRows, minRows, permissions, indexPath, admin: { readOnly, description, condition, initCollapsed, className, }, } = props;
|
|
29
29
|
const path = pathFromProps || name;
|
|
30
30
|
// Handle labeling for Arrays, Global Arrays, and Blocks
|
|
31
31
|
const getLabels = (p) => {
|
|
@@ -174,7 +174,7 @@ const ArrayFieldType = (props) => {
|
|
|
174
174
|
return (React.createElement(Draggable, { key: row.id, draggableId: row.id, index: i, isDragDisabled: readOnly }, (providedDrag) => (React.createElement("div", { id: `${path}-row-${i}`, ref: providedDrag.innerRef, ...providedDrag.draggableProps },
|
|
175
175
|
React.createElement(Collapsible, { collapsed: row.collapsed, onToggle: (collapsed) => setCollapse(row.id, collapsed), className: `${baseClass}__row`, key: row.id, dragHandleProps: providedDrag.dragHandleProps, header: `${labels.singular} ${rowNumber >= 10 ? rowNumber : `0${rowNumber}`}`, actions: !readOnly ? (React.createElement(ArrayAction, { rowCount: rows.length, duplicateRow: duplicateRow, addRow: addRow, moveRow: moveRow, removeRow: removeRow, index: i })) : undefined },
|
|
176
176
|
React.createElement(HiddenInput, { name: `${path}.${i}.id`, value: row.id }),
|
|
177
|
-
React.createElement(RenderFields, { className: `${baseClass}__fields`, forceRender: true, readOnly: readOnly, fieldTypes: fieldTypes, permissions: permissions === null || permissions === void 0 ? void 0 : permissions.fields, fieldSchema: fields.map((field) => ({
|
|
177
|
+
React.createElement(RenderFields, { className: `${baseClass}__fields`, forceRender: true, readOnly: readOnly, fieldTypes: fieldTypes, permissions: permissions === null || permissions === void 0 ? void 0 : permissions.fields, indexPath: indexPath, fieldSchema: fields.map((field) => ({
|
|
178
178
|
...field,
|
|
179
179
|
path: `${path}.${i}${fieldAffectsData(field) ? `.${field.name}` : ''}`,
|
|
180
180
|
})) }))))));
|
|
@@ -32,7 +32,7 @@ const labelDefaults = {
|
|
|
32
32
|
plural: 'Blocks',
|
|
33
33
|
};
|
|
34
34
|
const BlocksField = (props) => {
|
|
35
|
-
const { label, name, path: pathFromProps, blocks, labels = labelDefaults, fieldTypes, maxRows, minRows, required, validate = blocksValidator, permissions, admin: { readOnly, description, condition, initCollapsed, className, }, } = props;
|
|
35
|
+
const { label, name, path: pathFromProps, blocks, labels = labelDefaults, fieldTypes, maxRows, minRows, required, validate = blocksValidator, permissions, indexPath, admin: { readOnly, description, condition, initCollapsed, className, }, } = props;
|
|
36
36
|
const path = pathFromProps || name;
|
|
37
37
|
const { preferencesKey } = useDocumentInfo();
|
|
38
38
|
const { getPreference } = usePreferences();
|
|
@@ -189,7 +189,7 @@ const BlocksField = (props) => {
|
|
|
189
189
|
React.createElement(RenderFields, { className: `${baseClass}__fields`, forceRender: true, readOnly: readOnly, fieldTypes: fieldTypes, permissions: permissions === null || permissions === void 0 ? void 0 : permissions.fields, fieldSchema: blockToRender.fields.map((field) => ({
|
|
190
190
|
...field,
|
|
191
191
|
path: `${path}.${i}${fieldAffectsData(field) ? `.${field.name}` : ''}`,
|
|
192
|
-
})) }))))));
|
|
192
|
+
})), indexPath: indexPath }))))));
|
|
193
193
|
}
|
|
194
194
|
return null;
|
|
195
195
|
}),
|
|
@@ -10,7 +10,7 @@ import { getFieldPath } from '../getFieldPath';
|
|
|
10
10
|
import './index.scss';
|
|
11
11
|
const baseClass = 'collapsible-field';
|
|
12
12
|
const CollapsibleField = (props) => {
|
|
13
|
-
const { label, fields, fieldTypes, path, permissions, admin: { readOnly, className, initCollapsed, description, }, } = props;
|
|
13
|
+
const { label, fields, fieldTypes, path, permissions, indexPath, admin: { readOnly, className, initCollapsed, description, }, } = props;
|
|
14
14
|
const { getPreference, setPreference } = usePreferences();
|
|
15
15
|
const { preferencesKey } = useDocumentInfo();
|
|
16
16
|
const [collapsedOnMount, setCollapsedOnMount] = useState();
|
|
@@ -45,7 +45,7 @@ const CollapsibleField = (props) => {
|
|
|
45
45
|
baseClass,
|
|
46
46
|
className,
|
|
47
47
|
].filter(Boolean).join(' '), header: React.createElement("div", { className: `${baseClass}__label` }, label), onToggle: onToggle },
|
|
48
|
-
React.createElement(RenderFields, { forceRender: true, readOnly: readOnly, permissions: permissions, fieldTypes: fieldTypes, fieldSchema: fields.map((field) => ({
|
|
48
|
+
React.createElement(RenderFields, { forceRender: true, readOnly: readOnly, permissions: permissions, fieldTypes: fieldTypes, indexPath: indexPath, fieldSchema: fields.map((field) => ({
|
|
49
49
|
...field,
|
|
50
50
|
path: getFieldPath(path, field),
|
|
51
51
|
})) })),
|
|
@@ -9,7 +9,7 @@ import { GroupProvider, useGroup } from './provider';
|
|
|
9
9
|
import { useTabs } from '../Tabs/provider';
|
|
10
10
|
const baseClass = 'group-field';
|
|
11
11
|
const Group = (props) => {
|
|
12
|
-
const { label, fields, name, path: pathFromProps, fieldTypes, admin: { readOnly, style, className, width, description, hideGutter = false, }, permissions, } = props;
|
|
12
|
+
const { label, fields, name, path: pathFromProps, fieldTypes, indexPath, admin: { readOnly, style, className, width, description, hideGutter = false, }, permissions, } = props;
|
|
13
13
|
const isWithinCollapsible = useCollapsible();
|
|
14
14
|
const isWithinGroup = useGroup();
|
|
15
15
|
const isWithinTab = useTabs();
|
|
@@ -31,7 +31,7 @@ const Group = (props) => {
|
|
|
31
31
|
(label || description) && (React.createElement("header", { className: `${baseClass}__header` },
|
|
32
32
|
label && (React.createElement("h3", { className: `${baseClass}__title` }, label)),
|
|
33
33
|
React.createElement(FieldDescription, { value: null, description: description }))),
|
|
34
|
-
React.createElement(RenderFields, { permissions: permissions === null || permissions === void 0 ? void 0 : permissions.fields, readOnly: readOnly, fieldTypes: fieldTypes, fieldSchema: fields.map((subField) => ({
|
|
34
|
+
React.createElement(RenderFields, { permissions: permissions === null || permissions === void 0 ? void 0 : permissions.fields, readOnly: readOnly, fieldTypes: fieldTypes, indexPath: indexPath, fieldSchema: fields.map((subField) => ({
|
|
35
35
|
...subField,
|
|
36
36
|
path: `${path}${fieldAffectsData(subField) ? `.${subField.name}` : ''}`,
|
|
37
37
|
})) })))));
|
|
@@ -4,13 +4,13 @@ import withCondition from '../../withCondition';
|
|
|
4
4
|
import { getFieldPath } from '../getFieldPath';
|
|
5
5
|
import './index.scss';
|
|
6
6
|
const Row = (props) => {
|
|
7
|
-
const { fields, fieldTypes, path, permissions, admin: { readOnly, className, }, } = props;
|
|
7
|
+
const { fields, fieldTypes, path, permissions, admin: { readOnly, className, }, indexPath } = props;
|
|
8
8
|
const classes = [
|
|
9
9
|
'field-type',
|
|
10
10
|
'row',
|
|
11
11
|
className,
|
|
12
12
|
].filter(Boolean).join(' ');
|
|
13
|
-
return (React.createElement(RenderFields, { readOnly: readOnly, className: classes, permissions: permissions, fieldTypes: fieldTypes, fieldSchema: fields.map((field) => ({
|
|
13
|
+
return (React.createElement(RenderFields, { readOnly: readOnly, className: classes, permissions: permissions, fieldTypes: fieldTypes, indexPath: indexPath, fieldSchema: fields.map((field) => ({
|
|
14
14
|
...field,
|
|
15
15
|
path: getFieldPath(path, field),
|
|
16
16
|
})) }));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import RenderFields from '../../RenderFields';
|
|
3
3
|
import withCondition from '../../withCondition';
|
|
4
4
|
import { fieldAffectsData, tabHasName } from '../../../../../fields/config/types';
|
|
@@ -6,13 +6,52 @@ import FieldDescription from '../../FieldDescription';
|
|
|
6
6
|
import toKebabCase from '../../../../../utilities/toKebabCase';
|
|
7
7
|
import { useCollapsible } from '../../../elements/Collapsible/provider';
|
|
8
8
|
import { TabsProvider } from './provider';
|
|
9
|
+
import { usePreferences } from '../../../utilities/Preferences';
|
|
10
|
+
import { useDocumentInfo } from '../../../utilities/DocumentInfo';
|
|
9
11
|
import './index.scss';
|
|
10
12
|
const baseClass = 'tabs-field';
|
|
11
13
|
const TabsField = (props) => {
|
|
12
|
-
const { tabs, fieldTypes, path, permissions, admin: { readOnly, className, }, } = props;
|
|
14
|
+
const { tabs, fieldTypes, path, permissions, indexPath, admin: { readOnly, className, }, } = props;
|
|
15
|
+
const { getPreference, setPreference } = usePreferences();
|
|
16
|
+
const { preferencesKey } = useDocumentInfo();
|
|
13
17
|
const isWithinCollapsible = useCollapsible();
|
|
14
|
-
const [
|
|
15
|
-
const
|
|
18
|
+
const [activeTabIndex, setActiveTabIndex] = useState(0);
|
|
19
|
+
const tabsPrefKey = `tabs-${indexPath}`;
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const getInitialPref = async () => {
|
|
22
|
+
var _a, _b, _c, _d;
|
|
23
|
+
const existingPreferences = await getPreference(preferencesKey);
|
|
24
|
+
const initialIndex = path ? (_b = (_a = existingPreferences === null || existingPreferences === void 0 ? void 0 : existingPreferences.fields) === null || _a === void 0 ? void 0 : _a[path]) === null || _b === void 0 ? void 0 : _b.tabIndex : (_d = (_c = existingPreferences === null || existingPreferences === void 0 ? void 0 : existingPreferences.fields) === null || _c === void 0 ? void 0 : _c[tabsPrefKey]) === null || _d === void 0 ? void 0 : _d.tabIndex;
|
|
25
|
+
setActiveTabIndex(initialIndex || 0);
|
|
26
|
+
};
|
|
27
|
+
getInitialPref();
|
|
28
|
+
}, [path, indexPath]);
|
|
29
|
+
const handleTabChange = useCallback(async (incomingTabIndex) => {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
setActiveTabIndex(incomingTabIndex);
|
|
32
|
+
const existingPreferences = await getPreference(preferencesKey);
|
|
33
|
+
setPreference(preferencesKey, {
|
|
34
|
+
...existingPreferences,
|
|
35
|
+
...path ? {
|
|
36
|
+
fields: {
|
|
37
|
+
...(existingPreferences === null || existingPreferences === void 0 ? void 0 : existingPreferences.fields) || {},
|
|
38
|
+
[path]: {
|
|
39
|
+
...(_a = existingPreferences === null || existingPreferences === void 0 ? void 0 : existingPreferences.fields) === null || _a === void 0 ? void 0 : _a[path],
|
|
40
|
+
tabIndex: incomingTabIndex,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
} : {
|
|
44
|
+
fields: {
|
|
45
|
+
...existingPreferences === null || existingPreferences === void 0 ? void 0 : existingPreferences.fields,
|
|
46
|
+
[tabsPrefKey]: {
|
|
47
|
+
...(_b = existingPreferences === null || existingPreferences === void 0 ? void 0 : existingPreferences.fields) === null || _b === void 0 ? void 0 : _b[tabsPrefKey],
|
|
48
|
+
tabIndex: incomingTabIndex,
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}, [indexPath, preferencesKey, getPreference, setPreference, path]);
|
|
54
|
+
const activeTabConfig = tabs[activeTabIndex];
|
|
16
55
|
return (React.createElement("div", { className: [
|
|
17
56
|
className,
|
|
18
57
|
baseClass,
|
|
@@ -20,20 +59,22 @@ const TabsField = (props) => {
|
|
|
20
59
|
].filter(Boolean).join(' ') },
|
|
21
60
|
React.createElement(TabsProvider, null,
|
|
22
61
|
React.createElement("div", { className: `${baseClass}__tabs-wrap` },
|
|
23
|
-
React.createElement("div", { className: `${baseClass}__tabs` }, tabs.map((tab,
|
|
24
|
-
return (React.createElement("button", { key:
|
|
62
|
+
React.createElement("div", { className: `${baseClass}__tabs` }, tabs.map((tab, tabIndex) => {
|
|
63
|
+
return (React.createElement("button", { key: tabIndex, type: "button", className: [
|
|
25
64
|
`${baseClass}__tab-button`,
|
|
26
|
-
|
|
27
|
-
].filter(Boolean).join(' '), onClick: () =>
|
|
65
|
+
activeTabIndex === tabIndex && `${baseClass}__tab-button--active`,
|
|
66
|
+
].filter(Boolean).join(' '), onClick: () => {
|
|
67
|
+
handleTabChange(tabIndex);
|
|
68
|
+
} }, tab.label ? tab.label : (tabHasName(tab) && tab.name)));
|
|
28
69
|
}))),
|
|
29
|
-
React.createElement("div", { className: `${baseClass}__content-wrap` },
|
|
70
|
+
React.createElement("div", { className: `${baseClass}__content-wrap` }, activeTabConfig && (React.createElement("div", { className: [
|
|
30
71
|
`${baseClass}__tab`,
|
|
31
|
-
`${baseClass}__tab-${toKebabCase(
|
|
72
|
+
`${baseClass}__tab-${toKebabCase(activeTabConfig.label)}`,
|
|
32
73
|
].join(' ') },
|
|
33
|
-
React.createElement(FieldDescription, { className: `${baseClass}__description`, description:
|
|
34
|
-
React.createElement(RenderFields, { key: String(
|
|
74
|
+
React.createElement(FieldDescription, { className: `${baseClass}__description`, description: activeTabConfig.description }),
|
|
75
|
+
React.createElement(RenderFields, { key: String(activeTabConfig.label), forceRender: true, readOnly: readOnly, permissions: tabHasName(activeTabConfig) ? permissions[activeTabConfig.name].fields : permissions, fieldTypes: fieldTypes, fieldSchema: activeTabConfig.fields.map((field) => ({
|
|
35
76
|
...field,
|
|
36
|
-
path: `${path ? `${path}.` : ''}${tabHasName(
|
|
37
|
-
})) })))))));
|
|
77
|
+
path: `${path ? `${path}.` : ''}${tabHasName(activeTabConfig) ? `${activeTabConfig.name}.` : ''}${fieldAffectsData(field) ? field.name : ''}`,
|
|
78
|
+
})), indexPath: indexPath })))))));
|
|
38
79
|
};
|
|
39
80
|
export default withCondition(TabsField);
|
|
@@ -18,7 +18,10 @@ const TextareaInput = (props) => {
|
|
|
18
18
|
} },
|
|
19
19
|
React.createElement(Error, { showError: showError, message: errorMessage }),
|
|
20
20
|
React.createElement(Label, { htmlFor: `field-${path.replace(/\./gi, '__')}`, label: label, required: required }),
|
|
21
|
-
React.createElement("
|
|
21
|
+
React.createElement("div", { className: "textarea-outer" },
|
|
22
|
+
React.createElement("div", { className: "textarea-inner" },
|
|
23
|
+
React.createElement("div", { className: "textarea-clone", "data-value": value || placeholder || '' }),
|
|
24
|
+
React.createElement("textarea", { className: "textarea-element", id: `field-${path.replace(/\./gi, '__')}`, value: value || '', onChange: onChange, disabled: readOnly, placeholder: placeholder, name: path, rows: rows }))),
|
|
22
25
|
React.createElement(FieldDescription, { value: value, description: description })));
|
|
23
26
|
};
|
|
24
27
|
export default TextareaInput;
|
|
@@ -1,14 +1,22 @@
|
|
|
1
|
-
@import
|
|
1
|
+
@import "../../../../scss/styles.scss";
|
|
2
2
|
|
|
3
3
|
.field-type.textarea {
|
|
4
4
|
position: relative;
|
|
5
5
|
margin-bottom: $baseline;
|
|
6
6
|
|
|
7
|
-
textarea {
|
|
7
|
+
.textarea-outer {
|
|
8
8
|
@include formInput();
|
|
9
|
+
resize: vertical;
|
|
9
10
|
height: auto;
|
|
10
11
|
min-height: base(3);
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
// Scrollbar for giant content
|
|
14
|
+
max-height: 90vh;
|
|
15
|
+
overflow: auto;
|
|
16
|
+
|
|
17
|
+
@include mid-break {
|
|
18
|
+
max-height: 60vh;
|
|
19
|
+
}
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
&.error {
|
|
@@ -16,4 +24,65 @@
|
|
|
16
24
|
background-color: var(--theme-error-200);
|
|
17
25
|
}
|
|
18
26
|
}
|
|
27
|
+
|
|
28
|
+
// This element takes exactly the same dimensions as the clone
|
|
29
|
+
.textarea-inner {
|
|
30
|
+
display: block;
|
|
31
|
+
position: relative;
|
|
32
|
+
line-height: inherit;
|
|
33
|
+
flex-grow: 1;
|
|
34
|
+
box-sizing: border-box;
|
|
35
|
+
background: none;
|
|
36
|
+
outline: none;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Unstyle the textarea, the border is rendered on .textarea-outer
|
|
40
|
+
.textarea-element {
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: 0;
|
|
43
|
+
left: 0;
|
|
44
|
+
width: 100%;
|
|
45
|
+
height: 100%;
|
|
46
|
+
border: inherit;
|
|
47
|
+
padding: inherit;
|
|
48
|
+
font: inherit;
|
|
49
|
+
line-height: inherit;
|
|
50
|
+
color: inherit;
|
|
51
|
+
background: none;
|
|
52
|
+
box-sizing: border-box;
|
|
53
|
+
overflow: auto;
|
|
54
|
+
resize: none;
|
|
55
|
+
outline: none;
|
|
56
|
+
text-transform: inherit;
|
|
57
|
+
|
|
58
|
+
&::-webkit-scrollbar {
|
|
59
|
+
display: none;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Clone of textarea with same height
|
|
64
|
+
.textarea-clone {
|
|
65
|
+
vertical-align: top;
|
|
66
|
+
display: inline-block;
|
|
67
|
+
flex-grow: 1;
|
|
68
|
+
white-space: pre;
|
|
69
|
+
box-sizing: border-box;
|
|
70
|
+
overflow: hidden;
|
|
71
|
+
text-overflow: ellipsis;
|
|
72
|
+
white-space: pre-wrap;
|
|
73
|
+
pointer-events: none;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.textarea-clone::before {
|
|
77
|
+
content: attr(data-value) " ";
|
|
78
|
+
visibility: hidden;
|
|
79
|
+
opacity: 0;
|
|
80
|
+
white-space: pre-wrap;
|
|
81
|
+
overflow-wrap: break-word;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.textarea-clone::after {
|
|
85
|
+
content: attr(data-after);
|
|
86
|
+
opacity: 0.5;
|
|
87
|
+
}
|
|
19
88
|
}
|
|
@@ -25,7 +25,11 @@ const useField = (options) => {
|
|
|
25
25
|
const val = (e && e.target) ? e.target.value : e;
|
|
26
26
|
if (!modified && !disableModifyingForm) {
|
|
27
27
|
if (typeof setModified === 'function') {
|
|
28
|
-
|
|
28
|
+
// Update modified state after field value comes back
|
|
29
|
+
// to avoid cursor jump caused by state value / DOM mismatch
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
setModified(true);
|
|
32
|
+
}, 10);
|
|
29
33
|
}
|
|
30
34
|
}
|
|
31
35
|
dispatchField({
|
|
@@ -10,7 +10,8 @@ const modalSlug = 'stay-logged-in';
|
|
|
10
10
|
const StayLoggedInModal = (props) => {
|
|
11
11
|
const { refreshCookie } = props;
|
|
12
12
|
const history = useHistory();
|
|
13
|
-
const
|
|
13
|
+
const config = useConfig();
|
|
14
|
+
const { routes: { admin }, admin: { logoutRoute } } = config;
|
|
14
15
|
const { toggleModal } = useModal();
|
|
15
16
|
return (React.createElement(Modal, { className: baseClass, slug: "stay-logged-in" },
|
|
16
17
|
React.createElement(MinimalTemplate, { className: `${baseClass}__template` },
|
|
@@ -19,7 +20,7 @@ const StayLoggedInModal = (props) => {
|
|
|
19
20
|
React.createElement("div", { className: `${baseClass}__actions` },
|
|
20
21
|
React.createElement(Button, { buttonStyle: "secondary", onClick: () => {
|
|
21
22
|
toggleModal(modalSlug);
|
|
22
|
-
history.push(`${admin}
|
|
23
|
+
history.push(`${admin}${logoutRoute}`);
|
|
23
24
|
} }, "Log out"),
|
|
24
25
|
React.createElement(Button, { onClick: () => {
|
|
25
26
|
refreshCookie();
|
|
@@ -13,7 +13,7 @@ export const AuthProvider = ({ children }) => {
|
|
|
13
13
|
const { pathname } = useLocation();
|
|
14
14
|
const { push } = useHistory();
|
|
15
15
|
const config = useConfig();
|
|
16
|
-
const { admin: { user: userSlug, }, serverURL, routes: { admin, api, }, } = config;
|
|
16
|
+
const { admin: { user: userSlug, inactivityRoute: logoutInactivityRoute, }, serverURL, routes: { admin, api, }, } = config;
|
|
17
17
|
const exp = user === null || user === void 0 ? void 0 : user.exp;
|
|
18
18
|
const [permissions, setPermissions] = useState();
|
|
19
19
|
const { openModal, closeAllModals } = useModal();
|
|
@@ -32,7 +32,7 @@ export const AuthProvider = ({ children }) => {
|
|
|
32
32
|
}
|
|
33
33
|
else {
|
|
34
34
|
setUser(null);
|
|
35
|
-
push(`${admin}
|
|
35
|
+
push(`${admin}${logoutInactivityRoute}`);
|
|
36
36
|
}
|
|
37
37
|
}, 1000);
|
|
38
38
|
}
|
|
@@ -104,7 +104,7 @@ export const AuthProvider = ({ children }) => {
|
|
|
104
104
|
if (remainingTime > 0) {
|
|
105
105
|
forceLogOut = setTimeout(() => {
|
|
106
106
|
setUser(null);
|
|
107
|
-
push(`${admin}
|
|
107
|
+
push(`${admin}${logoutInactivityRoute}`);
|
|
108
108
|
closeAllModals();
|
|
109
109
|
}, Math.min(remainingTime * 1000, maxTimeoutTime));
|
|
110
110
|
}
|
|
@@ -8,6 +8,8 @@ export declare type ContextType = {
|
|
|
8
8
|
collection?: SanitizedCollectionConfig;
|
|
9
9
|
global?: SanitizedGlobalConfig;
|
|
10
10
|
type: 'global' | 'collection';
|
|
11
|
+
/** Slug of the collection or global */
|
|
12
|
+
slug?: string;
|
|
11
13
|
id?: string | number;
|
|
12
14
|
preferencesKey?: string;
|
|
13
15
|
versions?: PaginatedDocs<Version>;
|
|
@@ -15,7 +15,8 @@ const baseClass = 'login';
|
|
|
15
15
|
const Login = () => {
|
|
16
16
|
const history = useHistory();
|
|
17
17
|
const { user, setToken } = useAuth();
|
|
18
|
-
const
|
|
18
|
+
const config = useConfig();
|
|
19
|
+
const { admin: { user: userSlug, logoutRoute, components: { beforeLogin, afterLogin, logout } = {}, }, serverURL, routes: { admin, api, }, collections, } = config;
|
|
19
20
|
const collection = collections.find(({ slug }) => slug === userSlug);
|
|
20
21
|
const onSuccess = (data) => {
|
|
21
22
|
if (data.token) {
|
|
@@ -31,7 +32,7 @@ const Login = () => {
|
|
|
31
32
|
React.createElement("p", null,
|
|
32
33
|
"To log in with another user, you should",
|
|
33
34
|
' ',
|
|
34
|
-
React.createElement(Link, { to: `${admin}
|
|
35
|
+
React.createElement(Link, { to: `${admin}${logoutRoute}` }, "log out"),
|
|
35
36
|
' ',
|
|
36
37
|
"first."),
|
|
37
38
|
React.createElement("br", null),
|
|
@@ -13,7 +13,8 @@ import './index.scss';
|
|
|
13
13
|
import HiddenInput from '../../forms/field-types/HiddenInput';
|
|
14
14
|
const baseClass = 'reset-password';
|
|
15
15
|
const ResetPassword = () => {
|
|
16
|
-
const
|
|
16
|
+
const config = useConfig();
|
|
17
|
+
const { admin: { user: userSlug, logoutRoute }, serverURL, routes: { admin, api } } = config;
|
|
17
18
|
const { token } = useParams();
|
|
18
19
|
const history = useHistory();
|
|
19
20
|
const { user, setToken } = useAuth();
|
|
@@ -31,7 +32,7 @@ const ResetPassword = () => {
|
|
|
31
32
|
React.createElement("p", null,
|
|
32
33
|
"To log in with another user, you should",
|
|
33
34
|
' ',
|
|
34
|
-
React.createElement(Link, { to: `${admin}
|
|
35
|
+
React.createElement(Link, { to: `${admin}${logoutRoute}` }, "log out"),
|
|
35
36
|
' ',
|
|
36
37
|
"first."),
|
|
37
38
|
React.createElement("br", null),
|
|
@@ -4,12 +4,13 @@ import Button from '../../elements/Button';
|
|
|
4
4
|
import Meta from '../../utilities/Meta';
|
|
5
5
|
import MinimalTemplate from '../../templates/Minimal';
|
|
6
6
|
const Unauthorized = () => {
|
|
7
|
-
const
|
|
7
|
+
const config = useConfig();
|
|
8
|
+
const { routes: { admin }, admin: { logoutRoute }, } = config;
|
|
8
9
|
return (React.createElement(MinimalTemplate, { className: "unauthorized" },
|
|
9
10
|
React.createElement(Meta, { title: "Unauthorized", description: "Unauthorized", keywords: "Unauthorized, Payload, CMS" }),
|
|
10
11
|
React.createElement("h2", null, "Unauthorized"),
|
|
11
12
|
React.createElement("p", null, "You are not allowed to access this page."),
|
|
12
13
|
React.createElement("br", null),
|
|
13
|
-
React.createElement(Button, { el: "link", to: `${admin}
|
|
14
|
+
React.createElement(Button, { el: "link", to: `${admin}${logoutRoute}` }, "Log out")));
|
|
14
15
|
};
|
|
15
16
|
export default Unauthorized;
|
package/dist/config/defaults.js
CHANGED
|
@@ -24,6 +24,8 @@ exports.defaults = {
|
|
|
24
24
|
indexHTML: path_1.default.resolve(__dirname, '../admin/index.html'),
|
|
25
25
|
avatar: 'default',
|
|
26
26
|
components: {},
|
|
27
|
+
logoutRoute: '/logout',
|
|
28
|
+
inactivityRoute: '/logout-inactivity',
|
|
27
29
|
css: path_1.default.resolve(__dirname, '../admin/scss/custom.css'),
|
|
28
30
|
dateFormat: 'MMMM do yyyy, h:mm a',
|
|
29
31
|
},
|
|
@@ -57,4 +59,4 @@ exports.defaults = {
|
|
|
57
59
|
localization: false,
|
|
58
60
|
telemetry: true,
|
|
59
61
|
};
|
|
60
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
62
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVmYXVsdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29uZmlnL2RlZmF1bHRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLGdEQUF3QjtBQUdYLFFBQUEsUUFBUSxHQUFXO0lBQzlCLFNBQVMsRUFBRSxFQUFFO0lBQ2IsWUFBWSxFQUFFLENBQUM7SUFDZixRQUFRLEVBQUUsRUFBRTtJQUNaLG9CQUFvQixFQUFFLEtBQUs7SUFDM0IsV0FBVyxFQUFFLEVBQUU7SUFDZixPQUFPLEVBQUUsRUFBRTtJQUNYLFNBQVMsRUFBRSxFQUFFO0lBQ2IsWUFBWSxFQUFFLFNBQVM7SUFDdkIsSUFBSSxFQUFFLEVBQUU7SUFDUixJQUFJLEVBQUUsRUFBRTtJQUNSLEtBQUssRUFBRTtRQUNMLElBQUksRUFBRTtZQUNKLFdBQVcsRUFBRSxXQUFXO1NBQ3pCO1FBQ0QsT0FBTyxFQUFFLEtBQUs7UUFDZCxTQUFTLEVBQUUsY0FBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUscUJBQXFCLENBQUM7UUFDekQsTUFBTSxFQUFFLFNBQVM7UUFDakIsVUFBVSxFQUFFLEVBQUU7UUFDZCxXQUFXLEVBQUUsU0FBUztRQUN0QixlQUFlLEVBQUUsb0JBQW9CO1FBQ3JDLEdBQUcsRUFBRSxjQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSwwQkFBMEIsQ0FBQztRQUN4RCxVQUFVLEVBQUUsc0JBQXNCO0tBQ25DO0lBQ0QsVUFBVSxFQUFFO1FBQ1YsVUFBVSxFQUFFLEdBQUcsT0FBTyxDQUFBLE9BQU8sYUFBUCxPQUFPLHVCQUFQLE9BQU8sQ0FBRSxHQUFHLENBQUEsS0FBSyxVQUFVLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxtQkFBbUI7S0FDMUY7SUFDRCxNQUFNLEVBQUUsRUFBRTtJQUNWLE9BQU8sRUFBRTtRQUNQLGFBQWEsRUFBRSxJQUFJO1FBQ25CLDZCQUE2QixFQUFFLElBQUk7UUFDbkMsZ0JBQWdCLEVBQUUsR0FBRyxPQUFPLENBQUEsT0FBTyxhQUFQLE9BQU8sdUJBQVAsT0FBTyxDQUFFLEdBQUcsQ0FBQSxLQUFLLFVBQVUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLGlCQUFpQjtLQUM5RjtJQUNELE1BQU0sRUFBRTtRQUNOLEtBQUssRUFBRSxRQUFRO1FBQ2YsR0FBRyxFQUFFLE1BQU07UUFDWCxPQUFPLEVBQUUsVUFBVTtRQUNuQixpQkFBaUIsRUFBRSxxQkFBcUI7S0FDekM7SUFDRCxTQUFTLEVBQUU7UUFDVCxNQUFNLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxHQUFHO1FBQ3JCLEdBQUcsRUFBRSxHQUFHO0tBQ1Q7SUFDRCxPQUFPLEVBQUU7UUFDUCxJQUFJLEVBQUUsRUFBRTtRQUNSLFdBQVcsRUFBRSxFQUFFO1FBQ2YsVUFBVSxFQUFFLEVBQUU7UUFDZCxhQUFhLEVBQUUsRUFBRTtRQUNqQixjQUFjLEVBQUUsRUFBRTtLQUNuQjtJQUNELEtBQUssRUFBRSxFQUFFO0lBQ1QsWUFBWSxFQUFFLEtBQUs7SUFDbkIsU0FBUyxFQUFFLElBQUk7Q0FDaEIsQ0FBQyJ9
|
package/dist/config/schema.js
CHANGED
|
@@ -53,6 +53,8 @@ exports.default = joi_1.default.object({
|
|
|
53
53
|
dateFormat: joi_1.default.string(),
|
|
54
54
|
avatar: joi_1.default.alternatives()
|
|
55
55
|
.try(joi_1.default.string(), component),
|
|
56
|
+
logoutRoute: joi_1.default.string(),
|
|
57
|
+
inactivityRoute: joi_1.default.string(),
|
|
56
58
|
components: joi_1.default.object()
|
|
57
59
|
.keys({
|
|
58
60
|
routes: joi_1.default.array()
|
|
@@ -71,6 +73,9 @@ exports.default = joi_1.default.object({
|
|
|
71
73
|
beforeNavLinks: joi_1.default.array().items(component),
|
|
72
74
|
afterNavLinks: joi_1.default.array().items(component),
|
|
73
75
|
Nav: component,
|
|
76
|
+
logout: joi_1.default.object({
|
|
77
|
+
Button: component,
|
|
78
|
+
}),
|
|
74
79
|
views: joi_1.default.object({
|
|
75
80
|
Dashboard: component,
|
|
76
81
|
Account: component,
|
|
@@ -139,4 +144,4 @@ exports.default = joi_1.default.object({
|
|
|
139
144
|
onInit: joi_1.default.func(),
|
|
140
145
|
debug: joi_1.default.boolean(),
|
|
141
146
|
});
|
|
142
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
147
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbmZpZy9zY2hlbWEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQ0EsOENBQXNCO0FBRXRCLE1BQU0sU0FBUyxHQUFHLGFBQUcsQ0FBQyxZQUFZLEVBQUUsQ0FBQyxHQUFHLENBQ3RDLGFBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLEVBQUUsRUFDdEIsYUFBRyxDQUFDLElBQUksRUFBRSxDQUNYLENBQUM7QUFFVyxRQUFBLGVBQWUsR0FBRyxhQUFHLENBQUMsS0FBSyxFQUFFLENBQUMsS0FBSyxDQUFDLGFBQUcsQ0FBQyxNQUFNLENBQUM7SUFDMUQsSUFBSSxFQUFFLGFBQUcsQ0FBQyxNQUFNLEVBQUU7SUFDbEIsTUFBTSxFQUFFLGFBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLFNBQVMsQ0FBQztJQUNqRyxJQUFJLEVBQUUsYUFBRyxDQUFDLElBQUksRUFBRTtJQUNoQixPQUFPLEVBQUUsYUFBRyxDQUFDLFlBQVksRUFBRSxDQUFDLEdBQUcsQ0FDN0IsYUFBRyxDQUFDLEtBQUssRUFBRSxDQUFDLEtBQUssQ0FBQyxhQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsRUFDN0IsYUFBRyxDQUFDLElBQUksRUFBRSxDQUNYO0NBQ0YsQ0FBQyxDQUFDLENBQUM7QUFFSixrQkFBZSxhQUFHLENBQUMsTUFBTSxDQUFDO0lBQ3hCLFNBQVMsRUFBRSxhQUFHLENBQUMsTUFBTSxFQUFFO1NBQ3BCLEdBQUcsRUFBRTtTQUNMLEtBQUssQ0FBQyxFQUFFLENBQUM7U0FDVCxNQUFNLENBQUMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDeEIsTUFBTSxrQkFBa0IsR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUks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
|
package/dist/config/types.d.ts
CHANGED
|
@@ -123,6 +123,8 @@ export declare type Config = {
|
|
|
123
123
|
css?: string;
|
|
124
124
|
dateFormat?: string;
|
|
125
125
|
avatar?: 'default' | 'gravatar' | React.ComponentType<any>;
|
|
126
|
+
logoutRoute?: string;
|
|
127
|
+
inactivityRoute?: string;
|
|
126
128
|
components?: {
|
|
127
129
|
routes?: AdminRoute[];
|
|
128
130
|
providers?: React.ComponentType<{
|
|
@@ -135,6 +137,9 @@ export declare type Config = {
|
|
|
135
137
|
beforeNavLinks?: React.ComponentType<any>[];
|
|
136
138
|
afterNavLinks?: React.ComponentType<any>[];
|
|
137
139
|
Nav?: React.ComponentType<any>;
|
|
140
|
+
logout?: {
|
|
141
|
+
Button?: React.ComponentType<any>;
|
|
142
|
+
};
|
|
138
143
|
graphics?: {
|
|
139
144
|
Icon?: React.ComponentType<any>;
|
|
140
145
|
Logo?: React.ComponentType<any>;
|
|
@@ -171,11 +171,10 @@ async function update(args) {
|
|
|
171
171
|
// /////////////////////////////////////
|
|
172
172
|
// afterRead - Global
|
|
173
173
|
// /////////////////////////////////////
|
|
174
|
-
await globalConfig.hooks.
|
|
174
|
+
await globalConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
|
175
175
|
await priorHook;
|
|
176
176
|
global = await hook({
|
|
177
177
|
doc: global,
|
|
178
|
-
previousDoc: originalDoc,
|
|
179
178
|
req,
|
|
180
179
|
}) || global;
|
|
181
180
|
}, Promise.resolve());
|
|
@@ -207,4 +206,4 @@ async function update(args) {
|
|
|
207
206
|
return global;
|
|
208
207
|
}
|
|
209
208
|
exports.default = update;
|
|
210
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
209
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2dsb2JhbHMvb3BlcmF0aW9ucy91cGRhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFFQSw2RUFBcUQ7QUFDckQsb0dBQTRFO0FBQzVFLHdFQUFxRTtBQUNyRSwyRUFBd0U7QUFDeEUsOEZBQTJGO0FBQzNGLCtGQUF1RTtBQUN2RSxxQ0FBa0Q7QUFDbEQsa0VBQStEO0FBQy9ELHNFQUFtRTtBQUNuRSxnRUFBNkQ7QUFDN0QsNERBQXlEO0FBZXpELEtBQUssVUFBVSxNQUFNLENBQTZCLElBQVU7O0lBQzFELE1BQU0sRUFDSixZQUFZLEVBQ1osSUFBSSxFQUNKLEdBQUcsRUFDSCxHQUFHLEVBQUUsRUFDSCxNQUFNLEVBQ04sT0FBTyxFQUNQLE9BQU8sRUFBRSxFQUNQLE9BQU8sRUFBRSxFQUNQLEtBQUssR0FDTixHQUNGLEdBQ0YsRUFDRCxLQUFLLEVBQ0wsY0FBYyxFQUNkLGdCQUFnQixFQUNoQixLQUFLLEVBQUUsUUFBUSxFQUNmLFFBQVEsR0FDVCxHQUFHLElBQUksQ0FBQztJQUVULElBQUksRUFBRSxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUM7SUFFcEIsTUFBTSxlQUFlLEdBQUcsT0FBTyxDQUFDLFFBQVEsS0FBSSxNQUFBLFlBQVksQ0FBQyxRQUFRLDBDQUFFLE1BQU0sQ0FBQSxDQUFDLENBQUM7SUFFM0Usd0NBQXdDO0lBQ3hDLGlDQUFpQztJQUNqQyx3Q0FBd0M7SUFFeEMsTUFBTSxhQUFhLEdBQUcsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLE1BQU0sSUFBQSx1QkFBYSxFQUFDLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFLFlBQVksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUU5Ryx3Q0FBd0M7SUFDeEMsb0JBQW9CO0lBQ3BCLHdDQUF3QztJQUV4QyxNQUFNLFlBQVksR0FBcUI7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
|
|
@@ -24,8 +24,12 @@ export declare type PreferenceUpdateRequest = PreferenceRequest & {
|
|
|
24
24
|
value: undefined;
|
|
25
25
|
};
|
|
26
26
|
export declare type CollapsedPreferences = string[];
|
|
27
|
+
export declare type TabsPreferences = Array<{
|
|
28
|
+
[path: string]: number;
|
|
29
|
+
}>;
|
|
27
30
|
export declare type FieldsPreferences = {
|
|
28
31
|
[key: string]: {
|
|
32
|
+
tabIndex: number;
|
|
29
33
|
collapsed: CollapsedPreferences;
|
|
30
34
|
};
|
|
31
35
|
};
|
|
@@ -11,35 +11,39 @@ While designing the Payload Admin panel, we determined it should be as minimal a
|
|
|
11
11
|
To swap in your own React component, first, consult the list of available component overrides below. Determine the scope that corresponds to what you are trying to accomplish, and then author your React component accordingly.
|
|
12
12
|
|
|
13
13
|
<Banner type="success">
|
|
14
|
-
<strong>Tip:</strong
|
|
15
|
-
|
|
14
|
+
<strong>Tip:</strong>
|
|
15
|
+
<br />
|
|
16
|
+
Custom components will automatically be provided with all props that the
|
|
17
|
+
default component would accept.
|
|
16
18
|
</Banner>
|
|
17
19
|
|
|
18
20
|
### Base Component Overrides
|
|
19
21
|
|
|
20
22
|
You can override a set of admin panel-wide components by providing a component to your base Payload config's `admin.components` property. The following options are available:
|
|
21
23
|
|
|
22
|
-
| Path
|
|
23
|
-
|
|
|
24
|
-
| **`Nav`**
|
|
25
|
-
| **`
|
|
26
|
-
| **`
|
|
27
|
-
| **`
|
|
28
|
-
| **`
|
|
29
|
-
| **`
|
|
30
|
-
| **`
|
|
31
|
-
| **`
|
|
32
|
-
| **`views.
|
|
33
|
-
| **`
|
|
34
|
-
| **`graphics.
|
|
35
|
-
| **`
|
|
36
|
-
| **`
|
|
24
|
+
| Path | Description |
|
|
25
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| **`Nav`** | Contains the sidebar and mobile Nav in its entirety. |
|
|
27
|
+
| **`logout.Button`** | A custom React component.
|
|
28
|
+
| **`BeforeDashboard`** | Array of components to inject into the built-in Dashboard, _before_ the default dashboard contents. |
|
|
29
|
+
| **`AfterDashboard`** | Array of components to inject into the built-in Dashboard, _after_ the default dashboard contents. [Demo](https://github.com/payloadcms/payload/tree/master/test/admin/components/AfterDashboard/index.tsx) |
|
|
30
|
+
| **`BeforeLogin`** | Array of components to inject into the built-in Login, _before_ the default login form. |
|
|
31
|
+
| **`AfterLogin`** | Array of components to inject into the built-in Login, _after_ the default login form. |
|
|
32
|
+
| **`BeforeNavLinks`** | Array of components to inject into the built-in Nav, _before_ the links themselves. |
|
|
33
|
+
| **`AfterNavLinks`** | Array of components to inject into the built-in Nav, _after_ the links. |
|
|
34
|
+
| **`views.Account`** | The Account view is used to show the currently logged in user's Account page. |
|
|
35
|
+
| **`views.Dashboard`** | The main landing page of the Admin panel. |
|
|
36
|
+
| **`graphics.Icon`** | Used as a graphic within the `Nav` component. Often represents a condensed version of a full logo. |
|
|
37
|
+
| **`graphics.Logo`** | The full logo to be used in contexts like the `Login` view. |
|
|
38
|
+
| **`routes`** | Define your own routes to add to the Payload Admin UI. [More](#custom-routes) |
|
|
39
|
+
| **`providers`** | Define your own provider components that will wrap the Payload Admin UI. [More](#custom-providers) |
|
|
37
40
|
|
|
38
41
|
#### Full example:
|
|
39
42
|
|
|
40
43
|
`payload.config.js`
|
|
44
|
+
|
|
41
45
|
```ts
|
|
42
|
-
import { buildConfig } from
|
|
46
|
+
import { buildConfig } from "payload/config";
|
|
43
47
|
import {
|
|
44
48
|
MyCustomNav,
|
|
45
49
|
MyCustomLogo,
|
|
@@ -47,7 +51,7 @@ import {
|
|
|
47
51
|
MyCustomAccount,
|
|
48
52
|
MyCustomDashboard,
|
|
49
53
|
MyProvider,
|
|
50
|
-
} from
|
|
54
|
+
} from "./customComponents";
|
|
51
55
|
|
|
52
56
|
export default buildConfig({
|
|
53
57
|
admin: {
|
|
@@ -67,75 +71,69 @@ export default buildConfig({
|
|
|
67
71
|
});
|
|
68
72
|
```
|
|
69
73
|
|
|
70
|
-
|
|
74
|
+
_For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/master/test/admin/components)._
|
|
71
75
|
|
|
72
76
|
### Collections
|
|
73
77
|
|
|
74
78
|
You can override components on a Collection-by-Collection basis via each Collection's `admin` property.
|
|
75
79
|
|
|
76
|
-
| Path
|
|
77
|
-
| ----------------
|
|
78
|
-
| **`views.Edit`**
|
|
79
|
-
| **`views.List`**
|
|
80
|
+
| Path | Description |
|
|
81
|
+
| ---------------- | ------------------------------------------------------------------------------------------------ |
|
|
82
|
+
| **`views.Edit`** | Used while a document within this Collection is being edited. |
|
|
83
|
+
| **`views.List`** | The `List` view is used to render a paginated, filterable table of Documents in this Collection. |
|
|
80
84
|
|
|
81
85
|
### Globals
|
|
82
86
|
|
|
83
87
|
As with Collections, You can override components on a global-by-global basis via their `admin` property.
|
|
84
88
|
|
|
85
|
-
| Path
|
|
86
|
-
| ----------------
|
|
87
|
-
| **`views.Edit`**
|
|
89
|
+
| Path | Description |
|
|
90
|
+
| ---------------- | --------------------------------------- |
|
|
91
|
+
| **`views.Edit`** | Used while this Global is being edited. |
|
|
88
92
|
|
|
89
93
|
### Fields
|
|
90
94
|
|
|
91
95
|
All Payload fields support the ability to swap in your own React components. So, for example, instead of rendering a default Text input, you might need to render a color picker that provides the editor with a custom color picker interface to restrict the data entered to colors only.
|
|
92
96
|
|
|
93
97
|
<Banner type="success">
|
|
94
|
-
<strong>Tip:</strong
|
|
95
|
-
|
|
98
|
+
<strong>Tip:</strong>
|
|
99
|
+
<br />
|
|
100
|
+
Don't see a built-in field type that you need? Build it! Using a combination
|
|
101
|
+
of custom validation and custom components, you can override the entirety of
|
|
102
|
+
how a component functions within the admin panel and effectively create your
|
|
103
|
+
own field type.
|
|
96
104
|
</Banner>
|
|
97
105
|
|
|
98
106
|
**Fields support the following custom components:**
|
|
99
107
|
|
|
100
|
-
| Component
|
|
101
|
-
|
|
|
102
|
-
| **`Filter`**
|
|
103
|
-
| **`Cell`**
|
|
104
|
-
| **`Field`**
|
|
108
|
+
| Component | Description |
|
|
109
|
+
| ------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
|
110
|
+
| **`Filter`** | Override the text input that is presented in the `List` view when a user is filtering documents by the customized field. |
|
|
111
|
+
| **`Cell`** | Used in the `List` view's table to represent a table-based preview of the data stored in the field. [More](#cell-component) |
|
|
112
|
+
| **`Field`** | Swap out the field itself within all `Edit` views. [More](#field-component) |
|
|
105
113
|
|
|
106
114
|
## Cell Component
|
|
107
115
|
|
|
108
116
|
These are the props that will be passed to your custom Cell to use in your own components.
|
|
109
117
|
|
|
110
|
-
| Property
|
|
111
|
-
|
|
112
|
-
| **`field`**
|
|
113
|
-
| **`colIndex`**
|
|
118
|
+
| Property | Description |
|
|
119
|
+
| ---------------- | ----------------------------------------------------------------- |
|
|
120
|
+
| **`field`** | An object that includes the field configuration. |
|
|
121
|
+
| **`colIndex`** | A unique number for the column in the list. |
|
|
114
122
|
| **`collection`** | An object with the config of the collection that the field is in. |
|
|
115
123
|
| **`cellData`** | The data for the field that the cell represents. |
|
|
116
|
-
| **`rowData`**
|
|
124
|
+
| **`rowData`** | An object with all the field values for the row. |
|
|
117
125
|
|
|
118
126
|
#### Example
|
|
119
127
|
|
|
120
128
|
```tsx
|
|
121
|
-
import React from
|
|
122
|
-
import
|
|
123
|
-
const baseClass =
|
|
129
|
+
import React from "react";
|
|
130
|
+
import "./index.scss";
|
|
131
|
+
const baseClass = "custom-cell";
|
|
124
132
|
|
|
125
133
|
const CustomCell: React.FC<Props> = (props) => {
|
|
126
|
-
const {
|
|
127
|
-
field,
|
|
128
|
-
colIndex,
|
|
129
|
-
collection,
|
|
130
|
-
cellData,
|
|
131
|
-
rowData,
|
|
132
|
-
} = props;
|
|
134
|
+
const { field, colIndex, collection, cellData, rowData } = props;
|
|
133
135
|
|
|
134
|
-
return
|
|
135
|
-
<span className={baseClass}>
|
|
136
|
-
{ cellData }
|
|
137
|
-
</span>
|
|
138
|
-
);
|
|
136
|
+
return <span className={baseClass}>{cellData}</span>;
|
|
139
137
|
};
|
|
140
138
|
```
|
|
141
139
|
|
|
@@ -148,21 +146,28 @@ When writing your own custom components you can make use of a number of hooks to
|
|
|
148
146
|
When swapping out the `Field` component, you'll be responsible for sending and receiving the field's `value` from the form itself. To do so, import the `useField` hook as follows:
|
|
149
147
|
|
|
150
148
|
```tsx
|
|
151
|
-
import { useField } from
|
|
149
|
+
import { useField } from "payload/components/forms";
|
|
152
150
|
|
|
153
|
-
type Props = { path: string }
|
|
151
|
+
type Props = { path: string };
|
|
154
152
|
|
|
155
153
|
const CustomTextField: React.FC<Props> = ({ path }) => {
|
|
156
154
|
// highlight-start
|
|
157
|
-
const { value, setValue } = useField<Props>({ path })
|
|
155
|
+
const { value, setValue } = useField<Props>({ path });
|
|
158
156
|
// highlight-end
|
|
159
157
|
|
|
160
|
-
return
|
|
161
|
-
}
|
|
158
|
+
return (
|
|
159
|
+
<input onChange={(e) => setValue(e.target.value)} value={value.path} />
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
<Banner type="success">
|
|
165
|
-
For more information regarding the hooks that are available to you while you
|
|
165
|
+
For more information regarding the hooks that are available to you while you
|
|
166
|
+
build custom components, including the <strong>useField</strong> hook,{" "}
|
|
167
|
+
<a href="/docs/admin/hooks" style={{ color: "black" }}>
|
|
168
|
+
click here
|
|
169
|
+
</a>
|
|
170
|
+
.
|
|
166
171
|
</Banner>
|
|
167
172
|
|
|
168
173
|
## Custom routes
|
|
@@ -171,28 +176,31 @@ You can easily add your own custom routes to the Payload Admin panel using the `
|
|
|
171
176
|
|
|
172
177
|
**Custom routes support the following properties:**
|
|
173
178
|
|
|
174
|
-
| Property
|
|
175
|
-
|
|
|
176
|
-
| **`Component`**
|
|
177
|
-
| **`path`**
|
|
178
|
-
| **`exact`**
|
|
179
|
-
| **`strict`**
|
|
180
|
-
| **`sensitive`**
|
|
179
|
+
| Property | Description |
|
|
180
|
+
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
181
|
+
| **`Component`** \* | Pass in the component that should be rendered when a user navigates to this route. |
|
|
182
|
+
| **`path`** \* | React Router `path`. [See the React Router docs](https://v5.reactrouter.com/web/api/Route/path-string-string) for more info. |
|
|
183
|
+
| **`exact`** | React Router `exact` property. [More](https://v5.reactrouter.com/web/api/Route/exact-bool) |
|
|
184
|
+
| **`strict`** | React Router `strict` property. [More](https://v5.reactrouter.com/web/api/Route/strict-bool) |
|
|
185
|
+
| **`sensitive`** | React Router `sensitive` property. [More](https://v5.reactrouter.com/web/api/Route/sensitive-bool) |
|
|
181
186
|
|
|
182
|
-
|
|
187
|
+
_\* An asterisk denotes that a property is required._
|
|
183
188
|
|
|
184
189
|
#### Custom route components
|
|
185
190
|
|
|
186
191
|
Your custom route components will be given all the props that a React Router `<Route />` typically would receive, as well as two props from Payload:
|
|
187
192
|
|
|
188
|
-
| Prop
|
|
189
|
-
|
|
|
190
|
-
| **`user`**
|
|
191
|
-
| **`canAccessAdmin`**
|
|
193
|
+
| Prop | Description |
|
|
194
|
+
| ----------------------- | ---------------------------------------------------------------------------- |
|
|
195
|
+
| **`user`** | The currently logged in user. Will be `null` if no user is logged in. |
|
|
196
|
+
| **`canAccessAdmin`** \* | If the currently logged in user is allowed to access the admin panel or not. |
|
|
192
197
|
|
|
193
198
|
<Banner type="warning">
|
|
194
|
-
<strong>Note:</strong
|
|
195
|
-
|
|
199
|
+
<strong>Note:</strong>
|
|
200
|
+
<br />
|
|
201
|
+
It's up to you to secure your custom routes. If your route requires a user to
|
|
202
|
+
be logged in or to have certain access rights, you should handle that within
|
|
203
|
+
your route component yourself.
|
|
196
204
|
</Banner>
|
|
197
205
|
|
|
198
206
|
#### Example
|
|
@@ -208,7 +216,10 @@ To see how to pass in your custom views to create custom routes of your own, tak
|
|
|
208
216
|
|
|
209
217
|
As your admin customizations gets more complex you may want to share state between fields or other components. You can add custom providers to do add your own context to any Payload app for use in other custom components within the admin panel. Within your config add `admin.components.providers`, these can be used to share context or provide other custom functionality. Read the [React context](https://reactjs.org/docs/context.html) docs to learn more.
|
|
210
218
|
|
|
211
|
-
<Banner type="warning"
|
|
219
|
+
<Banner type="warning">
|
|
220
|
+
<strong>Reminder:</strong> Don't forget to pass the **children** prop through
|
|
221
|
+
the provider component for the admin UI to show
|
|
222
|
+
</Banner>
|
|
212
223
|
|
|
213
224
|
### Styling Custom Components
|
|
214
225
|
|
|
@@ -225,7 +236,7 @@ To make use of Payload SCSS variables / mixins to use directly in your own compo
|
|
|
225
236
|
In any custom component you can get the selected locale with the `useLocale` hook. Here is a simple example:
|
|
226
237
|
|
|
227
238
|
```tsx
|
|
228
|
-
import { useLocale } from
|
|
239
|
+
import { useLocale } from "payload/components/utilities";
|
|
229
240
|
|
|
230
241
|
const Greeting: React.FC = () => {
|
|
231
242
|
// highlight-start
|
|
@@ -233,12 +244,10 @@ const Greeting: React.FC = () => {
|
|
|
233
244
|
// highlight-end
|
|
234
245
|
|
|
235
246
|
const trans = {
|
|
236
|
-
en:
|
|
237
|
-
es:
|
|
247
|
+
en: "Hello",
|
|
248
|
+
es: "Hola",
|
|
238
249
|
};
|
|
239
250
|
|
|
240
|
-
return
|
|
241
|
-
<span> { trans[locale] } </span>
|
|
242
|
-
);
|
|
251
|
+
return <span> {trans[locale]} </span>;
|
|
243
252
|
};
|
|
244
253
|
```
|
package/docs/admin/overview.mdx
CHANGED
|
@@ -22,18 +22,20 @@ The Payload Admin panel is built with Webpack, code-split, highly performant (ev
|
|
|
22
22
|
|
|
23
23
|
All options for the Admin panel are defined in your base Payload config file.
|
|
24
24
|
|
|
25
|
-
| Option
|
|
26
|
-
|
|
|
27
|
-
| `user`
|
|
28
|
-
| `meta`
|
|
29
|
-
| `disable`
|
|
30
|
-
| `indexHTML`
|
|
31
|
-
| `css`
|
|
32
|
-
| `scss`
|
|
33
|
-
| `dateFormat`
|
|
34
|
-
| `avatar`
|
|
35
|
-
| `components`
|
|
36
|
-
| `webpack`
|
|
25
|
+
| Option | Description |
|
|
26
|
+
| --------------------- | -------------|
|
|
27
|
+
| `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) |
|
|
28
|
+
| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. |
|
|
29
|
+
| `disable` | If set to `true`, the entire Admin panel will be disabled. |
|
|
30
|
+
| `indexHTML` | Optionally replace the entirety of the `index.html` file used by the Admin panel. Reference the [base index.html file](https://github.com/payloadcms/payload/blob/master/src/admin/index.html) to ensure your replacement has the appropriate HTML elements. |
|
|
31
|
+
| `css` | Absolute path to a stylesheet that you can use to override / customize the Admin panel styling. [More](/docs/admin/customizing-css). |
|
|
32
|
+
| `scss` | Absolute path to a Sass variables / mixins stylesheet meant to override Payload styles to make for an easy re-skinning of the Admin panel. [More](/docs/admin/customizing-css#overriding-scss-variables). |
|
|
33
|
+
| `dateFormat` | Global date format that will be used for all dates in the Admin panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
|
|
34
|
+
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
|
|
35
|
+
| `components` | Component overrides that affect the entirety of the Admin panel. [More](/docs/admin/components) |
|
|
36
|
+
| `webpack` | Customize the Webpack config that's used to generate the Admin panel. [More](/docs/admin/webpack) | |
|
|
37
|
+
| **`logoutRoute`** | The route for the `logout` page. |
|
|
38
|
+
| **`inactivityRoute`** | The route for the `logout` inactivity page. |
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
### The Admin User Collection
|
package/docs/fields/ui.mdx
CHANGED
|
@@ -27,8 +27,8 @@ With this field, you can also inject custom `Cell` components that appear as add
|
|
|
27
27
|
| ---------------------------- |-------------------------------------------------------------------------------------------------------------------|
|
|
28
28
|
| **`name`** * | A unique identifier for this field. |
|
|
29
29
|
| **`label`** | Human-readable label for this UI field. |
|
|
30
|
-
| **`admin.components.Field`** | React component to be rendered for this field within the Edit view. [More](/admin/components/#field-component) |
|
|
31
|
-
| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](/admin/components/#field-component) |
|
|
30
|
+
| **`admin.components.Field`** | React component to be rendered for this field within the Edit view. [More](/docs/admin/components/#field-component) |
|
|
31
|
+
| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](/docs/admin/components/#field-component) |
|
|
32
32
|
|
|
33
33
|
*\* An asterisk denotes that a property is required.*
|
|
34
34
|
|
|
@@ -48,7 +48,7 @@ The team behind Payload has been building websites and apps with existing conten
|
|
|
48
48
|
- Secure
|
|
49
49
|
- Fully flexible and extensible
|
|
50
50
|
|
|
51
|
-
Payload is our silver bullet solution.
|
|
51
|
+
Payload is our silver bullet solution. We've blended the best parts of our experience with other CMS and app frameworks into Payload, and we finally have everything we need when we build new apps and websites:
|
|
52
52
|
|
|
53
53
|
- A beautiful, dynamic, customizable admin UI
|
|
54
54
|
- Extensible and reusable authentication
|
|
@@ -14,7 +14,7 @@ The labels you provide for your Collections and Globals are used to name the Gra
|
|
|
14
14
|
|
|
15
15
|
## GraphQL Options
|
|
16
16
|
|
|
17
|
-
At the top of your Payload config you can define all the options to manage GraphQL.
|
|
17
|
+
At the top of your Payload config you can define all the options to manage GraphQL.
|
|
18
18
|
|
|
19
19
|
| Option | Description |
|
|
20
20
|
| -------------------- | -------------|
|