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.
Files changed (45) hide show
  1. package/dist/admin/components/Routes.js +4 -3
  2. package/dist/admin/components/elements/Logout/index.d.ts +3 -0
  3. package/dist/admin/components/elements/Logout/index.js +19 -0
  4. package/dist/admin/components/elements/Nav/index.js +3 -4
  5. package/dist/admin/components/elements/UploadGallery/index.js +1 -1
  6. package/dist/admin/components/forms/RenderFields/index.js +6 -5
  7. package/dist/admin/components/forms/RenderFields/types.d.ts +1 -0
  8. package/dist/admin/components/forms/field-types/Array/index.js +2 -2
  9. package/dist/admin/components/forms/field-types/Array/types.d.ts +1 -0
  10. package/dist/admin/components/forms/field-types/Blocks/index.js +2 -2
  11. package/dist/admin/components/forms/field-types/Blocks/types.d.ts +1 -0
  12. package/dist/admin/components/forms/field-types/Collapsible/index.js +2 -2
  13. package/dist/admin/components/forms/field-types/Collapsible/types.d.ts +1 -0
  14. package/dist/admin/components/forms/field-types/Group/index.js +2 -2
  15. package/dist/admin/components/forms/field-types/Group/types.d.ts +1 -0
  16. package/dist/admin/components/forms/field-types/Row/index.js +2 -2
  17. package/dist/admin/components/forms/field-types/Row/types.d.ts +1 -0
  18. package/dist/admin/components/forms/field-types/Tabs/index.js +55 -14
  19. package/dist/admin/components/forms/field-types/Tabs/types.d.ts +1 -0
  20. package/dist/admin/components/forms/field-types/Textarea/Input.js +4 -1
  21. package/dist/admin/components/forms/field-types/Textarea/index.scss +72 -3
  22. package/dist/admin/components/forms/useField/index.js +8 -19
  23. package/dist/admin/components/modals/StayLoggedIn/index.js +3 -2
  24. package/dist/admin/components/utilities/Auth/index.js +3 -3
  25. package/dist/admin/components/utilities/DocumentInfo/types.d.ts +2 -0
  26. package/dist/admin/components/views/Login/index.js +3 -2
  27. package/dist/admin/components/views/ResetPassword/index.js +3 -2
  28. package/dist/admin/components/views/Unauthorized/index.js +3 -2
  29. package/dist/config/defaults.js +3 -1
  30. package/dist/config/schema.js +6 -1
  31. package/dist/config/types.d.ts +5 -0
  32. package/dist/globals/operations/update.js +2 -3
  33. package/dist/preferences/types.d.ts +4 -0
  34. package/docs/admin/components.mdx +90 -81
  35. package/docs/admin/overview.mdx +15 -13
  36. package/docs/authentication/overview.mdx +1 -1
  37. package/docs/fields/array.mdx +1 -1
  38. package/docs/fields/blocks.mdx +1 -1
  39. package/docs/fields/rich-text.mdx +3 -3
  40. package/docs/fields/tabs.mdx +1 -1
  41. package/docs/fields/ui.mdx +2 -2
  42. package/docs/getting-started/what-is-payload.mdx +1 -1
  43. package/docs/graphql/overview.mdx +1 -1
  44. package/docs/upload/overview.mdx +1 -1
  45. 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 { admin: { user: userSlug, components: { routes: customRoutes, } = {}, }, routes, collections, globals, } = useConfig();
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}/logout` },
63
+ React.createElement(Route, { path: `${match.url}${logoutRoute}` },
63
64
  React.createElement(Logout, null)),
64
- React.createElement(Route, { path: `${match.url}/logout-inactivity` },
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,3 @@
1
+ import React from 'react';
2
+ declare const Logout: React.FC;
3
+ export default Logout;
@@ -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, }, }, } = useConfig();
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(Link, { to: `${admin}/logout`, className: `${baseClass}__log-out` },
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, i) => (React.createElement("li", { key: i },
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, i) => {
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: i }));
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: i, 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: {
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: i },
63
+ return (React.createElement("div", { className: "missing-field", key: fieldIndex },
63
64
  "No matched field found for",
64
65
  ' ',
65
66
  "\"",
@@ -11,4 +11,5 @@ export declare type Props = {
11
11
  filter?: (field: Field) => boolean;
12
12
  fieldSchema: FieldWithPath[];
13
13
  fieldTypes: FieldTypes;
14
+ indexPath?: string;
14
15
  };
@@ -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
  })) }))))));
@@ -6,4 +6,5 @@ export declare type Props = Omit<ArrayField, 'type'> & {
6
6
  fieldTypes: FieldTypes;
7
7
  permissions: FieldPermissions;
8
8
  label: string | false;
9
+ indexPath: string;
9
10
  };
@@ -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
  }),
@@ -5,4 +5,5 @@ export declare type Props = Omit<BlockField, 'type'> & {
5
5
  path?: string;
6
6
  fieldTypes: FieldTypes;
7
7
  permissions: FieldPermissions;
8
+ indexPath: string;
8
9
  };
@@ -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
  })) })),
@@ -5,4 +5,5 @@ export declare type Props = Omit<CollapsibleField, 'type'> & {
5
5
  path?: string;
6
6
  fieldTypes: FieldTypes;
7
7
  permissions: FieldPermissions;
8
+ indexPath: string;
8
9
  };
@@ -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
  })) })))));
@@ -5,4 +5,5 @@ export declare type Props = Omit<GroupField, 'type'> & {
5
5
  path?: string;
6
6
  fieldTypes: FieldTypes;
7
7
  permissions: FieldPermissions;
8
+ indexPath: string;
8
9
  };
@@ -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
  })) }));
@@ -5,4 +5,5 @@ export declare type Props = Omit<RowField, 'type'> & {
5
5
  path?: string;
6
6
  fieldTypes: FieldTypes;
7
7
  permissions: FieldPermissions;
8
+ indexPath: string;
8
9
  };
@@ -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 [active, setActive] = useState(0);
15
- const activeTab = tabs[active];
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, i) => {
24
- return (React.createElement("button", { key: i, type: "button", className: [
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
- active === i && `${baseClass}__tab-button--active`,
27
- ].filter(Boolean).join(' '), onClick: () => setActive(i) }, tab.label ? tab.label : (tabHasName(tab) && tab.name)));
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` }, activeTab && (React.createElement("div", { className: [
70
+ React.createElement("div", { className: `${baseClass}__content-wrap` }, activeTabConfig && (React.createElement("div", { className: [
30
71
  `${baseClass}__tab`,
31
- `${baseClass}__tab-${toKebabCase(activeTab.label)}`,
72
+ `${baseClass}__tab-${toKebabCase(activeTabConfig.label)}`,
32
73
  ].join(' ') },
33
- React.createElement(FieldDescription, { className: `${baseClass}__description`, description: activeTab.description }),
34
- React.createElement(RenderFields, { key: String(activeTab.label), forceRender: true, readOnly: readOnly, permissions: tabHasName(activeTab) ? permissions[activeTab.name].fields : permissions, fieldTypes: fieldTypes, fieldSchema: activeTab.fields.map((field) => ({
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(activeTab) ? `${activeTab.name}.` : ''}${fieldAffectsData(field) ? field.name : ''}`,
37
- })) })))))));
77
+ path: `${path ? `${path}.` : ''}${tabHasName(activeTabConfig) ? `${activeTabConfig.name}.` : ''}${fieldAffectsData(field) ? field.name : ''}`,
78
+ })), indexPath: indexPath })))))));
38
79
  };
39
80
  export default withCondition(TabsField);
@@ -5,4 +5,5 @@ export declare type Props = Omit<TabsField, 'type'> & {
5
5
  path?: string;
6
6
  fieldTypes: FieldTypes;
7
7
  permissions: FieldPermissions;
8
+ indexPath: string;
8
9
  };
@@ -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("textarea", { id: `field-${path.replace(/\./gi, '__')}`, value: value || '', onChange: onChange, disabled: readOnly, placeholder: placeholder, name: path, rows: rows }),
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 '../../../../scss/styles.scss';
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
- resize: vertical;
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, useEffect, useMemo, useState } from 'react';
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: internalValue,
49
+ value,
55
50
  formSubmitted: submitted,
56
51
  formProcessing: processing,
57
52
  setValue,
58
53
  initialValue,
59
- }), [field, processing, setValue, showError, submitted, internalValue, initialValue]);
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: internalValue,
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(internalValue, validateOptions) : true;
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
- internalValue,
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 { routes: { admin } } = useConfig();
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}/logout`);
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}/logout-inactivity`);
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}/logout-inactivity`);
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>;