payload 1.1.23 → 1.1.25
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 +8 -19
- 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 +15 -13
- package/docs/authentication/overview.mdx +1 -1
- package/docs/fields/array.mdx +1 -1
- package/docs/fields/blocks.mdx +1 -1
- package/docs/fields/rich-text.mdx +3 -3
- package/docs/fields/tabs.mdx +1 -1
- 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/docs/upload/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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback,
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
2
|
import { useAuth } from '../../utilities/Auth';
|
|
3
3
|
import { useFormProcessing, useFormSubmitted, useFormModified, useForm, useFormFields } from '../Form/context';
|
|
4
4
|
import { useDocumentInfo } from '../../utilities/DocumentInfo';
|
|
@@ -15,24 +15,19 @@ const useField = (options) => {
|
|
|
15
15
|
const field = useFormFields(([fields]) => fields[path]);
|
|
16
16
|
const dispatchField = useFormFields(([_, dispatch]) => dispatch);
|
|
17
17
|
const { getData, getSiblingData, setModified } = useForm();
|
|
18
|
+
const value = field === null || field === void 0 ? void 0 : field.value;
|
|
18
19
|
const initialValue = field === null || field === void 0 ? void 0 : field.initialValue;
|
|
19
20
|
const valid = typeof (field === null || field === void 0 ? void 0 : field.valid) === 'boolean' ? field.valid : true;
|
|
20
21
|
const showError = valid === false && submitted;
|
|
21
|
-
// We store internal value to ensure that components react immediately
|
|
22
|
-
// to any value changes, therefore preventing "cursor jump" that can be seen
|
|
23
|
-
// if a user types quickly in an input
|
|
24
|
-
const [internalValue, setInternalValue] = useState(() => field === null || field === void 0 ? void 0 : field.value);
|
|
25
|
-
const [internalInitialValue, setInternalInitialValue] = useState(() => field === null || field === void 0 ? void 0 : field.initialValue);
|
|
26
22
|
// Method to return from `useField`, used to
|
|
27
23
|
// update field values from field component(s)
|
|
28
24
|
const setValue = useCallback((e, disableModifyingForm = false) => {
|
|
29
25
|
const val = (e && e.target) ? e.target.value : e;
|
|
30
26
|
if (!modified && !disableModifyingForm) {
|
|
31
27
|
if (typeof setModified === 'function') {
|
|
32
|
-
setModified(true);
|
|
28
|
+
Promise.resolve(() => setModified(true));
|
|
33
29
|
}
|
|
34
30
|
}
|
|
35
|
-
setInternalValue(val);
|
|
36
31
|
dispatchField({
|
|
37
32
|
type: 'UPDATE',
|
|
38
33
|
path,
|
|
@@ -51,12 +46,12 @@ const useField = (options) => {
|
|
|
51
46
|
const result = useMemo(() => ({
|
|
52
47
|
showError,
|
|
53
48
|
errorMessage: field === null || field === void 0 ? void 0 : field.errorMessage,
|
|
54
|
-
value
|
|
49
|
+
value,
|
|
55
50
|
formSubmitted: submitted,
|
|
56
51
|
formProcessing: processing,
|
|
57
52
|
setValue,
|
|
58
53
|
initialValue,
|
|
59
|
-
}), [field, processing, setValue, showError, submitted,
|
|
54
|
+
}), [field, processing, setValue, showError, submitted, value, initialValue]);
|
|
60
55
|
// Throttle the validate function
|
|
61
56
|
useThrottledEffect(() => {
|
|
62
57
|
const validateField = async () => {
|
|
@@ -66,7 +61,7 @@ const useField = (options) => {
|
|
|
66
61
|
disableFormData,
|
|
67
62
|
validate,
|
|
68
63
|
condition,
|
|
69
|
-
value
|
|
64
|
+
value,
|
|
70
65
|
valid: false,
|
|
71
66
|
errorMessage: undefined,
|
|
72
67
|
};
|
|
@@ -77,7 +72,7 @@ const useField = (options) => {
|
|
|
77
72
|
siblingData: getSiblingData(path),
|
|
78
73
|
operation,
|
|
79
74
|
};
|
|
80
|
-
const validationResult = typeof validate === 'function' ? await validate(
|
|
75
|
+
const validationResult = typeof validate === 'function' ? await validate(value, validateOptions) : true;
|
|
81
76
|
if (typeof validationResult === 'string') {
|
|
82
77
|
action.errorMessage = validationResult;
|
|
83
78
|
action.valid = false;
|
|
@@ -92,7 +87,7 @@ const useField = (options) => {
|
|
|
92
87
|
};
|
|
93
88
|
validateField();
|
|
94
89
|
}, 150, [
|
|
95
|
-
|
|
90
|
+
value,
|
|
96
91
|
condition,
|
|
97
92
|
disableFormData,
|
|
98
93
|
dispatchField,
|
|
@@ -104,12 +99,6 @@ const useField = (options) => {
|
|
|
104
99
|
user,
|
|
105
100
|
validate,
|
|
106
101
|
]);
|
|
107
|
-
useEffect(() => {
|
|
108
|
-
if (initialValue !== internalInitialValue) {
|
|
109
|
-
setInternalValue(initialValue);
|
|
110
|
-
setInternalInitialValue(initialValue);
|
|
111
|
-
}
|
|
112
|
-
}, [initialValue, internalInitialValue]);
|
|
113
102
|
return result;
|
|
114
103
|
};
|
|
115
104
|
export default useField;
|
|
@@ -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>;
|