payload 0.15.3 → 0.15.4

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 (89) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/admin/components/elements/WhereBuilder/field-types.d.ts +2 -2
  3. package/dist/admin/components/elements/WhereBuilder/field-types.js +11 -7
  4. package/dist/admin/components/forms/DraggableSection/index.js +2 -3
  5. package/dist/admin/components/forms/Form/buildStateFromSchema.d.ts +10 -1
  6. package/dist/admin/components/forms/Form/buildStateFromSchema.js +14 -6
  7. package/dist/admin/components/forms/Form/getDataByPath.js +6 -11
  8. package/dist/admin/components/forms/Form/getSiblingData.js +12 -14
  9. package/dist/admin/components/forms/Form/index.js +23 -6
  10. package/dist/admin/components/forms/Form/reduceFieldsToValues.d.ts +1 -1
  11. package/dist/admin/components/forms/Form/reduceFieldsToValues.js +1 -2
  12. package/dist/admin/components/forms/Form/types.d.ts +3 -2
  13. package/dist/admin/components/forms/RenderFields/index.d.ts +1 -2
  14. package/dist/admin/components/forms/RenderFields/index.js +6 -16
  15. package/dist/admin/components/forms/RenderFields/types.d.ts +0 -5
  16. package/dist/admin/components/forms/field-types/Array/Array.js +18 -9
  17. package/dist/admin/components/forms/field-types/Blocks/Blocks.js +21 -15
  18. package/dist/admin/components/forms/field-types/Checkbox/index.js +3 -4
  19. package/dist/admin/components/forms/field-types/Code/Code.js +4 -5
  20. package/dist/admin/components/forms/field-types/DateTime/index.js +2 -3
  21. package/dist/admin/components/forms/field-types/Email/index.js +2 -3
  22. package/dist/admin/components/forms/field-types/Number/index.js +3 -4
  23. package/dist/admin/components/forms/field-types/Password/index.js +2 -2
  24. package/dist/admin/components/forms/field-types/Point/index.js +2 -3
  25. package/dist/admin/components/forms/field-types/RadioGroup/index.js +3 -4
  26. package/dist/admin/components/forms/field-types/Relationship/index.js +49 -17
  27. package/dist/admin/components/forms/field-types/RichText/RichText.js +3 -4
  28. package/dist/admin/components/forms/field-types/RichText/elements/upload/Element/EditModal/index.js +4 -2
  29. package/dist/admin/components/forms/field-types/Row/index.js +7 -2
  30. package/dist/admin/components/forms/field-types/Select/index.js +4 -5
  31. package/dist/admin/components/forms/field-types/Text/index.js +6 -3
  32. package/dist/admin/components/forms/field-types/Textarea/index.js +4 -5
  33. package/dist/admin/components/forms/field-types/Upload/Add/index.js +1 -1
  34. package/dist/admin/components/forms/field-types/Upload/Input.d.ts +2 -1
  35. package/dist/admin/components/forms/field-types/Upload/Input.js +3 -1
  36. package/dist/admin/components/forms/field-types/Upload/SelectExisting/index.js +27 -4
  37. package/dist/admin/components/forms/field-types/Upload/SelectExisting/types.d.ts +3 -0
  38. package/dist/admin/components/forms/field-types/Upload/index.js +4 -5
  39. package/dist/admin/components/forms/field-types/getFilterOptionsQuery.d.ts +5 -0
  40. package/dist/admin/components/forms/field-types/getFilterOptionsQuery.js +11 -0
  41. package/dist/admin/components/forms/useField/index.js +39 -8
  42. package/dist/admin/components/utilities/OperationProvider/index.d.ts +4 -0
  43. package/dist/admin/components/utilities/OperationProvider/index.js +3 -0
  44. package/dist/admin/components/views/Account/Default.js +41 -39
  45. package/dist/admin/components/views/Account/index.js +4 -2
  46. package/dist/admin/components/views/CreateFirstUser/index.js +1 -1
  47. package/dist/admin/components/views/Global/Default.js +45 -43
  48. package/dist/admin/components/views/Global/index.js +5 -5
  49. package/dist/admin/components/views/collections/Edit/Auth/APIKey.js +1 -1
  50. package/dist/admin/components/views/collections/Edit/Default.js +55 -53
  51. package/dist/admin/components/views/collections/Edit/index.js +5 -5
  52. package/dist/collections/tests/pointField.spec.js +26 -15
  53. package/dist/collections/tests/relationships.spec.js +21 -1
  54. package/dist/config/load.d.ts +3 -1
  55. package/dist/config/load.js +5 -3
  56. package/dist/config/types.d.ts +3 -0
  57. package/dist/config/types.js +1 -1
  58. package/dist/config/validate.d.ts +2 -1
  59. package/dist/config/validate.js +2 -4
  60. package/dist/email/build.d.ts +2 -1
  61. package/dist/email/build.js +7 -9
  62. package/dist/express/middleware/errorHandler.spec.js +2 -2
  63. package/dist/fields/config/sanitize.js +2 -2
  64. package/dist/fields/config/schema.js +3 -1
  65. package/dist/fields/config/types.d.ts +25 -1
  66. package/dist/fields/config/types.js +1 -1
  67. package/dist/fields/performFieldOperations.js +3 -3
  68. package/dist/fields/traverseFields.js +15 -5
  69. package/dist/fields/validationPromise.d.ts +10 -3
  70. package/dist/fields/validationPromise.js +16 -5
  71. package/dist/fields/validations.d.ts +33 -35
  72. package/dist/fields/validations.js +130 -76
  73. package/dist/fields/validations.spec.js +115 -43
  74. package/dist/graphql/schema/fieldToSchemaMap.js +7 -7
  75. package/dist/index.d.ts +2 -2
  76. package/dist/index.js +5 -5
  77. package/dist/mongoose/buildQuery.js +2 -2
  78. package/dist/mongoose/connect.d.ts +2 -1
  79. package/dist/mongoose/connect.js +2 -4
  80. package/dist/mongoose/sanitizeFormattedValue.js +12 -3
  81. package/dist/utilities/logger.d.ts +1 -1
  82. package/dist/utilities/logger.js +10 -6
  83. package/dist/webpack/getBaseConfig.js +2 -1
  84. package/docs/fields/overview.mdx +39 -3
  85. package/docs/fields/relationship.mdx +45 -0
  86. package/docs/fields/upload.mdx +1 -0
  87. package/docs/getting-started/installation.mdx +14 -10
  88. package/docs/queries/overview.mdx +2 -1
  89. package/package.json +1 -1
@@ -11,9 +11,8 @@ const baseClass = 'date-time-field';
11
11
  const DateTime = (props) => {
12
12
  const { path: pathFromProps, name, required, validate = dateValidation, label, admin: { placeholder, readOnly, style, className, width, date, description, condition, } = {}, } = props;
13
13
  const path = pathFromProps || name;
14
- const memoizedValidate = useCallback((value) => {
15
- const validationResult = validate(value, { required });
16
- return validationResult;
14
+ const memoizedValidate = useCallback((value, options) => {
15
+ return validate(value, { ...options, required });
17
16
  }, [validate, required]);
18
17
  const { value, showError, errorMessage, setValue, } = useField({
19
18
  path,
@@ -9,9 +9,8 @@ import './index.scss';
9
9
  const Email = (props) => {
10
10
  const { name, path: pathFromProps, required, validate = email, admin: { readOnly, style, className, width, placeholder, autoComplete, description, condition, } = {}, label, } = props;
11
11
  const path = pathFromProps || name;
12
- const memoizedValidate = useCallback((value) => {
13
- const validationResult = validate(value, { required });
14
- return validationResult;
12
+ const memoizedValidate = useCallback((value, options) => {
13
+ return validate(value, { ...options, required });
15
14
  }, [validate, required]);
16
15
  const fieldType = useField({
17
16
  path,
@@ -9,10 +9,9 @@ import './index.scss';
9
9
  const NumberField = (props) => {
10
10
  const { name, path: pathFromProps, required, validate = number, label, max, min, admin: { readOnly, style, className, width, step, placeholder, description, condition, } = {}, } = props;
11
11
  const path = pathFromProps || name;
12
- const memoizedValidate = useCallback((value) => {
13
- const validationResult = validate(value, { min, max, required });
14
- return validationResult;
15
- }, [validate, max, min, required]);
12
+ const memoizedValidate = useCallback((value, options) => {
13
+ return validate(value, { ...options, min, max, required });
14
+ }, [validate, min, max, required]);
16
15
  const { value, showError, setValue, errorMessage, } = useField({
17
16
  path,
18
17
  validate: memoizedValidate,
@@ -8,8 +8,8 @@ import './index.scss';
8
8
  const Password = (props) => {
9
9
  const { path: pathFromProps, name, required, validate = password, style, className, width, autoComplete, label, } = props;
10
10
  const path = pathFromProps || name;
11
- const memoizedValidate = useCallback((value) => {
12
- const validationResult = validate(value, { required });
11
+ const memoizedValidate = useCallback((value, options) => {
12
+ const validationResult = validate(value, { ...options, required });
13
13
  return validationResult;
14
14
  }, [validate, required]);
15
15
  const { value, showError, formProcessing, setValue, errorMessage, } = useField({
@@ -10,9 +10,8 @@ const baseClass = 'point';
10
10
  const PointField = (props) => {
11
11
  const { name, path: pathFromProps, required, validate = point, label, admin: { readOnly, style, className, width, step, placeholder, description, condition, } = {}, } = props;
12
12
  const path = pathFromProps || name;
13
- const memoizedValidate = useCallback((value) => {
14
- const validationResult = validate(value, { required });
15
- return validationResult;
13
+ const memoizedValidate = useCallback((value, options) => {
14
+ return validate(value, { ...options, required });
16
15
  }, [validate, required]);
17
16
  const { value = [null, null], showError, setValue, errorMessage, } = useField({
18
17
  path,
@@ -12,10 +12,9 @@ const baseClass = 'radio-group';
12
12
  const RadioGroup = (props) => {
13
13
  const { name, path: pathFromProps, required, validate = radio, label, admin: { readOnly, layout = 'horizontal', style, className, width, description, condition, } = {}, options, } = props;
14
14
  const path = pathFromProps || name;
15
- const memoizedValidate = useCallback((value) => {
16
- const validationResult = validate(value, { required, options });
17
- return validationResult;
18
- }, [validate, required, options]);
15
+ const memoizedValidate = useCallback((value, validationOptions) => {
16
+ return validate(value, { ...validationOptions, options, required });
17
+ }, [validate, options, required]);
19
18
  const { value, showError, errorMessage, setValue, } = useField({
20
19
  path,
21
20
  validate: memoizedValidate,
@@ -1,5 +1,6 @@
1
1
  import React, { useCallback, useEffect, useState, useReducer, } from 'react';
2
- import { useConfig } from '@payloadcms/config-provider';
2
+ import equal from 'deep-equal';
3
+ import { useAuth, useConfig } from '@payloadcms/config-provider';
3
4
  import qs from 'qs';
4
5
  import withCondition from '../../withCondition';
5
6
  import ReactSelect from '../../../elements/ReactSelect';
@@ -8,27 +9,32 @@ import Label from '../../Label';
8
9
  import Error from '../../Error';
9
10
  import FieldDescription from '../../FieldDescription';
10
11
  import { relationship } from '../../../../../fields/validations';
11
- import { useFormProcessing } from '../../Form/context';
12
+ import { useFormProcessing, useWatchForm } from '../../Form/context';
12
13
  import optionsReducer from './optionsReducer';
13
14
  import { createRelationMap } from './createRelationMap';
14
15
  import { useDebouncedCallback } from '../../../../hooks/useDebouncedCallback';
16
+ import { useDocumentInfo } from '../../../utilities/DocumentInfo';
17
+ import { getFilterOptionsQuery } from '../getFilterOptionsQuery';
15
18
  import './index.scss';
16
19
  const maxResultsPerRequest = 10;
17
20
  const baseClass = 'relationship';
18
21
  const Relationship = (props) => {
19
- const { relationTo, validate = relationship, path, name, required, label, hasMany, admin: { readOnly, style, className, width, description, condition, } = {}, } = props;
22
+ const { relationTo, validate = relationship, path, name, required, label, hasMany, filterOptions, admin: { readOnly, style, className, width, description, condition, } = {}, } = props;
20
23
  const { serverURL, routes: { api, }, collections, } = useConfig();
24
+ const { id } = useDocumentInfo();
25
+ const { user } = useAuth();
26
+ const { getData, getSiblingData } = useWatchForm();
21
27
  const formProcessing = useFormProcessing();
22
28
  const hasMultipleRelations = Array.isArray(relationTo);
23
29
  const [options, dispatchOptions] = useReducer(optionsReducer, required || hasMany ? [] : [{ value: 'null', label: 'None' }]);
24
30
  const [lastFullyLoadedRelation, setLastFullyLoadedRelation] = useState(-1);
25
31
  const [lastLoadedPage, setLastLoadedPage] = useState(1);
26
32
  const [errorLoading, setErrorLoading] = useState('');
33
+ const [optionFilters, setOptionFilters] = useState();
27
34
  const [hasLoadedValueOptions, setHasLoadedValueOptions] = useState(false);
28
35
  const [search, setSearch] = useState('');
29
- const memoizedValidate = useCallback((value) => {
30
- const validationResult = validate(value, { required });
31
- return validationResult;
36
+ const memoizedValidate = useCallback((value, validationOptions) => {
37
+ return validate(value, { ...validationOptions, required });
32
38
  }, [validate, required]);
33
39
  const { value, showError, errorMessage, setValue, initialValue, } = useField({
34
40
  path: path || name,
@@ -51,9 +57,13 @@ const Relationship = (props) => {
51
57
  const fieldToSearch = ((_a = collection === null || collection === void 0 ? void 0 : collection.admin) === null || _a === void 0 ? void 0 : _a.useAsTitle) || 'id';
52
58
  const query = {
53
59
  where: {
54
- id: {
55
- not_in: relationMap[relation],
56
- },
60
+ and: [
61
+ {
62
+ id: {
63
+ not_in: relationMap[relation],
64
+ },
65
+ },
66
+ ],
57
67
  },
58
68
  limit: maxResultsPerRequest,
59
69
  page: lastLoadedPageToUse,
@@ -61,9 +71,14 @@ const Relationship = (props) => {
61
71
  depth: 0,
62
72
  };
63
73
  if (searchArg) {
64
- query.where[fieldToSearch] = {
65
- like: searchArg,
66
- };
74
+ query.where.and.push({
75
+ [fieldToSearch]: {
76
+ like: searchArg,
77
+ },
78
+ });
79
+ }
80
+ if (optionFilters[relation]) {
81
+ query.where.and.push(optionFilters[relation]);
67
82
  }
68
83
  const response = await fetch(`${serverURL}${api}/${relation}?${qs.stringify(query)}`);
69
84
  if (response.ok) {
@@ -88,7 +103,7 @@ const Relationship = (props) => {
88
103
  }
89
104
  }, Promise.resolve());
90
105
  }
91
- }, [api, collections, serverURL, errorLoading, relationTo, hasMany, hasMultipleRelations]);
106
+ }, [relationTo, hasMany, errorLoading, collections, optionFilters, serverURL, api, hasMultipleRelations]);
92
107
  const findOptionsByValue = useCallback(() => {
93
108
  if (value) {
94
109
  if (hasMany) {
@@ -182,11 +197,28 @@ const Relationship = (props) => {
182
197
  }
183
198
  }, [hasMany, hasMultipleRelations, relationTo, initialValue, hasLoadedValueOptions, errorLoading, collections, api, serverURL]);
184
199
  useEffect(() => {
185
- setHasLoadedValueOptions(false);
186
- getResults({
187
- value: initialValue,
200
+ if (!filterOptions) {
201
+ return;
202
+ }
203
+ const newOptionFilters = getFilterOptionsQuery(filterOptions, {
204
+ id,
205
+ data: getData(),
206
+ relationTo,
207
+ siblingData: getSiblingData(path),
208
+ user,
188
209
  });
189
- }, [initialValue, getResults]);
210
+ if (!equal(newOptionFilters, optionFilters)) {
211
+ setOptionFilters(newOptionFilters);
212
+ }
213
+ }, [relationTo, filterOptions, optionFilters, id, getData, getSiblingData, path, user]);
214
+ useEffect(() => {
215
+ if (optionFilters) {
216
+ setHasLoadedValueOptions(false);
217
+ getResults({
218
+ value: initialValue,
219
+ });
220
+ }
221
+ }, [initialValue, getResults, optionFilters]);
190
222
  const classes = [
191
223
  'field-type',
192
224
  baseClass,
@@ -51,9 +51,8 @@ const RichText = (props) => {
51
51
  }
52
52
  return (React.createElement("span", { ...attributes }, children));
53
53
  }, [enabledLeaves, path, props]);
54
- const memoizedValidate = useCallback((value) => {
55
- const validationResult = validate(value, { required });
56
- return validationResult;
54
+ const memoizedValidate = useCallback((value, validationOptions) => {
55
+ return validate(value, { ...validationOptions, required });
57
56
  }, [validate, required]);
58
57
  const fieldType = useField({
59
58
  path,
@@ -147,7 +146,7 @@ const RichText = (props) => {
147
146
  return null;
148
147
  })),
149
148
  React.createElement("div", { className: `${baseClass}__editor`, ref: editorRef },
150
- React.createElement(Editable, { className: `${baseClass}__input`, renderElement: renderElement, renderLeaf: renderLeaf, placeholder: placeholder, spellCheck: true, readOnly: readOnly, onBlur: onBlur, onKeyDown: (event) => {
149
+ React.createElement(Editable, { renderElement: renderElement, renderLeaf: renderLeaf, placeholder: placeholder, spellCheck: true, readOnly: readOnly, onBlur: onBlur, onKeyDown: (event) => {
151
150
  if (event.key === 'Enter') {
152
151
  if (event.shiftKey) {
153
152
  event.preventDefault();
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react';
2
2
  import { Transforms } from 'slate';
3
3
  import { ReactEditor, useSlateStatic } from 'slate-react';
4
4
  import { Modal } from '@faceless-ui/modal';
5
+ import { useAuth } from '@payloadcms/config-provider';
5
6
  import buildStateFromSchema from '../../../../../../Form/buildStateFromSchema';
6
7
  import MinimalTemplate from '../../../../../../../templates/Minimal';
7
8
  import Button from '../../../../../../../elements/Button';
@@ -15,6 +16,7 @@ const baseClass = 'edit-upload-modal';
15
16
  export const EditModal = ({ slug, closeModal, relatedCollectionConfig, fieldSchema, element }) => {
16
17
  const editor = useSlateStatic();
17
18
  const [initialState, setInitialState] = useState({});
19
+ const { user } = useAuth();
18
20
  const handleUpdateEditData = useCallback((fields) => {
19
21
  const newNode = {
20
22
  fields: reduceFieldsToValues(fields, true),
@@ -25,11 +27,11 @@ export const EditModal = ({ slug, closeModal, relatedCollectionConfig, fieldSche
25
27
  }, [closeModal, editor, element]);
26
28
  useEffect(() => {
27
29
  const awaitInitialState = async () => {
28
- const state = await buildStateFromSchema(fieldSchema, element === null || element === void 0 ? void 0 : element.fields);
30
+ const state = await buildStateFromSchema({ fieldSchema, data: element === null || element === void 0 ? void 0 : element.fields, user, operation: 'update' });
29
31
  setInitialState(state);
30
32
  };
31
33
  awaitInitialState();
32
- }, [fieldSchema, element.fields]);
34
+ }, [fieldSchema, element.fields, user]);
33
35
  return (React.createElement(Modal, { slug: slug, className: baseClass },
34
36
  React.createElement(MinimalTemplate, { width: "normal" },
35
37
  React.createElement("header", { className: `${baseClass}__header` },
@@ -4,8 +4,13 @@ import withCondition from '../../withCondition';
4
4
  import { fieldAffectsData } from '../../../../../fields/config/types';
5
5
  import './index.scss';
6
6
  const Row = (props) => {
7
- const { fields, fieldTypes, path, permissions, admin: { readOnly, }, } = props;
8
- return (React.createElement(RenderFields, { readOnly: readOnly, className: "field-type row", permissions: permissions === null || permissions === void 0 ? void 0 : permissions.fields, fieldTypes: fieldTypes, fieldSchema: fields.map((field) => ({
7
+ const { fields, fieldTypes, path, permissions, admin: { readOnly, className, }, } = props;
8
+ const classes = [
9
+ 'field-type',
10
+ 'row',
11
+ className,
12
+ ].filter(Boolean).join(' ');
13
+ return (React.createElement(RenderFields, { readOnly: readOnly, className: classes, permissions: permissions === null || permissions === void 0 ? void 0 : permissions.fields, fieldTypes: fieldTypes, fieldSchema: fields.map((field) => ({
9
14
  ...field,
10
15
  path: `${path ? `${path}.` : ''}${fieldAffectsData(field) ? field.name : ''}`,
11
16
  })) }));
@@ -13,16 +13,15 @@ const formatOptions = (options) => options.map((option) => {
13
13
  };
14
14
  });
15
15
  const Select = (props) => {
16
- const { path: pathFromProps, name, required, validate = select, label, options: optionsFromProps, hasMany, admin: { readOnly, style, className, width, description, condition, } = {}, } = props;
16
+ const { path: pathFromProps, name, validate = select, label, options: optionsFromProps, hasMany, required, admin: { readOnly, style, className, width, description, condition, } = {}, } = props;
17
17
  const path = pathFromProps || name;
18
18
  const [options, setOptions] = useState(formatOptions(optionsFromProps));
19
19
  useEffect(() => {
20
20
  setOptions(formatOptions(optionsFromProps));
21
21
  }, [optionsFromProps]);
22
- const memoizedValidate = useCallback((value) => {
23
- const validationResult = validate(value, { required, options });
24
- return validationResult;
25
- }, [validate, required, options]);
22
+ const memoizedValidate = useCallback((value, validationOptions) => {
23
+ return validate(value, { ...validationOptions, options, hasMany, required });
24
+ }, [validate, required, hasMany, options]);
26
25
  const { value, showError, setValue, errorMessage, } = useField({
27
26
  path,
28
27
  validate: memoizedValidate,
@@ -1,14 +1,17 @@
1
- import React from 'react';
1
+ import React, { useCallback } from 'react';
2
2
  import useField from '../../useField';
3
3
  import withCondition from '../../withCondition';
4
4
  import { text } from '../../../../../fields/validations';
5
5
  import TextInput from './Input';
6
6
  const Text = (props) => {
7
- const { path: pathFromProps, name, required, validate = text, label, admin: { placeholder, readOnly, style, className, width, description, condition, } = {}, } = props;
7
+ const { path: pathFromProps, name, required, validate = text, label, minLength, maxLength, admin: { placeholder, readOnly, style, className, width, description, condition, } = {}, } = props;
8
8
  const path = pathFromProps || name;
9
+ const memoizedValidate = useCallback((value, options) => {
10
+ return validate(value, { ...options, minLength, maxLength, required });
11
+ }, [validate, minLength, maxLength, required]);
9
12
  const field = useField({
10
13
  path,
11
- validate,
14
+ validate: memoizedValidate,
12
15
  enableDebouncedValue: true,
13
16
  condition,
14
17
  });
@@ -5,12 +5,11 @@ import { textarea } from '../../../../../fields/validations';
5
5
  import TextareaInput from './Input';
6
6
  import './index.scss';
7
7
  const Textarea = (props) => {
8
- const { path: pathFromProps, name, required, validate = textarea, admin: { readOnly, style, className, width, placeholder, rows, description, condition, } = {}, label, minLength, maxLength, } = props;
8
+ const { path: pathFromProps, name, required, validate = textarea, maxLength, minLength, admin: { readOnly, style, className, width, placeholder, rows, description, condition, } = {}, label, } = props;
9
9
  const path = pathFromProps || name;
10
- const memoizedValidate = useCallback((value) => {
11
- const validationResult = validate(value, { minLength, maxLength, required });
12
- return validationResult;
13
- }, [validate, maxLength, minLength, required]);
10
+ const memoizedValidate = useCallback((value, options) => {
11
+ return validate(value, { ...options, required, maxLength, minLength });
12
+ }, [validate, required, maxLength, minLength]);
14
13
  const { value, showError, setValue, errorMessage, } = useField({
15
14
  path,
16
15
  validate: memoizedValidate,
@@ -26,7 +26,7 @@ const AddUploadModal = (props) => {
26
26
  const collectionPermissions = (_b = (_a = permissions === null || permissions === void 0 ? void 0 : permissions.collections) === null || _a === void 0 ? void 0 : _a[collection.slug]) === null || _b === void 0 ? void 0 : _b.fields;
27
27
  return (React.createElement(Modal, { className: classes, slug: slug },
28
28
  React.createElement(MinimalTemplate, { width: "wide" },
29
- React.createElement(Form, { method: "post", action: `${serverURL}${api}/${collection.slug}`, onSuccess: onSuccess, disableSuccessStatus: true },
29
+ React.createElement(Form, { method: "post", action: `${serverURL}${api}/${collection.slug}`, onSuccess: onSuccess, disableSuccessStatus: true, validationOperation: "create" },
30
30
  React.createElement("header", { className: `${baseClass}__header` },
31
31
  React.createElement("div", null,
32
32
  React.createElement("h1", null,
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { UploadField } from '../../../../../fields/config/types';
2
+ import { FilterOptions, UploadField } from '../../../../../fields/config/types';
3
3
  import { Description } from '../../FieldDescription/types';
4
4
  import { FieldTypes } from '..';
5
5
  import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
@@ -21,6 +21,7 @@ export declare type UploadInputProps = Omit<UploadField, 'type'> & {
21
21
  collection?: SanitizedCollectionConfig;
22
22
  serverURL?: string;
23
23
  api?: string;
24
+ filterOptions: FilterOptions;
24
25
  };
25
26
  declare const UploadInput: React.FC<UploadInputProps>;
26
27
  export default UploadInput;
@@ -10,7 +10,7 @@ import SelectExistingModal from './SelectExisting';
10
10
  import './index.scss';
11
11
  const baseClass = 'upload';
12
12
  const UploadInput = (props) => {
13
- const { path, required, readOnly, style, className, width, description, label, relationTo, fieldTypes, value, onChange, showError, serverURL = 'http://localhost:3000', api = '/api', collection, errorMessage, } = props;
13
+ const { path, required, readOnly, style, className, width, description, label, relationTo, fieldTypes, value, onChange, showError, serverURL = 'http://localhost:3000', api = '/api', collection, errorMessage, filterOptions, } = props;
14
14
  const { toggle } = useModal();
15
15
  const addModalSlug = `${path}-add`;
16
16
  const selectExistingModalSlug = `${path}-select-existing`;
@@ -78,6 +78,8 @@ const UploadInput = (props) => {
78
78
  slug: selectExistingModalSlug,
79
79
  setValue: onChange,
80
80
  addModalSlug,
81
+ filterOptions,
82
+ path,
81
83
  } }),
82
84
  React.createElement(FieldDescription, { value: file, description: description })))));
83
85
  };
@@ -1,6 +1,7 @@
1
1
  import React, { Fragment, useState, useEffect } from 'react';
2
+ import equal from 'deep-equal';
2
3
  import { Modal, useModal } from '@faceless-ui/modal';
3
- import { useConfig } from '@payloadcms/config-provider';
4
+ import { useAuth, useConfig } from '@payloadcms/config-provider';
4
5
  import MinimalTemplate from '../../../../templates/Minimal';
5
6
  import Button from '../../../../elements/Button';
6
7
  import usePayloadAPI from '../../../../../hooks/usePayloadAPI';
@@ -10,17 +11,24 @@ import UploadGallery from '../../../../elements/UploadGallery';
10
11
  import PerPage from '../../../../elements/PerPage';
11
12
  import formatFields from '../../../../views/collections/List/formatFields';
12
13
  import './index.scss';
14
+ import { getFilterOptionsQuery } from '../../getFilterOptionsQuery';
15
+ import { useDocumentInfo } from '../../../../utilities/DocumentInfo';
16
+ import { useWatchForm } from '../../../Form/context';
13
17
  const baseClass = 'select-existing-upload-modal';
14
18
  const SelectExistingUploadModal = (props) => {
15
19
  var _a, _b;
16
- const { setValue, collection, collection: { slug: collectionSlug, admin: { description, pagination: { defaultLimit, }, } = {}, } = {}, slug: modalSlug, } = props;
20
+ const { setValue, collection, collection: { slug: collectionSlug, admin: { description, pagination: { defaultLimit, }, } = {}, } = {}, slug: modalSlug, path, filterOptions, } = props;
17
21
  const { serverURL, routes: { api } } = useConfig();
22
+ const { id } = useDocumentInfo();
23
+ const { user } = useAuth();
24
+ const { getData, getSiblingData } = useWatchForm();
18
25
  const { closeAll, currentModal } = useModal();
19
26
  const [fields] = useState(() => formatFields(collection));
20
27
  const [limit, setLimit] = useState(defaultLimit);
21
28
  const [sort, setSort] = useState(null);
22
29
  const [where, setWhere] = useState(null);
23
30
  const [page, setPage] = useState(null);
31
+ const [optionFilters, setOptionFilters] = useState();
24
32
  const classes = [
25
33
  baseClass,
26
34
  ].filter(Boolean).join(' ');
@@ -32,13 +40,28 @@ const SelectExistingUploadModal = (props) => {
32
40
  if (page)
33
41
  params.page = page;
34
42
  if (where)
35
- params.where = where;
43
+ params.where = { and: [where, optionFilters] };
36
44
  if (sort)
37
45
  params.sort = sort;
38
46
  if (limit)
39
47
  params.limit = limit;
40
48
  setParams(params);
41
- }, [setParams, page, sort, where, limit]);
49
+ }, [setParams, page, sort, where, limit, optionFilters]);
50
+ useEffect(() => {
51
+ if (!filterOptions || !isOpen) {
52
+ return;
53
+ }
54
+ const newOptionFilters = getFilterOptionsQuery(filterOptions, {
55
+ id,
56
+ relationTo: collectionSlug,
57
+ data: getData(),
58
+ siblingData: getSiblingData(path),
59
+ user,
60
+ })[collectionSlug];
61
+ if (!equal(newOptionFilters, optionFilters)) {
62
+ setOptionFilters(newOptionFilters);
63
+ }
64
+ }, [collectionSlug, filterOptions, optionFilters, id, getData, getSiblingData, path, user, isOpen]);
42
65
  return (React.createElement(Modal, { className: classes, slug: modalSlug }, isOpen && (React.createElement(MinimalTemplate, { width: "wide" },
43
66
  React.createElement("header", { className: `${baseClass}__header` },
44
67
  React.createElement("div", null,
@@ -1,8 +1,11 @@
1
1
  import { SanitizedCollectionConfig } from '../../../../../../collections/config/types';
2
+ import { FilterOptions } from '../../../../../../fields/config/types';
2
3
  export declare type Props = {
3
4
  setValue: (val: {
4
5
  id: string;
5
6
  } | null) => void;
6
7
  collection: SanitizedCollectionConfig;
7
8
  slug: string;
9
+ path: any;
10
+ filterOptions: FilterOptions;
8
11
  };
@@ -7,11 +7,10 @@ import UploadInput from './Input';
7
7
  import './index.scss';
8
8
  const Upload = (props) => {
9
9
  const { collections, serverURL, routes: { api, }, } = useConfig();
10
- const { path, name, required, admin: { readOnly, style, className, width, description, condition, } = {}, label, validate = upload, relationTo, fieldTypes, } = props;
10
+ const { path, name, required, admin: { readOnly, style, className, width, description, condition, } = {}, label, validate = upload, relationTo, fieldTypes, filterOptions, } = props;
11
11
  const collection = collections.find((coll) => coll.slug === relationTo);
12
- const memoizedValidate = useCallback((value) => {
13
- const validationResult = validate(value, { required });
14
- return validationResult;
12
+ const memoizedValidate = useCallback((value, options) => {
13
+ return validate(value, { ...options, required });
15
14
  }, [validate, required]);
16
15
  const field = useField({
17
16
  path,
@@ -26,7 +25,7 @@ const Upload = (props) => {
26
25
  setValue,
27
26
  ]);
28
27
  if (collection.upload) {
29
- return (React.createElement(UploadInput, { path: path, value: value, onChange: onChange, description: description, label: label, required: required, showError: showError, serverURL: serverURL, api: api, errorMessage: errorMessage, readOnly: readOnly, style: style, className: className, width: width, collection: collection, fieldTypes: fieldTypes, name: name, relationTo: relationTo }));
28
+ return (React.createElement(UploadInput, { path: path, value: value, onChange: onChange, description: description, label: label, required: required, showError: showError, serverURL: serverURL, api: api, errorMessage: errorMessage, readOnly: readOnly, style: style, className: className, width: width, collection: collection, fieldTypes: fieldTypes, name: name, relationTo: relationTo, filterOptions: filterOptions }));
30
29
  }
31
30
  return null;
32
31
  };
@@ -0,0 +1,5 @@
1
+ import { Where } from '../../../../types';
2
+ import { FilterOptions, FilterOptionsProps } from '../../../../fields/config/types';
3
+ export declare const getFilterOptionsQuery: (filterOptions: FilterOptions, options: FilterOptionsProps) => {
4
+ [collection: string]: Where;
5
+ };
@@ -0,0 +1,11 @@
1
+ export const getFilterOptionsQuery = (filterOptions, options) => {
2
+ const { relationTo } = options;
3
+ const relations = Array.isArray(relationTo) ? relationTo : [relationTo];
4
+ const query = {};
5
+ if (typeof filterOptions !== 'undefined') {
6
+ relations.forEach((relation) => {
7
+ query[relation] = typeof filterOptions === 'function' ? filterOptions(options) : filterOptions;
8
+ });
9
+ }
10
+ return query;
11
+ };
@@ -1,21 +1,36 @@
1
1
  import { useCallback, useEffect, useState, } from 'react';
2
+ import { useAuth } from '@payloadcms/config-provider';
2
3
  import { useFormProcessing, useFormSubmitted, useFormModified, useForm } from '../Form/context';
3
4
  import useDebounce from '../../../hooks/useDebounce';
5
+ import { useDocumentInfo } from '../../utilities/DocumentInfo';
6
+ import { useOperation } from '../../utilities/OperationProvider';
4
7
  const useField = (options) => {
5
8
  const { path, validate, enableDebouncedValue, disableFormData, ignoreWhileFlattening, condition, } = options;
6
9
  const formContext = useForm();
7
10
  const submitted = useFormSubmitted();
8
11
  const processing = useFormProcessing();
9
12
  const modified = useFormModified();
10
- const { dispatchFields, getField, setModified, } = formContext || {};
13
+ const { user } = useAuth();
14
+ const { id } = useDocumentInfo();
15
+ const operation = useOperation();
16
+ const { dispatchFields, getField, getData, getSiblingData, setModified, } = formContext || {};
11
17
  const [internalValue, setInternalValue] = useState(undefined);
18
+ const [internallyValid, setInternallyValid] = useState(undefined);
12
19
  // Debounce internal values to update form state only every 60ms
13
20
  const debouncedValue = useDebounce(internalValue, 120);
14
21
  // Get field by path
15
22
  const field = getField(path);
16
23
  const initialValue = field === null || field === void 0 ? void 0 : field.initialValue;
17
- // Valid could be a string equal to an error message
18
- const valid = (field && typeof field.valid === 'boolean') ? field.valid : true;
24
+ // Validation is defined by two ways -
25
+ // 1. by field state
26
+ // 2. maintained locally to reflect instant validation state changes
27
+ let valid = true;
28
+ if (field && typeof field.valid === 'boolean') {
29
+ valid = field.valid;
30
+ }
31
+ if (typeof internallyValid === 'boolean') {
32
+ valid = internallyValid;
33
+ }
19
34
  const showError = valid === false && submitted;
20
35
  // Method to send update field values from field component(s)
21
36
  // Should only be used internally
@@ -31,25 +46,40 @@ const useField = (options) => {
31
46
  valid: false,
32
47
  errorMessage: undefined,
33
48
  };
34
- const validationResult = typeof validate === 'function' ? await validate(valueToSend) : true;
49
+ const validateOptions = {
50
+ id,
51
+ user,
52
+ data: getData(),
53
+ siblingData: getSiblingData(path),
54
+ operation,
55
+ };
56
+ const validationResult = typeof validate === 'function' ? await validate(valueToSend, validateOptions) : true;
35
57
  if (typeof validationResult === 'string') {
36
58
  fieldToDispatch.errorMessage = validationResult;
37
59
  fieldToDispatch.valid = false;
60
+ setInternallyValid(false);
38
61
  }
39
62
  else {
40
63
  fieldToDispatch.valid = validationResult;
64
+ fieldToDispatch.errorMessage = undefined;
65
+ setInternallyValid(true);
41
66
  }
42
67
  if (typeof dispatchFields === 'function') {
43
68
  dispatchFields(fieldToDispatch);
44
69
  }
45
70
  }, [
46
- path,
47
- dispatchFields,
48
- validate,
71
+ condition,
49
72
  disableFormData,
73
+ dispatchFields,
74
+ getData,
75
+ getSiblingData,
76
+ id,
50
77
  ignoreWhileFlattening,
51
78
  initialValue,
52
- condition,
79
+ operation,
80
+ path,
81
+ user,
82
+ validate,
53
83
  ]);
54
84
  // Method to return from `useField`, used to
55
85
  // update internal field values from field component(s)
@@ -69,6 +99,7 @@ const useField = (options) => {
69
99
  ]);
70
100
  useEffect(() => {
71
101
  setInternalValue(initialValue);
102
+ setInternallyValid(undefined);
72
103
  }, [initialValue]);
73
104
  // The only time that the FORM value should be updated
74
105
  // is when the debounced value updates. So, when the debounced value updates,
@@ -0,0 +1,4 @@
1
+ /// <reference types="react" />
2
+ export declare const OperationContext: import("react").Context<any>;
3
+ export declare type Operation = 'create' | 'update';
4
+ export declare const useOperation: () => Operation | undefined;
@@ -0,0 +1,3 @@
1
+ import { useContext, createContext } from 'react';
2
+ export const OperationContext = createContext(undefined);
3
+ export const useOperation = () => useContext(OperationContext);